【问题标题】:Struggling to understand EntityManager proper use努力理解 EntityManager 的正确使用
【发布时间】:2014-05-11 11:10:06
【问题描述】:

我正在开始一个新项目,而且我对 JPA/Hibernate 的使用完全陌生。我正在尝试了解如何正确使用 EntityManager。更准确地说,什么时候实例化它们,我需要多少,我应该关闭它们,我应该把所有东西都放入事务中吗?

无论如何,在我当前的代码中,我在尝试读取之前保存的实体时遇到了 org.hibernate.LazyInitializationException。我会理解相反的情况(在事务中读取一个对象,然后尝试将读取的实体保存在另一个事务中,但由于事务结束,实体不受管理,因此保存失败),但我无法理解。

我将我的代码放在 GitHub (https://github.com/GaetanLeu/intl) 上,它只是几个类。我的主要在 src/sandbox/MessageSandbox.java 中,它在第 28 行失败,并带有以下堆栈跟踪:

Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:164)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:285)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
    at entity.MessageKey_$$_jvstfcc_0.toString(MessageKey_$$_jvstfcc_0.java)
    at java.lang.String.valueOf(String.java:2854)
    at java.lang.StringBuilder.append(StringBuilder.java:128)
    at com.google.common.base.Present.toString(Present.java:88)
    at java.lang.String.valueOf(String.java:2854)
    at java.io.PrintStream.println(PrintStream.java:821)
    at sandbox.MessageSandbox.main(MessageSandbox.java:28)

我还收到来自 Hibernate 的警告,说我的 EntityManager 已经存在,然后会发生什么? EntityManagerFactory.createEntityManager 方法是否返回现有的?

WARN: HHH000436: Entity manager factory name (intl) is already registered.  If entity manager will be clustered or passivated, specify a unique value for property 'hibernate.ejb.entitymanager_factory_name'

我真的不知道何时创建 EntityManagers ^^ 任何帮助将不胜感激,但请简单解释一下,我对此真的很陌生。

哦,顺便说一句,我想准确地说我没有使用 Spring,我没有 EJB,我现在想手动操作 EntityManager,直到我理解为止。谢谢:)

【问题讨论】:

    标签: hibernate jpa entitymanager


    【解决方案1】:

    entityManager 管理一个persistence context,换句话说就是数据库状态的内存快照。

    What is a persistence object?

    使用 entityManager 加载的每个对象都将处于托管状态(请参阅 entity life cycle) 直到您关闭 EM。当实体为 managed 时,对其所做的所有更改都将被跟踪,然后在刷新 EM 时保持不变。如果你访问一些惰性属性,会自动触发一个请求来动态加载数据,但是如果实体处于分离状态(如果 EM 已经关闭)访问惰性属性会导致你得到的错误。

    您的 EM 的范围(/生命周期)取决于您的执行上下文。例如,对于 Web 应用程序,通常会为每个 http 请求创建一个 EM。

    对于独立应用程序,您必须考虑数据库是否可以由另一个应用程序/线程更新。如果可以,您的持久上下文可能与数据库状态不一致,您应该为每个工作单元(事务)创建它以避免这种情况。否则,您可以为所有应用程序生命周期创建一个实例并定期刷新它。

    对于 CRUD 应用,生命周期通常如下:

    • 创建 EM
    • 获取一些实体(它们是这样管理的,任何对惰性属性的访问都会从数据库中加载数据)
    • 关闭 EM(实体现已分离,任何对惰性属性的访问都会导致 LazyInitializationException)
    • 向用户显示数据

    关于用户更新验证:

    • 创建 em
    • 开启交易
    • 合并(附加)您更新的实体(这就是您所说的保存)(如果您设置了一些光学锁定,em 将在此处根据数据库检查实体版本)
    • 最终执行一些业务验证或其他更新
    • 提交事务并关闭 em(更改将被刷新)

    请记住,EM 是一个轻量级对象,创建和销毁都很便宜,而且不是 THREADSAFE。

    顺便说一句,JPA 是一种 Java EE 规范,它是 EJB 规范的一部分(持久性部分)。它的目的是用于 Java EE 容器上下文(Java EE 应用程序服务器或自 JEE 6 起的 CDI)。您仍然可以通过 JPA 合约在独立模式下使用 hibernate,但即使在这种情况下,也必须考虑与 spring 耦合以利用容器管理的特性。

    【讨论】:

    • 感谢您的回答。所以,如果明白你在说什么,因为我的“保存”功能和我的“读取”功能都使用了自己的 EntityManager,并且由于我在保存我的实体后没有手动刷新,所以当我的读取功能到达时,第二个EM 实例化,我的实体没有被持久化并且无法找到。但是为什么我会得到一个“LazyInitializationException”而不是 NPE,因为它应该是找不到的整个实体,不是吗?
    • 顺便说一句,为什么在调用 .persist 时更改没有立即持久化? “已提交”的更改实际上并未保留在数据库中,这不是很危险吗?特别是多个应用程序/EM 查询这个数据库?这里的最佳做法是什么?每次坚持都要冲洗吗? /提交?
    • "当我的读取函数到达实例化的第二个 EM 时,我的实体没有被持久化并且无法找到。"不不,你误会了。您收到错误是因为您在评估延迟获取的属性之前关闭了 EM。
    • 每个线程一次应该只打开一个 EM,同时维护 2 个持久性上下文没有兴趣。这不仅浪费资源,而且会导致问题,因为他们不会知道彼此的修改
    • persist 只是将实体的状态从“新”更改为“托管”,因为合并只会将状态从“分离”更改为托管。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-23
    • 2011-12-30
    • 1970-01-01
    • 1970-01-01
    • 2015-01-20
    • 1970-01-01
    相关资源
    最近更新 更多