1、概览 {#1概览}
Persistence Context (持久化上下文)和 Persistence Unit(持久化单元)是 JPA 中的两个重要概念,用来管理应用中实体的生命周期。
本文将带你了解 JPA 中的 EntityManager (实体管理器) 的作用,以及 Persistence Context 和 Persistence Unit 的重要性和用例。
2、EntityManager 和 EntityManagerFactory {#2entitymanager-和-entitymanagerfactory}
首先来看看 EntityManager
和 EntityManagerFactory
接口,它们在管理持久性(Persistence)、实体和数据库交互方面发挥着重要作用。
2.1、EntityManager {#21entitymanager}
EntityManager
是一个与 Persistence Context 交互的接口。它对实体执行 CRUD 操作、跟踪更改并确保在事务提交时与数据库同步。EntityManager
代表一个 Persistence Context,并在事务范围内运行。
2.2、EntityManagerFactory {#22entitymanagerfactory}
EntityManagerFactory
是一个创建 EntityManager
的接口,有效地发挥着工厂的作用。创建时,EntityManagerFactory
会与特定的 Persistence Unit 关联,从而创建 EntityManager
的实例。
3、PersistenceContext {#3persistencecontext}
PersistenceContext
是一个短暂的、事务范围的上下文,用于管理实体的生命周期。它代表一组存储在内存中的 "托管实体",是实体管理器的一级缓存。如果事务开始,就会创建持久化上下文,并最终在事务提交或回滚时关闭或清除。
持久化上下文会自动检测对托管实体所做的更改,并确保所有实体更改与持久化存储(Persistence Storage)同步。
我们可以使用 @PersistenceContext
注解定义持久化上下文(Persistence Context)的类型:
@PersistenceContext
private EntityManager entityManager;
JPA 中有两种持久化上下文: TRANSACTION
和 EXTENDED
。
首先,使用 @Entity
注解创建与 PRODUCT
表相对应的实体:
@Entity
@Table(name = "PRODUCT")
public class Product {
@Id
private Long id;
private String name;
private double price;
// 构造函数、Getter、Setter 方法省略
}
然后,创建 Service 类 PersistenceContextProductService
:
@Service
public class PersistenceContextProductService {
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
private EntityManager entityManagerTransactionType;
@PersistenceContext(type = PersistenceContextType.EXTENDED)
private EntityManager entityManagerExtendedType;
@Transactional
void insertProductWithTransactionTypePersistenceContext(Product product) {
entityManagerTransactionType.persist(product);
}
Product findWithTransactionTypePersistenceContext(long id) {
return entityManagerTransactionType.find(Product.class, id);
}
void insertProductWithExtendedTypePersistenceContext(Product product) {
entityManagerExtendedType.persist(product);
}
Product findWithExtendedTypePersistenceContext(long id) {
return entityManagerExtendedType.find(Product.class, id);
}
}
3.1、事务范围的 PersistenceContext {#31事务范围的-persistencecontext}
TRANSACTION PersistenceContext 类型是 JPA 中的默认持久化上下文。在这种类型中,PersistenceContext
与事务绑定。这意味着每个事务都会创建和销毁 PersistenceContext
。
使用 TRANSACTION 类型的持久化上下文(PersistenceContext )来持久化 Product
。
@Test
void whenProductPersistWithTransactionPersistenceContext_thenShouldPersist() {
Product p = new Product(1L, "Product 1", 100.0);
persistenceContextProductService.insertProductWithTransactionTypePersistenceContext(p);
Product productFromTransactionScoped = persistenceContextProductService.findWithTransactionTypePersistenceContext(1L);
Assertions.assertNotNull(productFromTransactionScoped);
Product productFromExtendedScoped = persistenceContextProductService.findWithExtendedTypePersistenceContext(1L);
Assertions.assertNotNull(productFromExtendedScoped);
}
如上,保存 Product
实体,当事务提交时,更改将自动持久化:
3.2、扩展的 PersistenceContext {#32扩展的-persistencecontext}
EXTENDED PersistenceContext 类型将 PersistenceContext 的范围扩展到了事务边界之外。
我们可以通过使用 @PersistenceContext
注解和 EXTENDED
type 来创建它。
现在,使用 EXTENDED
类型持久化上下文(Persistence Context )持久化 Product
,并且不使用事务。Product
将只保存在持久化上下文中:
@Test
void whenProductPersistWithExtendedPersistence_thenShouldPersist() {
Product product = new Product(2L, "Product 1", 100.0);
persistenceContextProductService.insertProductWithExtendedTypePersistenceContext(product);
Product productFromExtendedScoped = persistenceContextProductService.findWithExtendedTypePersistenceContext(2L);
Assertions.assertNotNull(productFromExtendedScoped);
Product productFromTransactionScoped = persistenceContextProductService.findWithTransactionTypePersistenceContext(2L);
Assertions.assertNull(productFromTransactionScoped);
}
应用会在删除 Bean 或有意关闭扩展的持久化上下文(Extended Persistence Context)时提交更改。
4、PersistenceUnit {#4persistenceunit}
PersistenceUnit
定义了一组实体类及其配置,它代表了实体管理器(Entity manager )所管理的这些实体的逻辑分组。我们可以通过创建 persistence.xml
文件或实现 PersistenceUnitInfo
接口来创建持久化单元(Persistence Unit)。
@PersistenceUnit
JPA 注解将实体管理器工厂(Entity Manager Factory)注入到 bean 中:
@PersistenceUnit(name = "persistence-unit-name")
private EntityManagerFactory entityManagerFactory;
持久化单元(Persistence Unit )支持两种类型:RESOURCE_LOCAL
和 JTA
。
持久化单元(Persistence Unit)的一大优势是,我们可以在同一个应用中定义多个持久化单元,每个单元适用于系统的不同部分,甚至是独立的数据库。
4.1、本地资源 PersistenceUnit {#41本地资源-persistenceunit}
默认情况下,Spring 应用使用本地资源 PersistenceUnit。在本地资源 PersistenceUnit 中,我们负责管理事务。它不依赖外部事务管理器(transaction manager)。
在 classpath 下的 META-INF/persistence.xml
中 声明一个 persistence.xml
文件:
<persistence-unit name="com.baeldung.contextvsunit.h2_persistence_unit" transaction-type="RESOURCE_LOCAL">
<description>EntityManager serializable persistence unit</description>
<class>com.baeldung.contextvsunit.entity.Product</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.generate_statistics" value="false"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="jakarta.persistence.jdbc.url" value="jdbc:h2:mem:db2;DB_CLOSE_DELAY=-1"/>
<property name="jakarta.persistence.jdbc.user" value="sa"/>
<property name="jakarta.persistence.jdbc.password" value=""/>
</properties>
</persistence-unit>
如上,通过数据库连接属性定义了持久化单元(Persistence Unit)。此外,还配置了 Hibernate 属性,包括方言、事务设置和其他用于持久化操作的属性。每次应用与数据库交互时,都是在持久化单元的上下文中进行操作。我们在持久化单元中定义 Java 实体和数据库表之间的映射。
现在,在 PersistenceUnitProductService
类中使用这个持久化单元:
@Service
public class PersistenceUnitProductService {
@PersistenceUnit(name = "com.baeldung.contextvsunit.h2_persistence_unit")
private EntityManagerFactory emf;
@Transactional
void insertProduct(Product product) {
EntityManager entityManager = emf.createEntityManager();
entityManager.persist(product);
}
Product find(long id) {
EntityManager entityManager = emf.createEntityManager();
return entityManager.find(Product.class, id);
}
}
持久化一个 Product
实体,以验证是否一切正常:
@Test
void whenProductPersistWithEntityManagerFactory_thenShouldPersist() {
Product p = new Product(1L, "Product 1", 100.0);
persistenceUnitProductService.insertProduct(p);
Product createdProduct = persistenceUnitProductService.find(1L);
assertNotNull(createdProduct);
}
4.2、JTA PersistenceUnit {#42jta-persistenceunit}
使用 JTA 意味着我们将工作委托给容器。因此,我们不能通过 EntityManagerFactory
获取 EntityManager
。相反,我们必须使用容器提供并通过 @PersistenceContext
注解注入的 EntityManager
。
在 TomEE 和 WildFly 等 Java EE 容器中部署时,企业应用通常使用 JTA 持久化单元(Persistence Unit)。
5、总结 {#5总结}
本文介绍了 JPA 中 EntityManager
和 EntityManagerFactory
的作用,以及持久化单元(Persistence Unit )和持久化上下文(Persistence Context)之间的区别。
Ref:https://www.baeldung.com/java-persistenceunit-persistencecontext-difference