简体   繁体   中英

Initialize all lazy loaded collections in hibernate

I have tried most of the solutions for this issue I am facing, but I am still not clear.

I am using Hibernate 4.

I have parent-child relationships in my entity. By default, I have used lazy loading for all the collections in my parent entity. However, in some cases, I need to load the whole object graph. In such cases, I want to force hibernate to load all the collections. I know that by using criteria.setFetchMode("collectionName",FetchMode.JOIN) , I can load a specific collection. However, if I tried to do that for multiple collections, I am getting org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags .

Here is my code:

Entity

Ignoring a few fields and getter and setter methods

public class Employee {
    @Id
    @GeneratedValue
    @Column(name = "EmployeeId")
    private long id;

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @Fetch(FetchMode.SELECT)
    @JoinColumn(name = "EmployeeId")
    @LazyCollection(LazyCollectionOption.TRUE)
    private List<EmployeeAddress> employeeAddresses;

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @Fetch(FetchMode.SELECT)
    @JoinColumn(name = "EmployeeId")
    @LazyCollection(LazyCollectionOption.TRUE)
    private List<EmployeeLanguage> employeeLanguages;

}

HibernateUtil

public HibernateUtil{

    public TEntity findById(Class<TEntity> clazz, long id) {
            Session session = sessionFactory.getCurrentSession();
            Criteria criteria = session.createCriteria(clazz);
            criteria.add(Restrictions.eq("id",id));
            ClassMetadata metadata = sessionFactory.getClassMetadata(clazz);

            String[] propertyNamesArray = metadata.getPropertyNames();
            for(int i=0;i<propertyNamesArray.length;i++){
                criteria.setFetchMode(propertyNamesArray[i], FetchMode.JOIN);
            }
            return (TEntity)criteria.uniqueResult();
        }
}

Service

private void getAllEmployee(Employee employee) {
         //invoke the findAll method of hibernateutil
}

I want to have eager-loading of all the collections of Employee in findAll() method. Please tell me how to get that done.

Adding the stacktrace when using FetchType.SELECT inside the HIbernateUtil

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.myapp.hr.entity.Employee.employeeAddresses, could not initialize proxy - no Session
    org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:566)
    org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:186)
    org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:545)
    org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:124)
    org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:266)
    java.util.Collections$UnmodifiableCollection$1.<init>(Collections.java:1099)
    java.util.Collections$UnmodifiableCollection.iterator(Collections.java:1098)
    com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:90)
    com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:23)
    com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase.serialize(AsArraySerializerBase.java:186)
    com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:569)
    com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:597)
    com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:142)
    com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:118)
    com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:1819)
    org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.writeInternal(MappingJackson2HttpMessageConverter.java:253)
    org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:207)
    org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:148)
    org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:125)
    org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:71)
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:122)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:690)
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    org.apache.catalina.filters.CorsFilter.handleNonCORS(CorsFilter.java:439)
    org.apache.catalina.filters.CorsFilter.doFilter(CorsFilter.java:178)

I mentioned this in a comment in my other answer but it's different enough and I don't want to lose Gimby's insights by editing my original answer.

You can use reflection to lookup the fields with the @LazyCollection annotation and call Hibernate.initialize() on them. It would look something like this:

    public static <T> void forceLoadLazyCollections(Class<T> tClass, T entity) {
        if (entity == null) {
            return;
        }
        for (Field field : tClass.getDeclaredFields()) {
            LazyCollection annotation = field.getAnnotation(LazyCollection.class);
            if (annotation != null) {
                try {
                    field.setAccessible(true);
                    Hibernate.initialize(field.get(entity));
                } catch (IllegalAccessException e) {
                    log.warning("Unable to force initialize field: " + field.getName());
                }
            }
        }
    }

Change your fetch mode to FetchMode.SELECT . Using a join to pull in a collection can only be applied to one collection/table because it causes lots of duplicate data to be pulled across the wire. Using separate selects to pull in collections can be used as much as you want.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM