JPA中PersistenceUnit與PersistenceContext區別

banq發表於2024-04-27

PersistenceContext持久化上下文和PersistenceUnit持久化單元是 JPA 中的兩個重要概念,我們用它們來管理應用程式中實體的生命週期。

在本教程中,我們將簡要介紹實體管理器和實體管理器工廠。接下來,我們將瞭解永續性上下文為何如此重要及其用例。最後,我們將看到永續性單元的作用及其用例。

EntityManager和EntityManagerFactory
在深入瞭解細節之前,必須對EntityManager和EntityManagerFactory介面有基本的瞭解。正如我們將看到的,它們在管理永續性、實體和資料庫互動方面發揮著重要作用。

EntityManager實體管理器
EntityManager是一個與持久化上下文互動的介面。它對實體執行 CRUD 操作、跟蹤更改並確保在事務提交時與資料庫同步。 EntityManager代表永續性上下文並在事務範圍內執行。

EntityManagerFactory實體管理器工廠
EntityManagerFactory是一個建立EntityManager的介面,有效地充當工廠。建立後,EntityManagerFactory與特定的永續性單元相關聯,從而可以建立EntityManager 的例項。

PersistenceContext持久化上下文
PersistenceContext 是一個短暫的、事務範圍的上下文,用於管理實體的生命週期。它表示作為實體管理器的一級快取儲存在記憶體中的一組託管實體 。如果事務開始,則會建立永續性上下文,並最終在事務提交或回滾時關閉或清除。

永續性上下文自動檢測對託管實體所做的更改,並確保所有實體更改與永續性儲存同步。

我們可以使用@PersistenceContext註釋來定義持久化上下文的型別:

@PersistenceContext
private EntityManager entityManager;

JPA 中有兩種型別的持久化上下文:TRANSACTION 和EXTENDED。

讓我們首先使用@Entity註釋建立與PRODUCT表對應的實體:

@Entity
@Table(name = <font>"PRODUCT")
public class Product {
    
    @Id
    private Long id;
    private String name;
    private double price;
   
// standard constructor, getters, setters<i>
}

現在,讓我們建立服務類 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);
    }
}


事務範圍的持久化上下文PersistenceContext:
TRANSACTION PersistenceContext型別是 JPA 中的預設持久化上下文。在此型別中,PersistenceContext繫結到事務。這意味著每個事務都會建立和銷燬PersistenceContext。

讓我們使用TRANSACTION型別持久化上下文來持久化產品。我們將儲存Product實體,並且在提交事務時更改將自動保留:

@Test
void whenProductPersistWithTransactionPersistenceContext_thenShouldPersist() {
    Product p = new Product(1L, <font>"Product 1", 100.0);
    persistenceContextProductService.insertProductWithTransactionTypePersistenceContext(p);
    Product productFromTransactionScoped = persistenceContextProductService.findWithTransactionTypePersistenceContext(1L);
    Assertions.assertNotNull(productFromTransactionScoped);
    Product productFromExtendedScoped = persistenceContextProductService.findWithExtendedTypePersistenceContext(1L);
    Assertions.assertNotNull(productFromExtendedScoped);
}

EXTENDED擴充套件的持久化上下文
EXTENDED PersistenceContext型別將PersistenceContext的範圍擴充套件到事務邊界之外。我們可以透過帶有EXTENDED型別的@PersistenceContext註釋來建立它。

現在,讓我們使用EXTENDED型別持久化上下文來持久化產品,並且不使用事務。產品將僅儲存在永續性上下文中:

@Test
void whenProductPersistWithExtendedPersistence_thenShouldPersist() {
    Product product = new Product(2L, <font>"Product 1", 100.0);
    persistenceContextProductService.insertProductWithExtendedTypePersistenceContext(product);
    Product productFromExtendedScoped = persistenceContextProductService.findWithExtendedTypePersistenceContext(2L);
    Assertions.assertNotNull(productFromExtendedScoped);
    Product productFromTransactionScoped = persistenceContextProductService.findWithTransactionTypePersistenceContext(2L);
    Assertions.assertNull(productFromTransactionScoped);
}

應用程式在刪除 bean 或有意關閉擴充套件永續性上下文時提交更改。

PersistenceUnit持久化單元
PersistenceUnit定義了實體類集及其配置,並且它表示實體管理器管理的這些實體的邏輯分組。我們可以透過建立persistence.xml檔案或擴充套件PersistenceUnitInfo介面來建立永續性單元。

@PersistenceUnit JPA註釋將實體管理器工廠注入到 bean 中:

@PersistenceUnit(name = <font>"persistence-unit-name")
private EntityManagerFactory entityManagerFactory;

持久化單元支援兩種型別:RESOURCE_LOCAL 和 JTA。

持久化單元的一大優點是我們可以在同一個應用程式中定義多個持久化單元,每個持久化單元適用於系統的不同部分甚至單獨的資料庫。

1、資源本地持久化單元
預設情況下,Spring應用程式使用資源本地持久化單元。在資源本地PersistenceUnit中,我們負責管理事務。它不依賴於外部事務管理器。

讓我們宣告一個persistence.xml檔案,位於類路徑的META-INF/persistence.xml中:

<persistence-unit name=<font>"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>

正如我們所看到的,我們使用資料庫連線屬性定義永續性單元。此外,我們還配置 Hibernate 屬性,包括方言、事務設定和永續性操作的其他屬性。每次應用程式與資料庫互動時,它都會在永續性單元的上下文中執行。我們在永續性單元內定義 Java 實體和資料庫表之間的對映。

現在,讓我們在PersistenceUnitProductService 類中使用這個永續性單元:

@Service
public class PersistenceUnitProductService {
    @PersistenceUnit(name = <font>"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, <font>"Product 1", 100.0);
    persistenceUnitProductService.insertProduct(p);
    Product createdProduct = persistenceUnitProductService.find(1L);
    assertNotNull(createdProduct);
}

2、JTA持久單元
使用JTA意味著我們將工作委託給容器。因此,我們無法透過EntityManagerFactory獲取EntityManager。相反,我們必須使用容器透過@PersistenceContext註釋提供和注入的EntityManager。

企業應用程式在 TomEE 和 WildFly 等 Java EE 容器中部署時通常使用 JTA 永續性單元。


 

相關文章