Java Hibernate LazyInitializationException on find()和EAGER@ElementCollection

Java Hibernate LazyInitializationException on find()和EAGER@ElementCollection,java,hibernate,jpa,persistence,Java,Hibernate,Jpa,Persistence,我在JPA代码中获得org.hibernate.LazyInitializationException:非法访问加载集合——当集合实体也有一个集合时,所有集合都是急切获取的 有人能帮我修一下吗 我已将JPA代码中的一个问题隔离到以下@Entity定义中: (注意,我跳过了package和import语句以缩短代码。使用了一些Lombok注释,例如@Data表示该字段有一个getter/setter和@Cleanup来执行通常的try/catch close()舞蹈) 以下是一个简短的应用程序,举

我在JPA代码中获得
org.hibernate.LazyInitializationException:非法访问加载集合
——当集合实体也有一个集合时,所有集合都是急切获取的

有人能帮我修一下吗

我已将JPA代码中的一个问题隔离到以下
@Entity
定义中:

(注意,我跳过了package和import语句以缩短代码。使用了一些Lombok注释,例如@Data表示该字段有一个getter/setter和@Cleanup来执行通常的try/catch close()舞蹈)

以下是一个简短的应用程序,举例说明了该问题:

public class JpaQuestion {
    public static void main(String[] args) throws Exception {
        Properties properties = new Properties();
        properties.put("hibernate.connection.driver_class", "org.apache.derby.jdbc.EmbeddedDriver");
        properties.put("hibernate.connection.url", "jdbc:derby:playground;create=true");
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("PlaygroundPU", properties);

        populate(emf);

        @Cleanup("close") EntityManager em = emf.createEntityManager();
        MyOtherEntity other = em.find(MyOtherEntity.class, 1L);
        System.out.println(other != null ? other.toString() : "null");
    }

    public static void populate(EntityManagerFactory emf) {
        @Cleanup("close") EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        MyEntity a = new MyEntity();
        em.persist(a);
        MyOtherEntity other = new MyOtherEntity();
        other.getEntities().add(a);
        em.persist(other);
        em.getTransaction().commit();
    }
}
更新:我知道,但这似乎是因为
load()
获取了实体的惰性版本。我用的是“查找”。我注意到,如果我发出一个JPA查询(而不是find),那么这个问题就会消失

更新:如果我不使用
find()
,而是使用类似于
“从MyTheEntity b中选择b,其中b.id=:id”
的查询,那么这确实可以很好地工作。也许
find()
真的忽略了
EAGER
加载!因此,这可能是Hibernate中的一个bug

更新:我已将此记录为Hibernate的错误报告

LazyInitializationException–表示在会话上下文之外访问未蚀刻的数据。例如,在会话关闭后访问未初始化的代理或集合时

一些您可能想尝试的事情:

已删除
@Cleanup
-通常意味着当代理尝试访问字段时,Hibernate会话已关闭,因此您应该在没有这些注释的情况下尝试。我自己从来没有使用过它们,但是文档说通过调用
.close()
“作用域的结束”来清理变量声明

双重检查配置-仍然是奇数,正如您声明的两个关联的
FetchType.EAGER
。你检查过这些选项是否真的被使用了吗?我知道配置有时可能会变得有点棘手

PersistenceContextType-您可能希望尝试为您的
EntityManager
设置(我认为
事务
是默认设置,可能是错误的,但您可能希望尝试确定)


我想您已经知道wiki条目了吧?

问题是Hibernate忽略了fetch=FetchType.eagent以进行大多数查询。尝试将@Fetch(FetchMode.JOIN)添加到实体

见:

首先,参考完整堆栈跟踪非常有用:

问题是由您在MyEntity的hashCode()实现中调用tags.hashCode()引起的

当您使用Hibernate加载MyotEntity实例时,它的MyEntity集合将被初始化。当MyEntity被添加到MyEntity内的集合实现时,它的hashCode()方法会被自然调用(集合不能包含重复项,记住,hashCode()是Java检查对象相等性的一部分)。MyEntity的hashCode()方法然后尝试调用tags集合上的hashCode()。标记集合正在初始化过程中,这会导致此错误

我认为值得考虑一下MyEntity的hashCode()实现。您的用例真的需要您比较标记集合中所有元素的值以确保对象相等吗

有关在Hibernate中管理对象相等性的详细信息,请参阅以下有用的资源:


当hibernate尝试初始化对象时出现此错误,并且出现一些错误

错误可以是

  • 未正确实现哈希代码和等于
  • 惰性对象应加密,但在db中未正确加密
  • 您在hibernate会话上下文之外

  • 异常在代码的哪一行引发?请确认:如果没有
    标记
    字段,此代码将按预期工作?是的,不带标记。find()方法调用出现异常。谢谢。删除
    close()
    调用将违反JPA准则,因此我不会毫无理由地这样做-在这种情况下,它将导致内存泄漏(大量打开的
    EntityManager
    s)。正在使用
    EAGER
    ,因为如果我删除它们,则代码的其他部分将被破坏。我不喜欢
    扩展的
    配置,因为
    事务的
    对我来说很好-我希望实例被分离。我已经用一些额外的信息更新了这个问题。谢谢,但这与HibernateAPI有关,我想坚持使用纯JPA注释,这似乎打破了注释的期望。请注意,
    Query
    实际上是有效的,只有
    find()
    失败。谢谢!这件事让我想把头发扯下来。仅使用
    id
    equals/
    hashCode
    对我的应用程序来说没有意义,设置业务密钥会带来太多开销。相反,我可以接受哈希冲突的影响,只需从我的
    @MyEntity
    hashCode
    中删除对
    标记.hashCode>的调用。我已经提交了一份RFE,以获得更高级的Lombok支持
    
    Exception in thread "main" javax.persistence.PersistenceException: org.hibernate.LazyInitializationException: illegal access to loading collection
        at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1377)
        at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:828)
        at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:781)
    
    public class JpaQuestion {
        public static void main(String[] args) throws Exception {
            Properties properties = new Properties();
            properties.put("hibernate.connection.driver_class", "org.apache.derby.jdbc.EmbeddedDriver");
            properties.put("hibernate.connection.url", "jdbc:derby:playground;create=true");
            EntityManagerFactory emf = Persistence.createEntityManagerFactory("PlaygroundPU", properties);
    
            populate(emf);
    
            @Cleanup("close") EntityManager em = emf.createEntityManager();
            MyOtherEntity other = em.find(MyOtherEntity.class, 1L);
            System.out.println(other != null ? other.toString() : "null");
        }
    
        public static void populate(EntityManagerFactory emf) {
            @Cleanup("close") EntityManager em = emf.createEntityManager();
            em.getTransaction().begin();
            MyEntity a = new MyEntity();
            em.persist(a);
            MyOtherEntity other = new MyOtherEntity();
            other.getEntities().add(a);
            em.persist(other);
            em.getTransaction().commit();
        }
    }