【问题标题】:Initialize all lazy loaded collections in hibernate在休眠中初始化所有延迟加载的集合
【发布时间】:2014-06-20 12:31:52
【问题描述】:

我已经尝试了我所面临的这个问题的大部分解决方案,但我仍然不清楚。

我正在使用 Hibernate 4。

我的实体中有父子关系。默认情况下,我对父实体中的所有集合使用延迟加载。但是,在某些情况下,我需要加载整个对象图。在这种情况下,我想强制休眠加载所有集合。我知道通过使用criteria.setFetchMode("collectionName",FetchMode.JOIN),我可以加载特定的集合。但是,如果我尝试为多个集合执行此操作,我会得到org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags

这是我的代码:

实体

忽略一些字段和getter和setter方法

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();
        }
}

服务

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

我想在 findAll() 方法中预先加载 Employee 的所有集合。 请告诉我如何完成。

在 HIbernateUtil 中使用 FetchType.SELECT 时添加堆栈跟踪

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)

【问题讨论】:

  • 你不能简单地将获取模式更改为渴望,而不是懒惰吗?
  • 没有。除了这种情况,我需要它变得懒惰。

标签: java hibernate


【解决方案1】:

我在其他答案的评论中提到了这一点,但它已经足够不同了,我不想通过编辑我的原始答案而失去 Gimby 的见解。

您可以使用反射来查找带​​有@LazyCollection 注释的字段并在它们上调用Hibernate.initialize()。它看起来像这样:

    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());
                }
            }
        }
    }

【讨论】:

    【解决方案2】:

    将您的获取模式更改为FetchMode.SELECT。使用连接拉入集合只能应用于一个集合/表,因为它会导致大量重复数据被拉过网络。可以根据需要使用单独的选择来拉入集合。

    【讨论】:

    • 如果我使用 FetchMode.Select,我会得到 LazyInitializationException。它不会强制加载延迟加载的集合:(
    • 是否可以为您检索到的实体上的每个集合调用Hibernate.initialize()
    • 为何如此令人惊讶? FetchMode 的 javadoc 明确指出:“表示关联获取策略。它与 Criteria API 一起用于指定运行时获取策略。”所以它显然只会影响标准 API。告诉 Hibernate “是的,使用 LAZY 获取......但仍然 EAGERly 获取它们!”这将是非常矛盾的。 Lazy 并不真正暗示如何获取数据,它暗示何时获取数据:lazy = 尽可能晚。
    • 其实我想说的是:在这种情况下,您不希望这是一个自动映射,这需要是一个 DAO 方法,它可以在适当的时间获取相关数据。
    • @Gimby 我面临的问题是,我的一些实体中有超过 10 个集合。当我需要将整个对象一起加载时,我必须为每个集合执行Hibernate.initialize("collection1")。我想避免在每个 DAO 层中这样做,而是公开一个通用方法来 EAGER 加载整个实体。这不是正确的方法吗?
    猜你喜欢
    • 2013-09-14
    • 1970-01-01
    • 1970-01-01
    • 2014-01-10
    • 1970-01-01
    • 1970-01-01
    • 2021-06-27
    • 2020-05-10
    • 2012-11-01
    相关资源
    最近更新 更多