【问题标题】:How JPA transactions worksJPA 事务的工作原理
【发布时间】:2012-06-28 13:57:55
【问题描述】:

只要我想持久化任何实体,就会执行以下代码。事情似乎工作正常,但我不明白它是如何工作的!

EntityManager em = getEntityManager();
EntityTransaction userTransaction = em.getTransaction();
userTransaction.begin();
em.persist( ent );
userTransaction.commit();

上面的 EntityManager 是整个应用程序共享的单个实例。开始交易后;我只是说em.persist(entity).. hibernate怎么知道它属于哪个事务!

假设我的应用程序上有 10 个并发用户,并且所有 10 个线程都在执行上述代码。因此,创建并提交了 10 个独立事务。但是所有 10 个不同的实体我都没有将它们与各自的交易相关联;那么 JPA 是如何解决的呢!

基于答案;我们有以下;我们是说每个线程都应该有一个 EntityManager 实例吗?这不会是服务器上的杀戮!我们应该汇集这些实例吗?难道不等于再次实现某种连接池吗?

【问题讨论】:

  • “哪笔交易”是什么意思?一个新兴市场在任何时候都有一个单一的(本地)交易;它没有多个。跨多个线程共享单个 EM 会导致问题,因为它不能保证是线程安全的。
  • docs.jboss.org/hibernate/orm/4.0/hem/en-US/html/… EntityManagerFactory 是一个创建成本高、线程安全的对象,旨在由所有应用程序线程共享。它创建一次,通常在应用程序启动时创建。 EntityManager 是一个廉价的、非线程安全的对象,应该使用一次,用于单个业务流程、单个工作单元,然后丢弃
  • 关于进一步阅读;我的理解是 EntityManager 的创建是轻量级的,但对于我们创建的每个小型数据库操作来说仍然不是很轻。建议的解决方案是根据请求创建 EM。所以,看起来应该是 ThreadLocal 级别的。
  • 我了解 EntityManager 不是线程安全的,我们应该为其创建新对象。但是我们是否也需要为 select 查询创建新的 EntityManagers(我们不启动任何事务).. 这不是矫枉过正吗?
  • 需要EntityManager操作的时候需要创建EntityManager,如果需要Query那你怎么获取Query呢?

标签: hibernate jpa transactions jpa-2.0


【解决方案1】:

它使用ThreadLocal 变量进行交易。

另请参阅UserTransaction 的文档:

开始()
创建一个新事务并将其与当前线程关联。

您不应该共享 EntityManager,因为它不能保证是线程安全的。

但是,如果您将其注入到 EJB 中,则不必担心线程安全:http://www.adam-bien.com/roller/abien/entry/is_in_an_ejb_injected

如果你使用 Spring 注入它,你会得到一个线程安全的代理:http://static.springsource.org/spring/docs/3.1.1.RELEASE/spring-framework-reference/html/orm.html#orm-jpa-straight

虽然 EntityManagerFactory 实例是线程安全的,但 EntityManager 实例不是。注入的 JPA EntityManager 的行为类似于从应用程序服务器的 JNDI 环境中获取的 EntityManager,如 JPA 规范所定义。它将所有调用委托给当前事务 EntityManager,如果有的话;否则,它会回退到每个操作新创建的 EntityManager,实际上使其使用线程安全。

【讨论】:

  • 至少对于 Hibernate 的新实现来说,这不是真的。为什么它会在仅用于单线程的对象中使用 ThreadLocal?
  • 这似乎取决于你在哪里使用它:stackoverflow.com/questions/8603478/…
  • 不,注入本身并不能使实体管理器线程的单个实例安全。相反,容器对 ejb 提供者的序列化访问保证了它不会被多个线程同时使用。您还可以使用 jndi 查找代替注入,并通过 ejb 合约保证线程安全。
  • 注入可以通过注入委托给线程特定实例的代理来实现。春天这样做:static.springsource.org/spring/docs/3.1.1.RELEASE/…
  • 当然,没错。我只是没有看到任何理由假设在这种情况下使用了 Spring 组件。
【解决方案2】:

它之所以有效,是因为你足够幸运。足够幸运意味着 commit 和 begin 被以正确的顺序调用 - 不小心。

您确实使用来自多个线程的实体管理器的单个实例。这是错误的做法,因为不能保证它是线程安全的。通过 EntityTransaction 访问资源级事务绑定到实体管理器实例,而不是线程。

所以结果是您共享相同的 EntityTransaction 并幸运地连续使用它进行多个事务。串行使用它来启动和结束多个事务是可以的,但在多个线程中使用它是不行的。

在休眠 (4.1.4) 中,引用存储到 AbstractEntityManageImpl 类中的 tx 实例字段,但这只是实现细节。

【讨论】:

  • 我们是说每个线程都应该有一个 EntityManager 实例吗?这不会是服务器上的杀戮!我们应该汇集这些实例吗?这不等于再次实现某种连接池吗?
  • EntityManager 是很轻的创建对象,EntityManagerFactory 是重量级的。没有理由启动池实体管理器。
  • 我了解 EntityManager 不是线程安全的,我们应该为其创建新对象。但是我们是否也需要为 select 查询创建新的 EntityManagers(我们不启动任何事务).. 这不是矫枉过正吗?
  • 用于 SELECT、UPDATE 或 INSERT 没有区别。一个实例可用于多个查询/操作,但不能同时使用。
【解决方案3】:

事务以某种方式与当前线程相关联,使用 ThreadLocal 变量。

【讨论】:

  • 以上设计完美与否!我还是一头雾水!
  • 这一切都取决于您如何获得 EntityManager 的实例。如果它被注入到 EJB 或 Spring bean 中(如 Spring 文档中所述),那么 EntityManager 实际上是实际 EntityManager 实现的代理。如果没有,请参阅 Mikko Maunu 的回答。
  • 我了解 EntityManager 不是线程安全的,我们应该为其创建新对象。但是我们是否也需要为 select 查询创建新的 EntityManagers(我们不启动任何事务).. 这不是矫枉过正吗?
【解决方案4】:

我建议您了解 JTA 的工作原理,而不管 Hibernate 是什么 - 了解这一点对您来说非常重要
此外,阅读有关容器管理事务和 bean 管理事务的信息。
如果您在容器管理的事务中工作,则可以为要注入 EntityManager 的 bean 指定事务范围 -
例如 - 如果范围是必需的,则意味着如果另一个 bean 调用此 bean,而不是在事务上下文中,则将打开一个新事务。如果交易已经存在,那么您将使用相同的交易。理解这一点很重要,因为事务在您的系统中是一种昂贵的资源。

事务对象与 ThreadLocal 相关联,但是,另一个线程可能会恢复暂停的事务,这取决于您的 TransactionManager 的实现(我说的是JBossTransactionManager

【讨论】:

  • 他在这里使用本地事务,如对 em.getTransaction() 的调用所示。在那种情况下,每个 EM 只有 1 笔交易,他的设计有缺陷。
猜你喜欢
  • 2021-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-21
  • 2010-09-09
  • 2013-04-15
相关资源
最近更新 更多