1. 引言
Jakarta Persistence(前身为 JPA)是 Java 中对象关系映射的标准 API。它使开发者能在 Java 应用中管理关系型数据,通过注解和实体类将 Java 对象映射到数据库表,简化了数据库交互操作。
本教程将探讨 Jakarta Persistence 3.2 引入的关键新特性,重点介绍配置、性能和可用性方面的改进。
2. 什么是 Jakarta Persistence 3.2?
Jakarta Persistence 3.2 是 Jakarta Persistence API 的最新版本,为 Java 应用提供了标准化的对象关系映射(ORM)方案。
此版本在查询能力、性能、可用性及现代数据库特性支持方面均有提升。
要支持 Jakarta Persistence 3.2,需在 pom.xml 中添加以下 Maven 依赖:
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.2.0</version>
</dependency>
同时需要最新支持该 API 的 Hibernate 7 版本:
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>7.0.0.Beta1</version>
</dependency>
3. 核心新特性
Jakarta Persistence 3.2 引入多项新特性,优化数据库连接处理、模式配置和事务管理。
3.1. 持久化配置
最新版 新增程序化 API,通过 PersistenceConfiguration 类获取 EntityManagerFactory 实例,替代传统的 persistence.xml 文件——这在运行时配置多变的环境中提供了更大灵活性。
为演示新特性,创建包含 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
新版本引入模式管理器 API,允许开发者程序化管理数据库模式。这简化了开发和生产环境中的数据库迁移与模式验证。
例如,可通过 API 启用模式创建:
emf.getSchemaManager().create(true);
共提供四个模式管理函数:
- ✅ *create()*:创建持久化单元关联的表
- ✅ *drop()*:删除持久化单元关联的表
- ✅ *validate()*:验证模式与实体映射是否匹配
- ✅ *truncate()*:清空实体关联表的数据
3.3. 事务内运行/调用
新增 runInTransaction() 和 callInTransaction() 方法,改进数据库事务处理,为应用管理的 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() 在事务内插入新员工记录,再用 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 的实体图允许在执行查询时急切加载属性。
新版本中实体图也实现了类型安全——确保图中引用的属性在编译时有效且存在,降低错误风险。
例如使用静态元模型 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 Data Query Language(JPQL 的子集)中常用。
当实体未指定别名时,自动默认为关联表名:
Employee employee = emf.callInTransaction(em ->
em.createQuery("from Employee where fullName = 'Tony Blair'", Employee.class).getSingleResult()
);
assertNotNull(employee);
此处未为 Employee 实体指定别名,别名默认为 this,可直接在实体上操作而无需限定字段名。
4.2. cast() 函数
新增 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);
先插入 fullName 为 "123456" 的新员工记录,再通过 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() 函数,可替换字符串部分内容:
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" 替换为 "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() 函数获取 fullName 为 "John Smith" 的员工记录的主键。
4.6. 改进的排序功能
排序增强支持 null 优先和大小写不敏感排序,使用 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());
示例中按 fullName 大小写不敏感升序排序(使用 lower() 函数),null 值优先,并按 id 降序排列员工记录。
5. 结论
本文介绍了最新的 Jakarta Persistence 3.2,它带来一系列新特性和改进,简化了 ORM 操作并提升数据处理效率。
我们涵盖了简化持久化配置、程序化模式管理、事务管理等特性,并探讨了 JPQL 的可用性改进,包括新增函数以编写更优查询。这些改进让开发者能更高效地构建健壮的数据访问层。