雅加達持久化簡介3.2
一、簡介
Jakarta Persistence(以前稱為 JPA)是 Java 中物件關係映射的標準 API。它**使開發人員能夠管理 Java 應用程式中的關係數據,並透過使用註釋和實體類別將 Java 物件映射到資料庫表來簡化資料庫互動**。
在本教程中,我們將探討 Jakarta Persistence 3.2 中引入的一些關鍵新功能,重點介紹配置、效能和可用性方面的改進。
2.什麼是雅加達持久性3.2?
Jakarta Persistence 3.2 是 Jakarta Persistence API 的最新版本,它為 Java 應用程式中的物件關係映射 (ORM) 提供了標準化方法。
此版本引入了查詢功能、效能、可用性方面的改進以及對現代資料庫功能的增強支援。
要新增對 Jakarta Persistence 3.2 的支持,我們必須將以下Maven 依賴項新增至pom.xml :
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.2.0</version>
</dependency>
此外,我們需要最新的Hibernate 7 版本,它支援此 API:
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>7.0.0.Beta1</version>
</dependency>
3. 主要新功能
Jakarta Persistence 3.2 引入了一些新功能來改善資料庫連接處理、模式配置和事務管理。
3.1.持久化配置
最新的 Jakarta Persistence 3.2 版本添加了程式設計 API,以使用 PersistenceConfiguration 類別而不是傳統的persistence.xml檔案**PersistenceConfiguration EntityManagerFactory介面的實例**- 提供靈活性,特別是在執行時間配置可能不同的環境中。
為了示範新功能和增強功能,讓我們建立帶有id 、 fullName和department等欄位的Employee實體類別:
@Entity
public class Employee {
@Id
private Long id;
private String fullName;
private String department;
// getters and setters ...
}
這裡, @Entity註解顯示Employee類別是一個持久化實體, @Id註解將id欄位標記為主鍵。
現在,讓我們使用新引入的PersistenceConfiguration類別以程式設計EntityManagerFactory類別的實例:
EntityManagerFactory emf = new PersistenceConfiguration("EmployeeData")
.jtaDataSource("java:comp/env/jdbc/EmployeeData")
.managedClass(Employee.class)
.property(PersistenceConfiguration.LOCK_TIMEOUT, 5000)
.createEntityManagerFactory();
assertNotNull(emf);
我們透過設定資料來源、註冊實體類別以及配置鎖定逾時等屬性來建立EntityManagerFactory的實例。
3.2.架構管理器 API
新版本的 Jakarta Persistence 還引入了 Schema Manager API,允許開發人員以程式設計方式管理 schema 。這簡化了開發和生產環境中的資料庫遷移和模式驗證。
例如,我們現在可以使用 API 啟用架構建立:
emf.getSchemaManager().create(true);
總共有四個可用於模式管理的功能:
-
create():建立與持久性單元中的實體關聯的表 -
drop():刪除與持久性單元中的實體關聯的表 -
validate():根據實體映射驗證模式 -
truncate():清除與實體相關的表中的數據
3.3.運行/調用事務
現在有像runInTransaction()和callInTransaction() to這樣的新方法,透過為應用程式管理的EntityManager提供活動事務來改進資料庫事務的處理。
透過這些方法,我們可以在事務範圍內執行操作,並在必要時存取底層資料庫連線。
我們可以使用這些方法在事務中運行查詢並直接對資料庫連接進行操作:
emf.runInTransaction(em -> em.runWithConnection(connection -> {
try (var stmt = ((Connection) connection).createStatement()) {
stmt.execute(
"INSERT INTO employee (id, fullName, department) VALUES (8, 'Jane Smith', 'HR')"
);
} catch (Exception e) {
Assertions.fail("JDBC operation failed");
}
}));
var employee = emf.callInTransaction(em -> em.find(Employee.class, 8L));
assertNotNull(employee);
assertEquals("Jane Smith", employee.getFullName());
首先,我們使用runInTransaction()執行 SQL,在事務中將新員工插入資料庫。然後, callInTransaction()方法檢索並驗證插入的員工的詳細資訊。
3.4. TypedQueryReference介面
命名查詢通常由 Jakarta Persistence 中的字串引用,這使得它們容易出現錯誤,例如查詢名稱中的拼字錯誤。
新引入的TypedQueryReference介面旨在透過將命名查詢連結到靜態元模型來解決此問題,從而使它們在編譯時類型安全且可發現。
讓我們使用命名查詢更新Employee實體,以使用department欄位進行搜尋:
@Entity
@NamedQuery(
name = "Employee.byDepartment",
query = "FROM Employee WHERE department = :department",
resultClass = Employee.class
)
public class Employee {
// ...
}
編譯後,將產生對應的靜態元模型,如下所示:
@StaticMetamodel(Employee.class)
@Generated("org.hibernate.processor.HibernateProcessor")
public abstract class Employee_ {
public static final String QUERY_EMPLOYEE_BY_DEPARTMENT = "Employee.byDepartment";
public static final String FULL_NAME = "fullName";
public static final String ID = "id";
public static final String DEPARTMENT = "department";
// ...
}
現在,我們可以使用QUERY_EMPLOYEE_BY_DEPARTMENT常數來引用Employee實體上定義的命名查詢byDepartment :
Map<String, TypedQueryReference> namedQueries = emf.getNamedQueries(Employee.class);
List employees = em.createQuery(namedQueries.get(QUERY_EMPLOYEE_BY_DEPARTMENT))
.setParameter("department", "Science")
.getResultList();
assertEquals(1, employees.size());
在程式碼片段中,我們可以觀察到EntityManagerFactory的getNamedQueries()方法傳回命名查詢及其TypedQueryReference的對應。然後,我們使用EntityManager的createQuery()方法來取得Science部門的員工,並斷言該清單僅包含一個結果,從而確認查詢的預期輸出。
因此, TypedQueryReference介面可確保命名查詢存在並被正確引用,從而提供編譯時驗證。
3.5. EntityGraph中的類型安全
Jakarta Persistence 的實體圖允許在執行查詢時立即載入屬性。
現在,使用新版本的 Jakarta Persistence,它們也是類型安全的 -確保圖中引用的屬性有效並在編譯時存在,從而降低了錯誤風險。
例如,讓我們使用靜態元模型Employee_來確保編譯時的類型安全:
var employeeGraph = emf.callInTransaction(em -> em.createEntityGraph(Employee.class));
employeeGraph.addAttributeNode(Employee_.department);
var employee = emf.callInTransaction(em -> em.find(employeeGraph, 7L));
assertNotNull(employee);
assertEquals("Engineering", employee.getDepartment());
在這裡,從靜態元模型類別存取屬性department ,該類別驗證它是否存在於Employee類別中,否則,如果屬性錯誤,則會產生編譯錯誤。
4. 可用性增強
Jakarta Persistence 3.2 引入了多項效能和可用性增強功能,以簡化資料庫查詢並提高整體應用程式效能。
4.1.化簡的 JPQL
JPQL 現在支援這種簡化的查詢語法,並且通常用於 Jakarta 資料查詢語言(JPQL 的子集)。
例如,當實體未指定別名時,它會自動預設為關聯表:
Employee employee = emf.callInTransaction(em ->
em.createQuery("from Employee where fullName = 'Tony Blair'", Employee.class).getSingleResult()
);
assertNotNull(employee);
在這裡,我們沒有為Employee實體指定別名。相反,別名預設為this ,允許我們直接對實體執行操作,而無需限定欄位名稱。
4.2. cast()
Jakarta Persistence中新的cast()方法允許我們轉換查詢結果:
emf.runInTransaction(em -> em.persist(new Employee(11L, "123456", "Art")));
TypedQuery<Integer> query = em.createQuery(
"select cast(e.fullName as integer) from Employee e where e.id = 11", Integer.class
);
Integer result = query.getSingleResult();
assertEquals(123456, result);
在此範例中,我們首先插入一筆新的Employee記錄,其 fullName 的值為123456 fullName.然後,使用 JPQL 查詢,我們將String屬性fullName轉換為Integer 。
4.3. left()和right()函數
接下來,JPQL 也允許像left()這樣的字串操作方法使用索引值來提取子字串:
TypedQuery<String> query = em.createQuery(
"select left(e.fullName, 3) from Employee e where e.id = 2", String.class
);
String result = query.getSingleResult();
assertEquals("Tom", result);
在這裡,我們使用 JPQL 函數left()從fullName的左側提取了子字串Tom 。
同樣,它也提供了right()方法來提取子字串:
query = em.createQuery("select right(e.fullName, 6) from Employee e where e.id = 2", String.class);
result = query.getSingleResult();
assertEquals("Riddle", result);
因此,如圖所示,我們從fullName右側提取了子字串Riddle 。
4.4. replace()函數
同樣,JPQL 中現在也提供了replace()函數,讓我們可以取代String的部分內容:
TypedQuery<String> query = em.createQuery(
"select replace(e.fullName, 'Jade', 'Jane') from Employee e where e.id = 4", String.class
);
String result = query.getSingleResult();
assertEquals("Jane Gringer", result);
此處, replace()函數已將fullName屬性中出現的Jade替換為新的String值Jane 。
4.5. id()函數
此外,新的id()方法允許我們提取資料庫記錄的識別碼:
TypedQuery<Long> query = em.createQuery(
"select id(e) from Employee e where e.fullName = 'John Smith'", Long.class
);
Long result = query.getSingleResult();
assertEquals(1L, result);
id()函數取得與John Smith fullName相符的Employee記錄的主鍵。
4.6.改進的排序
最後,Jakarta Persistence 3.2 的排序改進使用lower()和upper():
emf.runInTransaction(em -> {
em.persist(new Employee(21L, "alice", "HR"));
em.persist(new Employee(22L, "Bob", "Engineering"));
em.persist(new Employee(23L, null, "Finance"));
em.persist(new Employee(24L, "charlie", "HR"));
});
TypedQuery<Employee> query = em.createQuery(
"SELECT e FROM Employee e ORDER BY lower(e.fullName) ASC NULLS FIRST, e.id DESC", Employee.class
);
List<Employee> sortedEmployees = query.getResultList();
assertNull(sortedEmployees.get(0).getFullName());
assertEquals("alice", sortedEmployees.get(1).getFullName());
assertEquals("Bob", sortedEmployees.get(2).getFullName());
assertEquals("charlie", sortedEmployees.get(3).getFullName());
在這個範例中,我們按照不區分大小寫的升序(使用lower()函數)按fullName對Employee記錄進行排序, null值在前,然後按id降序排列。
5. 結論
在本文中,我們討論了最新的 Jakarta Persistence 3.2,其中包含許多新功能和改進,可簡化 ORM 操作和高效的資料處理。
我們介紹的功能包括簡化的持久性配置、程式設計模式管理和事務管理。然後,我們探索了 JPQL 的可用性改進,提供了額外的函數來編寫更好的查詢。
本文的完整程式碼可在 GitHub 上取得。