Backend Spring MVC and Spring-data,spring-security . Front end Angularjs.I'm using spring 3.1 ;Jackson 1.8 ; JPa 2.1 ands mysql.The basic problem is the same as asked multiple time. I have a simple program with two POJOs sites and typeSite- where a typeSite can have multiple Sites. But I'm getting the following error :
org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: TypeSites.sitees, could not initialize proxy - no Session (through reference chain: vo.TypeSitesListVO["typesites"]->java.util.UnmodifiableRandomAccessList[0]-model.TypeSites["sitees"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: model.TypeSites.sitees, could not initialize proxy - no Session (through reference chain:vo.TypeSitesListVO["typesites"]->java.util.UnmodifiableRandomAccessList[0]->model.TypeSites["sitees"])
Caused by: com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: model.TypeSites.sitees, could not initialize proxy - no Session (through reference chain: vo.TypeSitesListVO["typesites"]->java.util.UnmodifiableRandomAccessList[0]->model.TypeSites["sitees"])
And this following error in browser :
Failed to load resource: the server responded with a status of 500 (Internal Server Error)
So to understand better this error let us see how the JPA/Hibernate handles the relationship. Every time we do a query in the database the JPA will bring to all information of that class. The exception to this rule is when we talk about list (collection). Notice in the above code, that the database query will return a Sitesobject. When i access the site collection, the container will notice that the site collection is a lazy attribute and it will “ask” the JPA to load this collection from the database.
In the moment of the query (that will bring the site collection) execution, an exception will happen. When the JPA/Hibernate tries to access the database to get this lazy information, the JPA will notice that there is no opened collection. That is why the exception happens, the lack of an opened database connection.
model class :
@JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="id")
public class Sites implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private int id;
private TypeSites siteesTypeSite;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
//@ManyToOne(fetch = FetchType.LAZY)
//@JoinColumn(name ="idTypeSite")
//@JsonIgnore
@JsonBackReference("site-typeSite")
@ManyToOne
@JoinColumn(name = "idTypeSite", foreignKey = @ForeignKey(name = "fk_site_typeSite"))
public TypeSites getSiteesTypeSite() {
return siteesTypeSite;
}
@JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="idTypeSite")
public class TypeSites implements java.io.Serializable {
private int idTypeSite;
private Set<Sites> sitees= new HashSet<Sites>(0);
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public int getIdTypeSite() {
return idTypeSite;
}
//@JsonSerialize(using = CustomListSerializer.class)
//@JsonView(Views.Internal.class)
//@JsonIgnore
@JsonManagedReference("site-typeSite")
@OneToMany(mappedBy = "siteesTypeSite", cascade = CascadeType.ALL,fetch = FetchType.LAZY)
public Set<Sites> getSitees() {
return sitees;
}
code of value object design pattern
public class TypeSitesListVO {
private int pagesCount;
private long totalTypeSite;
private String actionMessage;
private String searchMessage;
private List<TypeSites> typesites;
//setters/gtters}
public class SitesListVO {
private int pagesCount;
private long totalSites;
private String actionMessage;
private String searchMessage;
private List<Sites> sites = new ArrayList<Sites>();
//setters/gtters}
Repository :
public interface SitesRepository extends PagingAndSortingRepository<Sites, Integer> {
//@Query("SELECT s FROM Sites s TypeSites ts JOIN FETCH s.siteesTypeSite WHERE s.id =ts.idTypeSite ")
//@EntityGraph(value = "sites.type", type = EntityGraphType.LOAD)
Page<Sites> findBycodeGSMLike(Pageable pageable, String codeGSM);
//Page<Sites> findBycodeGSMLike(Pageable pageable, List<String> codeGSM);
}
Services class :
@Service
@Transactional
public class SitesService {
@Autowired
private SitesRepository siteRepository;
@Transactional(readOnly = true)
public SitesListVO findAll(int page, int maxResults) {
Page<Sites> result = executeQueryFindAll(page, maxResults);
if(shouldExecuteSameQueryInLastPage(page, result)){
int lastPage = result.getTotalPages() - 1;
// for (Sites e: result){
// Hibernate.initialize(e.getSiteesTypeSite());
// }
result = executeQueryFindAll(lastPage, maxResults);
}
return buildResult(result);
}
public void save(Sites site) {
siteRepository.save(site);
}
@Transactional(readOnly = true)
public SitesListVO findBycodeGSMLike(int page, int maxResults, String codeGSM) {
Page<Sites> result = executeQueryFindByName(page, maxResults, codeGSM);
if(shouldExecuteSameQueryInLastPage(page, result)){
int lastPage = result.getTotalPages() - 1;
// for (Sites e: result){
// Hibernate.initialize(e.getSiteesTypeSite());
// }
result = executeQueryFindByName(lastPage, maxResults, codeGSM);
}
return buildResult(result);
}
private boolean shouldExecuteSameQueryInLastPage(int page, Page<Sites> result) {
return isUserAfterOrOnLastPage(page, result) && hasDataInDataBase(result);
}
private Page<Sites> executeQueryFindAll(int page, int maxResults) {
final PageRequest pageRequest = new PageRequest(page, maxResults, sortBycodeGSMASC());
// Page<Sites> SitesList = siteRepository.findAll(pageRequest);
// for (Sites e: SitesList){
// Hibernate.initialize(e.getSiteesTypeSite());
// }
// return SitesList;
return siteRepository.findAll(pageRequest);
}
private Sort sortBycodeGSMASC() {
return new Sort(Sort.Direction.ASC, "codeGSM");
}
private SitesListVO buildResult(Page<Sites> result) {
return new SitesListVO(result.getTotalPages(), result.getTotalElements(), result.getContent());
}
private Page<Sites> executeQueryFindByName(int page, int maxResults, String codeGSM) {
final PageRequest pageRequest = new PageRequest(page, maxResults, sortBycodeGSMASC());
// Page<Sites> SitesList = siteRepository.findBycodeGSMLike(pageRequest, codeGSM);
// for (Sites e: SitesList){
// Hibernate.initialize(e.getSiteesTypeSite());
// }
// return SitesList;
return siteRepository.findBycodeGSMLike(pageRequest, codeGSM);
}
private boolean isUserAfterOrOnLastPage(int page, Page<Sites> result) {
return page >= result.getTotalPages() - 1;
}
private boolean hasDataInDataBase(Page<Sites> result) {
return result.getTotalElements() > 0;
}
}
controller class :
@Controller
@RequestMapping(value = "/protected/sites")
public class SitesController {
private static final String DEFAULT_PAGE_DISPLAYED_TO_USER = "0";
@Autowired
private SitesService siteService;
@Autowired
private MessageSource messageSource;
@Value("5")
private int maxResults;
@RequestMapping(method = RequestMethod.GET)
public ModelAndView welcome() {
return new ModelAndView("sitesList");
}
@RequestMapping(method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<?> listAll(@RequestParam int page, Locale locale) {
return createListAllResponse(page, locale);
}
@RequestMapping(method = RequestMethod.POST, produces = "application/json")
public ResponseEntity<?> create(@ModelAttribute("site") Sites site,
@RequestParam(required = false) String searchFor,
@RequestParam(required = false,
defaultValue = DEFAULT_PAGE_DISPLAYED_TO_USER) int page,
Locale locale) {
siteService.save(site);
if (isSearchActivated(searchFor)) {
return search(searchFor, page, locale, "message.create.success");
}
return createListAllResponse(page, locale, "message.create.success");
}
private SitesListVO listAll(int page) {
return siteService.findAll(page, maxResults);
}
private ResponseEntity<SitesListVO> returnListToUser(SitesListVO siteList) {
return new ResponseEntity<SitesListVO>(siteList, HttpStatus.OK);
}
private ResponseEntity<?> createListAllResponse(int page, Locale locale) {
SitesListVO siteListVO = listAll(page);
return createListAllResponse(page, locale, null);
}
private ResponseEntity<?> createListAllResponse(int page, Locale locale, String messageKey) {
SitesListVO siteListVO = listAll(page);
addActionMessageToVO(siteListVO, locale, messageKey, null);
return returnListToUser(siteListVO);
}
private SitesListVO addActionMessageToVO(SitesListVO siteListVO, Locale locale, String actionMessageKey, Object[] args) {
if (StringUtils.isEmpty(actionMessageKey)) {
return siteListVO;
}
siteListVO.setActionMessage(messageSource.getMessage(actionMessageKey, args, null, locale));
return siteListVO;
}
private SitesListVO addSearchMessageToVO(SitesListVO siteListVO, Locale locale, String actionMessageKey, Object[] args) {
if (StringUtils.isEmpty(actionMessageKey)) {
return siteListVO;
}
siteListVO.setSearchMessage(messageSource.getMessage(actionMessageKey, args, null, locale));
return siteListVO;
}
private boolean isSearchActivated(String searchFor) {
//return !CollectionUtils.isEmpty(searchFor);
return !StringUtils.isEmpty(searchFor);
}
}
AngularJs code :
$scope.getContactList = function () {
var url = $scope.url;
$scope.lastAction = 'list';
$scope.startDialogAjaxRequest();
var config = {params: {page: $scope.pageToGet}};
$http.get(url, config)
.success(function (data) {
// console.log(data);
console.debug(data);
$scope.finishAjaxCallOnSuccess(data, null, false);
})
.error(function () {
$scope.state = 'error';
$scope.displayCreateContactButton = false;
});
}
$scope.populateTable = function (data) {
if (data.pagesCount > 0) {
$scope.state = 'list';
$scope.page = {source: data.sites, currentPage: $scope.pageToGet, pagesCount: data.pagesCount, totalContacts : data.totalContacts};
if($scope.page.pagesCount <= $scope.page.currentPage){
$scope.pageToGet = $scope.page.pagesCount - 1;
$scope.page.currentPage = $scope.page.pagesCount - 1;
}
$scope.displayCreateContactButton = true;
$scope.displaySearchButton = true;
} else {
$scope.state = 'noresult';
$scope.displayCreateContactButton = true;
if(!$scope.searchFor){
$scope.displaySearchButton = false;
}
}
if (data.actionMessage || data.searchMessage) {
$scope.displayMessageToUser = $scope.lastAction != 'search';
$scope.page.actionMessage = data.actionMessage;
$scope.page.searchMessage = data.searchMessage;
} else {
$scope.displayMessageToUser = false;
}
}
In spring mvc XML i have :
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="filter.HibernateAwareObjectMapper" />
</property>
</bean>
<bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"/>
</list>
</property>
</bean>
code of class HibernateAwareObjectMapper : public class HibernateAwareObjectMapper extends ObjectMapper {
private static final long serialVersionUID = 1L;
public HibernateAwareObjectMapper() {
registerModule(new Hibernate4Module());
}
}
Web XML i have this filter :
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
; i'm using the following dependency :
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>2.4.0</version>
</dependency>
is there a way to Load collection by Open Session ? thank you in advance for you replay
I solve that by adding this annotation @LazyCollection(LazyCollectionOption.FALSE)
//@JsonSerialize(using = CustomListSerializer.class)
//@JsonView(Views.Internal.class)
//@JsonIgnore
@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(mappedBy = "siteesTypeSite", cascade = CascadeType.ALL,fetch = FetchType.LAZY)
public Set<Sites> getSitees() {
return sitees;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With