3

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

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

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

(注意,我跳过了 package 和 import 语句来缩短代码。使用了一些 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();
}

(如果我明确地做一个 full @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 ,但这似乎是因为load()抓取了实体的惰性版本。我在这里使用“查找”。我注意到,如果我发出 JPA 查询(而不是 find),那么这个问题就会消失。

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

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

4

4 回答 4

12

首先,参考完整的堆栈跟踪很有用:http: //pastie.org/4358203

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

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

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

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

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

于 2012-07-30T12:05:52.287 回答
1

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

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

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

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

PersistenceContextType - 您可能想尝试PersistenceContextType.EXTENDED为您的设置EntityManager(我认为TRANSACTION是默认设置,可能是错误的,但您可能想尝试确定)。

在事务范围的持久性上下文的情况下,实体变得分离,也就是说,它们不再被管理。在扩展持久性上下文的情况下,实体保持受管理状态。

我猜你已经知道“克服 LazyInitializationException” wiki 条目了?

于 2012-07-23T14:09:29.203 回答
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

于 2012-07-26T15:50:58.180 回答
0

当休眠尝试初始化对象并发生一些错误时出现此错误。

错误可以是

  1. 哈希码和等于未正确实现
  2. 预计惰性对象将被加密,但在 db 中未正确加密
  3. 你在休眠会话上下文之外。
于 2013-11-17T07:31:31.747 回答