使用EAGER @ElementCollection对find()进行Hibernate LazyInitializationException

fom*_*mil 2 java persistence hibernate jpa

我收到org.hibernate.LazyInitializationException: illegal access to loading collection了我的JPA代码 - 所有集合都是EAGER fetch - 当集合实体也有一个集合时.

有人可以帮我解决这个问题吗?

我已将JPA代码中的问题与以下@Entity定义隔离开来:

(注意,我正在跳过包并导入语句以缩短代码.使用一些Lombok注释,例如@Data表示该字段具有getter/setter和@Cleanup来执行通常的try/catch close()舞蹈)

@Entity
@Data
public class MyEntity implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

//    @ElementCollection(fetch = FetchType.EAGER)
//    private Set<String> tags = Sets.newTreeSet();
}

@Entity
@Data
public class MyOtherEntity implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToMany(fetch = FetchType.EAGER)
    private Set<MyEntity> entities = Sets.newHashSet();
}
Run Code Online (Sandbox Code Playgroud)

(如果我明确地做了一个完整的话@JoinTable,我也会遇到同样的问题,但是Hibernate似乎在没有它的情况下生成一切都很好 - 我很高兴把它留下来).

问题是如果我取消注释"标签"字段@MyEntity,那么我总是得到以下内容PersistenceException

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)
Run Code Online (Sandbox Code Playgroud)

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:当字段急切时我知道LazyInitializationException,但这似乎是因为load()抓取了实体的懒惰版本.我在这里使用"查找".我注意到如果我发出JPA查询(而不是查找),那么这个问题就会消失.

更新:这确实工作正常,如果不是find(),我使用像查询"SELECT b FROM MyOtherEntity b WHERE b.id = :id".也许find()真的无视EAGER加载!因此,这可能是Hibernate中的一个错误.

更新:我已将此作为Hibernate的错误报告记录在https://hibernate.onjira.com/browse/HHH-7476

Rob*_*ake 11

首先,引用完整堆栈跟踪非常有用:http://pastie.org/4358203

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

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

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

有关在Hibernate中管理对象相等性的更多信息,以下是一个有用的资源:

https://community.jboss.org/wiki/EqualsAndHashCode