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

时间:2012-07-22 22:06:41

标签: java hibernate jpa persistence

我在我的JPA代码中获得org.hibernate.LazyInitializationException: illegal access to loading collection - 当集合实体也有一个集合时,所有集合都是EAGER fetch。

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

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

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

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

(如果我明确地做了一个完整的@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)

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

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

更新:我知道LazyInitializationException when field is eager,但这似乎是因为load()抓住了实体的懒惰版本。我在这里使用“查找”。我注意到如果我发出JPA查询(而不是查找),那么这个问题就会消失。

更新:如果不是find(),我会使用像"SELECT b FROM MyOtherEntity b WHERE b.id = :id"之类的查询,这确实可行。也许find()确实忽略EAGER加载!因此,这可能是Hibernate中的一个错误。

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

4 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

  

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

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

已移除@Cleanup - 由于LazyInitializationException通常意味着当代理尝试访问某个字段时Hibernate会话已关闭,因此您应该尝试使用@Cleanup PersistenceContextType.EXTENDED注释。我自己从未使用过它们,但是文档说通过调用.close()“范围的结尾”来清理变量声明。

Doublecheck配置 - 当您为两个关联声明FetchType.EAGER时仍然很奇怪。你检查过这些选项真的被使用了吗?我知道配置有时可能会有点棘手。

PersistenceContextType - 您可能想尝试为EntityManager设置In the case of a transaction-scoped persistence context, the entities become detached, that is, they are no longer managed. In the case of an extended persistence context, the entities remain managed.(我认为TRANSACTION是默认设置,可能是错误但您可能想要试着确定。)

  

"LazyInitializationException overcome"

我猜你已经知道{{3}} wiki条目了吗?

答案 2 :(得分:0)

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

请参阅: https://community.jboss.org/wiki/HibernateFAQ-AdvancedProblems#Hibernate_ignores_my_outerjointrue_or_fetchjoin_setting_and_fetches_an_association_lazily_using_n1_selects

答案 3 :(得分:0)

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

错误可以是

  1. 哈希代码和等于未正确实施
  2. 延迟对象应该加密,但在db中没有正确加密
  3. 你在hibernate会话上下文之外。