事务范围的持久化上下文和扩展持久化上下文有什么区别?
这个差异在JSR-220企业级JavaBean 3.0规范中有明确的解释:
(...)
容器管理的持久化上下文的生命周期可以被定义为单个事务作用域或跨越多个事务的扩展生命周期,这取决于创建其EntityManager
时指定的PersistenceContextType
。本规范将这些持久化上下文称为事务作用域持久化上下文和扩展持久化上下文。
(...)
应用程序可以通过注入或在JNDI命名空间中进行直接查找来获取与JTA事务绑定的容器管理的实体管理器,该实体管理器具有事务作用域持久化上下文。实体管理器的持久化上下文类型默认为PersistenceContextType.TRANSACTION
或者被明确定义。
当容器管理的实体管理器在活动JTA事务范围内被调用[36]并且没有与JTA事务关联的当前持久化上下文时,会开始一个新的持久化上下文。持久化上下文被创建然后与JTA事务相关联。
持久化上下文在关联的JTA事务提交或回滚时结束,所有由EntityManager
管理的实体都变为分离状态。
如果在事务范围之外调用实体管理器,则从数据库加载的任何实体都会在方法调用结束时立即变为分离状态。
容器管理的扩展持久化上下文只能在有状态会话bean的范围内启动。它存在于声明对PersistenceContextType.EXTENDED
类型的实体管理器的依赖项的有状态会话bean创建的时刻,并且被绑定到有状态会话bean。通过PersistenceContext
注释或persistence-context-ref部署描述符元素来声明对扩展持久化上下文的依赖项。
当有状态会话bean的@Remove
方法完成(或有状态会话bean实例以其他方式被销毁)时,容器会关闭持久化上下文。
(...)
有很多细节需要注意... 但为了简短起见,我记得区别如下:
简而言之:当调用事务范围bean上的方法时,容器会自动启动事务并为您创建新的持久性上下文。 方法结束时,事务结束且持久性上下文将关闭,您的实体将变为分离状态。
优点:此行为是无状态的,不需要您在代码中进行太多维护,并使您的实体管理器线程安全。
简而言之:仅可用于有状态会话bean,并与bean的生命周期相关联。 持久性上下文可以跨多个事务延伸,这意味着扩展bean中的方法共享相同的持久性上下文。
优点:非常适合实现与客户端的对话式交互。 客户端调用多个bean方法告诉bean您需要知道的所有信息,在对话结束时将所有内容持久化到数据库中。
事务传播:假设具有两个方法A和B的事务范围bean的默认事务属性。
如果在方法A内调用方法B,则可以将A的持久性上下文传播到B。这样,方法B甚至可以访问由A创建/更改但尚未持久化的实体,因为它们仍然由现在B具有访问权限的持久性上下文管理。
从扩展到事务范围的事务传播: 通过从扩展bean中调用事务范围bean的方法,可以将扩展bean的持久性上下文传播到事务范围bean。 使用默认事务属性(REQUIRED
),您的事务范围bean将重复使用已存在且活动的扩展bean的持久性上下文。
从事务范围扩展到扩展:然而,反过来就不太直观了,因为扩展持久性上下文总是试图使自己成为活动持久性上下文。您需要使用@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
更改扩展bean的默认事务属性。这将在扩展bean方法启动之前暂停任何活动事务(与持久性上下文相关联)。
事务范围的持久化上下文
顾名思义,事务范围的持久化上下文与事务的生命周期绑定。它由容器在事务期间创建,并在事务完成时关闭。
事务范围的实体管理器负责在需要时自动创建事务范围的持久化上下文。我们说“需要时”是因为事务范围的持久化上下文创建是惰性的。
只有在实体管理器上调用方法且没有持久化上下文可用时,实体管理器才会创建一个持久化上下文。
扩展的持久化上下文
扩展的持久化上下文的生命周期与其绑定的有状态会话Bean相关联。
与为每个事务创建新的持久化上下文的事务范围实体管理器不同,有状态会话Bean的扩展实体管理器始终使用相同的持久化上下文。
有状态会话Bean关联着单个扩展的持久化上下文,该上下文在Bean实例创建时创建并在Bean实例移除时关闭。这对扩展持久化上下文的关联和传播特性都产生了影响。
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
@PersistenceContext
,这与编写以上语句一样。意思是急切地将更改保存到数据库中。
@PersistenceContext(type = PersistenceContextType.EXTENDED)