JPA 中的 PersistenceUnit 和 PersistenceContext

1、概览

Persistence Context(持久化上下文)和 Persistence Unit(持久化单元)是 JPA 中的两个重要概念,用来管理应用中实体的生命周期。

本文将带你了解 JPA 中的 EntityManager(实体管理器) 的作用,以及 Persistence ContextPersistence Unit 的重要性和用例。

2、EntityManager 和 EntityManagerFactory

首先来看看 EntityManagerEntityManagerFactory 接口,它们在管理持久性(Persistence)、实体和数据库交互方面发挥着重要作用。

2.1、EntityManager

EntityManager 是一个与 Persistence Context 交互的接口。它对实体执行 CRUD 操作、跟踪更改并确保在事务提交时与数据库同步。EntityManager 代表一个 Persistence Context,并在事务范围内运行。

2.2、EntityManagerFactory

EntityManagerFactory 是一个创建 EntityManager 的接口,有效地发挥着工厂的作用。创建时,EntityManagerFactory 会与特定的 Persistence Unit 关联,从而创建 EntityManager 的实例。

3、PersistenceContext

PersistenceContext 是一个短暂的、事务范围的上下文,用于管理实体的生命周期。它代表一组存储在内存中的 “托管实体”,是实体管理器的一级缓存。如果事务开始,就会创建持久化上下文,并最终在事务提交或回滚时关闭或清除。

持久化上下文会自动检测对托管实体所做的更改,并确保所有实体更改与持久化存储(Persistence Storage)同步。

我们可以使用 @PersistenceContext 注解定义持久化上下文(Persistence Context)的类型:

@PersistenceContext
private EntityManager entityManager;

JPA 中有两种持久化上下文: TRANSACTIONEXTENDED

首先,使用 @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

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

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

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_LOCALJTA

持久化单元(Persistence Unit)的一大优势是,我们可以在同一个应用中定义多个持久化单元,每个单元适用于系统的不同部分,甚至是独立的数据库。

4.1、本地资源 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

使用 JTA 意味着我们将工作委托给容器。因此,我们不能通过 EntityManagerFactory 获取 EntityManager。相反,我们必须使用容器提供并通过 @PersistenceContext 注解注入的 EntityManager

TomEEWildFly 等 Java EE 容器中部署时,企业应用通常使用 JTA 持久化单元(Persistence Unit)。

5、总结

本文介绍了 JPA 中 EntityManagerEntityManagerFactory 的作用,以及持久化单元(Persistence Unit)和持久化上下文(Persistence Context)之间的区别。


Ref:https://www.baeldung.com/java-persistenceunit-persistencecontext-difference