【问题标题】:Hibernate LazyInitializationException on find() with EAGER @ElementCollection使用 EAGER @ElementCollection 在 find() 上休眠 LazyInitializationException
【发布时间】:2012-07-21 04:58:15
【问题描述】:

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

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

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

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

@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(),这确实可以正常工作。也许find() 确实忽略了EAGER 加载!因此,这可能是 Hibernate 中的一个错误。

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

【问题讨论】:

  • 代码的哪一行抛出了异常?只是为了确定:如果没有 tags 字段,这段代码可以按预期工作吗?
  • 是的,无需标签即可工作。异常是在 find() 方法调用上。

标签: java hibernate jpa persistence


【解决方案1】:

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

错误可能是

  1. 哈希码和等于未正确实现
  2. 惰性对象应被加密,但在 db 中未正确加密
  3. 您在休眠会话上下文之外。

【讨论】:

    【解决方案2】:

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

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

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

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

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

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

    【讨论】:

    • 谢谢!这让我想撕掉我的头发。仅使用id equals/hashCode 对我的应用程序没有意义,并且设置业务密钥的开销太大。相反,我可以应对哈希冲突,只需从我的@MyEntityhashCode 中删除对tags.hashCode 的调用。我已经在github.com/peichhorn/lombok-pg/issues/115github.com/peichhorn/lombok-pg/issues/115 提交了一份 RFE,以获得更高级的 Lombok 支持
    【解决方案3】:

    问题在于 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

    【讨论】:

    • 谢谢,但这与 Hibernate API 有关,我想坚持纯 JPA 注释,这似乎打破了注释的期望。请注意,Query 确实有效,只有 find() 失败。
    【解决方案4】:

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

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

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

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

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

    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.

    我猜你已经知道"LazyInitializationException overcome" wiki 条目了?

    【讨论】:

    • 谢谢。删除close() 调用将违反JPA 准则,因此我不会在没有充分理由的情况下这样做——在这种情况下,它会导致内存泄漏(大量打开的EntityManagers)。正在使用EAGER,因为如果我删除它们,那么我的代码的其他部分就会被破坏。我不热衷于EXTENDED 配置,因为TRANSACTIONAL 对我来说效果很好——我希望分离实例。我已经用一些额外的信息更新了这个问题。
    猜你喜欢
    • 1970-01-01
    • 2016-09-16
    • 2011-06-25
    • 1970-01-01
    • 2018-01-26
    • 1970-01-01
    • 2011-06-19
    • 2020-10-26
    • 2021-08-21
    相关资源
    最近更新 更多