13

每当我想保留任何实体时,都会执行以下代码。事情似乎工作正常,但我不明白它是如何工作的!

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 实例吗?这不会是服务器上的杀戮!我们应该汇集这些实例吗?它不等于再次实施某种连接池吗?

4

4 回答 4

7

它为事务使用ThreadLocal变量。

另请参阅UserTransaction的文档:

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

不过,您不应该共享 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-直

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

于 2012-06-27T08:16:05.133 回答
6

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

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

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

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

于 2012-06-27T08:13:37.557 回答
2

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

于 2012-06-27T07:16:19.070 回答
0

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

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

于 2012-06-27T07:48:24.527 回答