跳到主要内容

JPA详解

JPA(Java Persistence API)是Java EE标准中定义的ORM规范,为开发者提供了持久化对象与关系型数据库之间映射的标准方法。作为一套规范而非具体实现,JPA通过各种提供者(如Hibernate、EclipseLink、OpenJPA等)实际实现其功能,使应用程序能够以统一的方式访问不同的ORM框架。

核心价值

JPA = 标准化API + ORM映射规范 + JPQL查询语言 + 生命周期管理

  • 🔄 标准化接口:提供统一的对象关系映射API,减少特定ORM实现依赖
  • 📊 简化数据访问:通过注解和XML配置映射对象与数据库表
  • 🔍 强大查询能力:JPQL和Criteria API提供类型安全的查询构建
  • 🚀 可移植性:应用代码可在不同JPA实现间迁移

1. JPA基础与架构

1.1 JPA概念与定位

JPA(Java Persistence API)是在JDK 5.0中引入的Java EE标准规范,旨在简化和标准化Java应用中的对象关系映射(ORM)开发。它解决了Java应用程序与关系型数据库之间的阻抗不匹配问题,使开发者可以使用面向对象的方式处理关系数据。

JPA的主要目标是:

  • 定义标准化的ORM接口,减少应用程序对特定ORM实现的依赖
  • 简化数据访问层的开发,提高代码可维护性
  • 提供统一的查询语言和对象管理功能
  • 实现应用程序在不同持久化提供者之间的可移植性

1.1.1 JPA与其他持久化技术对比

特性JPAHibernateMyBatis纯JDBC
类型规范框架(JPA实现)SQL映射框架底层API
抽象级别
学习曲线中等陡峭平缓简单
标准化程度标准规范遵循JPA规范非标准标准API
SQL控制自动生成自动生成手动编写完全手动
性能控制中等中等完全控制
配置方式注解/XML注解/XMLXML/注解代码配置
可移植性
适用场景企业应用复杂对象模型SQL优化场景底层控制

1.2 JPA架构与核心组件

JPA定义了一套完整的对象关系映射组件和API,包括:

1.2.1 核心接口

JPA核心接口架构

JPA的核心接口体系包括:

  • EntityManagerFactory:创建EntityManager实例的工厂
  • EntityManager:管理实体生命周期的核心接口
  • EntityTransaction:控制事务操作
  • QueryTypedQuery:执行查询操作
  • Persistence:引导类,创建EntityManagerFactory
  1. EntityManagerFactory

    • 创建EntityManager实例的工厂
    • 线程安全,通常在应用程序启动时创建一次
    • 对应一个持久化单元(persistence unit)
    • 初始化成本高,应被缓存并重用
  2. EntityManager

    • JPA的核心接口,管理实体的持久化操作
    • 相当于Hibernate的Session
    • 非线程安全,通常与事务绑定
    • 提供实体的CRUD操作、查询功能和事务管理
  3. EntityTransaction

    • 管理资源层事务操作
    • 控制事务的开始、提交和回滚
    • 仅在非JTA环境中使用
  4. QueryTypedQuery

    • 执行JPQL查询和原生SQL查询
    • TypedQuery提供类型安全的查询结果
  5. Persistence

    • 引导类,用于获取EntityManagerFactory
    • 读取persistence.xml配置文件

1.2.2 JPA实现

JPA只是一套规范,需要具体的实现才能使用。市场上主要的JPA实现包括:

实现组织特点市场份额
HibernateRed Hat最成熟、功能最丰富的JPA实现
EclipseLinkEclipse FoundationJPA参考实现,来自TopLink
OpenJPAApache Software Foundation轻量级实现,扩展性好
DataNucleusDataNucleus支持多种数据存储类型

1.2.3 工作流程

JPA的典型工作流程如下:

  1. 配置persistence.xml定义持久化单元
  2. 通过Persistence类创建EntityManagerFactory
  3. 从EntityManagerFactory获取EntityManager
  4. 开始事务
  5. 执行持久化操作(CRUD)
  6. 提交或回滚事务
  7. 关闭EntityManager

1.3 JPA实体生命周期

JPA定义了实体对象的四种状态,描述实体在持久化过程中的不同阶段。

1.3.1 实体状态

  1. 新建态(New/Transient)
    • 刚创建的对象,未与EntityManager关联
    • 没有持久化标识符或数据库表示
    • 对此对象的更改不会影响数据库
java
1// 新建态实体
2User user = new User();
3user.setName("张三");
4// 此时对象不在EntityManager管理下,对其修改不会反映到数据库
  1. 托管态(Managed)
    • 与当前EntityManager上下文关联的实体
    • 有持久化标识符,对象状态被EntityManager跟踪
    • 对托管态实体的更改会在事务提交时同步到数据库
java
1EntityManager em = emf.createEntityManager();
2EntityTransaction tx = em.getTransaction();
3tx.begin();
4
5// 通过persist()方法使对象进入托管态
6em.persist(user);
7// 或通过find()方法获取的对象直接处于托管态
8User managedUser = em.find(User.class, 1L);
9
10// 托管态对象的修改会被自动跟踪
11managedUser.setEmail("zhangsan@example.com");
12// 不需要显式update,修改会在事务提交时同步到数据库
13
14tx.commit();
  1. 游离态(Detached)
    • 曾经处于托管态,但当前不在EntityManager管理下
    • 有持久化标识符,但修改不会同步到数据库
    • 通过显式调用merge()方法可重新变为托管态
java
1// EntityManager关闭后,托管态对象变为游离态
2em.close();
3// 此时managedUser已经是游离态
4managedUser.setPhone("13800138000");
5// 这个修改不会被同步到数据库
6
7// 重新获取EntityManager
8EntityManager newEm = emf.createEntityManager();
9EntityTransaction newTx = newEm.getTransaction();
10newTx.begin();
11
12// 将游离态对象重新变为托管态
13User mergedUser = newEm.merge(managedUser);
14// 现在mergedUser是托管态,而managedUser仍然是游离态
15
16newTx.commit();
17newEm.close();
  1. 移除态(Removed)
    • 被EntityManager标记为待删除的实体
    • 事务提交后将从数据库中删除
java
1EntityManager em = emf.createEntityManager();
2EntityTransaction tx = em.getTransaction();
3tx.begin();
4
5User user = em.find(User.class, 1L);
6// 标记对象为移除态
7em.remove(user);
8// 事务提交后,数据会从数据库中删除
9tx.commit();
10em.close();

1.3.2 状态转换

以下是对象状态转换的主要方法:

  • 新建态→托管态persist()
  • 托管态→游离态clear(), close(), detach()
  • 游离态→托管态merge()
  • 托管态→移除态remove()
  • 新建态/游离态→托管态merge()
JPA实体生命周期

实体生命周期状态图:

  1. 新建态(New/Transient) - 刚创建的对象,未与持久化上下文关联
  2. 托管态(Managed) - 被EntityManager管理的对象,自动跟踪变更
  3. 游离态(Detached) - 曾经被管理但当前不在持久化上下文中的对象
  4. 移除态(Removed) - 被标记为删除的对象,事务提交时将从数据库中删除

状态转换由EntityManager的方法触发,如persist()、merge()、remove()等。

1.4 环境搭建与配置

1.4.1 Maven依赖

在Maven项目中添加JPA依赖:

xml
1<!-- JPA API -->
2<dependency>
3 <groupId>jakarta.persistence</groupId>
4 <artifactId>jakarta.persistence-api</artifactId>
5 <version>3.1.0</version>
6</dependency>
7
8<!-- Hibernate实现(或其他JPA实现) -->
9<dependency>
10 <groupId>org.hibernate</groupId>
11 <artifactId>hibernate-core-jakarta</artifactId>
12 <version>5.6.15.Final</version>
13</dependency>
14
15<!-- 数据库驱动(以MySQL为例) -->
16<dependency>
17 <groupId>mysql</groupId>
18 <artifactId>mysql-connector-java</artifactId>
19 <version>8.0.30</version>
20</dependency>

1.4.2 配置persistence.xml

创建src/main/resources/META-INF/persistence.xml文件:

xml
1<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence
4 https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd"
5 version="3.0">
6 <persistence-unit name="myPersistenceUnit" transaction-type="RESOURCE_LOCAL">
7 <!-- JPA提供者 -->
8 <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
9
10 <!-- 实体类 -->
11 <class>com.example.entity.User</class>
12 <class>com.example.entity.Order</class>
13
14 <properties>
15 <!-- 数据库连接配置 -->
16 <property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver" />
17 <property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/testdb?useSSL=false&amp;serverTimezone=UTC" />
18 <property name="jakarta.persistence.jdbc.user" value="root" />
19 <property name="jakarta.persistence.jdbc.password" value="password" />
20
21 <!-- 特定于Hibernate的属性 -->
22 <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect" />
23 <property name="hibernate.show_sql" value="true" />
24 <property name="hibernate.format_sql" value="true" />
25
26 <!-- 自动生成数据库模式 -->
27 <property name="hibernate.hbm2ddl.auto" value="update" />
28 </properties>
29 </persistence-unit>
30</persistence>

1.4.3 创建EntityManager

java
1// 创建EntityManagerFactory
2EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPersistenceUnit");
3
4// 获取EntityManager
5EntityManager em = emf.createEntityManager();
6
7try {
8 // 使用EntityManager执行操作
9 EntityTransaction tx = em.getTransaction();
10 tx.begin();
11
12 // 执行持久化操作...
13
14 tx.commit();
15} catch (Exception e) {
16 if (em.getTransaction().isActive()) {
17 em.getTransaction().rollback();
18 }
19 e.printStackTrace();
20} finally {
21 // 关闭EntityManager
22 em.close();
23}
24
25// 应用关闭时关闭EntityManagerFactory
26emf.close();

1.4.4 主要配置选项

配置项说明示例值
jakarta.persistence.jdbc.driverJDBC驱动类com.mysql.cj.jdbc.Driver
jakarta.persistence.jdbc.url数据库连接URLjdbc:mysql://localhost:3306/testdb
jakarta.persistence.jdbc.user数据库用户名root
jakarta.persistence.jdbc.password数据库密码password
hibernate.dialect数据库方言org.hibernate.dialect.MySQL8Dialect
hibernate.show_sql显示SQLtrue, false
hibernate.format_sql格式化SQLtrue, false
hibernate.hbm2ddl.auto自动生成模式create, update, validate, none
hibernate.connection.pool_size连接池大小5, 10, 20
jakarta.persistence.schema-generation.database.action标准模式生成选项none, create, drop-and-create

2. JPA实体映射

JPA提供了丰富的注解和XML配置,用于将Java类映射到数据库表。实体映射是JPA的核心功能之一。

2.1 基本注解映射

2.1.1 实体类定义

java
1import jakarta.persistence.*;
2import java.time.LocalDateTime;
3
4@Entity // 声明这是一个实体类
5@Table(name = "users") // 映射到数据库中的表名
6public class User {
7
8 @Id // 声明主键
9 @GeneratedValue(strategy = GenerationType.IDENTITY) // 主键生成策略
10 private Long id;
11
12 @Column(name = "username", nullable = false, length = 50) // 列定义
13 private String username;
14
15 @Column(name = "email", unique = true)
16 private String email;
17
18 @Temporal(TemporalType.TIMESTAMP) // 时态类型(JPA 2.1之前的日期类型映射)
19 @Column(name = "created_at")
20 private java.util.Date legacyDate;
21
22 // JPA 2.2+支持Java 8日期时间API
23 @Column(name = "registration_date")
24 private LocalDateTime registrationDate;
25
26 @Enumerated(EnumType.STRING) // 枚举类型映射
27 @Column(name = "status")
28 private UserStatus status;
29
30 @Transient // 非持久化字段
31 private String temporaryData;
32
33 @Basic(fetch = FetchType.LAZY) // 懒加载的基本字段
34 @Column(name = "bio", length = 5000)
35 private String biography;
36
37 // 构造函数、Getter和Setter省略...
38}
39
40// 枚举类
41public enum UserStatus {
42 ACTIVE, INACTIVE, SUSPENDED
43}

2.1.2 主键生成策略

JPA支持多种主键生成策略:

java
1// 自增长(依赖数据库的自增特性)
2@Id
3@GeneratedValue(strategy = GenerationType.IDENTITY)
4private Long id;
5
6// 序列生成器(适用于Oracle等支持序列的数据库)
7@Id
8@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq")
9@SequenceGenerator(name = "user_seq", sequenceName = "USER_SEQ", allocationSize = 1)
10private Long id;
11
12// 表生成器(独立于特定数据库)
13@Id
14@GeneratedValue(strategy = GenerationType.TABLE, generator = "user_gen")
15@TableGenerator(name = "user_gen", table = "id_generator",
16 pkColumnName = "gen_name", pkColumnValue = "user_id",
17 valueColumnName = "gen_value", allocationSize = 1)
18private Long id;
19
20// 自动选择(让JPA提供者选择适当的策略)
21@Id
22@GeneratedValue(strategy = GenerationType.AUTO)
23private Long id;
24
25// UUID生成(需要自定义实现)
26@Id
27@Column(length = 36)
28private String id;
29
30@PrePersist
31protected void onCreate() {
32 if (id == null) {
33 id = UUID.randomUUID().toString();
34 }
35}

2.1.3 字段映射

JPA提供了多种字段映射注解:

java
1@Entity
2public class Product {
3 // 基本类型映射
4 @Column(name = "product_name", nullable = false, length = 100)
5 private String name;
6
7 @Column(precision = 10, scale = 2) // 数值精度
8 private BigDecimal price;
9
10 // 大文本映射
11 @Lob
12 @Column(name = "description")
13 private String description;
14
15 // 大二进制对象
16 @Lob
17 @Basic(fetch = FetchType.LAZY) // 懒加载
18 private byte[] image;
19
20 // 枚举映射(按名称)
21 @Enumerated(EnumType.STRING)
22 private ProductCategory category;
23
24 // 枚举映射(按序号)
25 @Enumerated(EnumType.ORDINAL)
26 private ProductStatus status;
27
28 // 日期时间映射(Java 8+)
29 private LocalDate releaseDate;
30 private LocalDateTime lastUpdated;
31 private Instant createdAt;
32
33 // 内嵌对象
34 @Embedded
35 private Audit audit;
36}
37
38@Embeddable
39public class Audit {
40 @Column(name = "created_by")
41 private String createdBy;
42
43 @Column(name = "created_at")
44 private LocalDateTime createdAt;
45
46 @Column(name = "updated_by")
47 private String updatedBy;
48
49 @Column(name = "updated_at")
50 private LocalDateTime updatedAt;
51}

2.2 关联关系映射

JPA支持定义实体间的多种关系类型。

2.2.1 一对一关系(@OneToOne)

java
1// 基于外键的一对一映射
2@Entity
3@Table(name = "users")
4public class User {
5 @Id
6 @GeneratedValue(strategy = GenerationType.IDENTITY)
7 private Long id;
8
9 private String username;
10
11 // 用户和用户详情的一对一关系
12 @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
13 @JoinColumn(name = "profile_id")
14 private UserProfile profile;
15
16 // Getters和Setters省略...
17}
18
19@Entity
20@Table(name = "user_profiles")
21public class UserProfile {
22 @Id
23 @GeneratedValue(strategy = GenerationType.IDENTITY)
24 private Long id;
25
26 private String biography;
27 private String avatarUrl;
28
29 // 双向关联(可选)
30 @OneToOne(mappedBy = "profile")
31 private User user;
32
33 // Getters和Setters省略...
34}

2.2.2 一对多/多对一关系(@OneToMany/@ManyToOne)

java
1// 部门与员工的一对多关系
2@Entity
3@Table(name = "departments")
4public class Department {
5 @Id
6 @GeneratedValue(strategy = GenerationType.IDENTITY)
7 private Long id;
8
9 private String name;
10
11 // 一个部门有多个员工
12 @OneToMany(mappedBy = "department", cascade = CascadeType.ALL, orphanRemoval = true)
13 private List<Employee> employees = new ArrayList<>();
14
15 // 便捷方法管理关系
16 public void addEmployee(Employee employee) {
17 employees.add(employee);
18 employee.setDepartment(this);
19 }
20
21 public void removeEmployee(Employee employee) {
22 employees.remove(employee);
23 employee.setDepartment(null);
24 }
25
26 // Getters和Setters省略...
27}
28
29@Entity
30@Table(name = "employees")
31public class Employee {
32 @Id
33 @GeneratedValue(strategy = GenerationType.IDENTITY)
34 private Long id;
35
36 private String name;
37 private String position;
38
39 // 多个员工属于一个部门
40 @ManyToOne(fetch = FetchType.LAZY)
41 @JoinColumn(name = "department_id")
42 private Department department;
43
44 // Getters和Setters省略...
45}

2.2.3 多对多关系(@ManyToMany)

java
1// 学生和课程的多对多关系
2@Entity
3@Table(name = "students")
4public class Student {
5 @Id
6 @GeneratedValue(strategy = GenerationType.IDENTITY)
7 private Long id;
8
9 private String name;
10
11 @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
12 @JoinTable(
13 name = "student_course",
14 joinColumns = @JoinColumn(name = "student_id"),
15 inverseJoinColumns = @JoinColumn(name = "course_id")
16 )
17 private Set<Course> courses = new HashSet<>();
18
19 // 便捷方法管理关系
20 public void addCourse(Course course) {
21 courses.add(course);
22 course.getStudents().add(this);
23 }
24
25 public void removeCourse(Course course) {
26 courses.remove(course);
27 course.getStudents().remove(this);
28 }
29
30 // Getters和Setters省略...
31}
32
33@Entity
34@Table(name = "courses")
35public class Course {
36 @Id
37 @GeneratedValue(strategy = GenerationType.IDENTITY)
38 private Long id;
39
40 private String name;
41 private int credits;
42
43 @ManyToMany(mappedBy = "courses")
44 private Set<Student> students = new HashSet<>();
45
46 // Getters和Setters省略...
47}

2.2.4 级联操作与孤儿删除

JPA通过级联(Cascade)选项控制关联对象的操作传播:

java
1// 级联所有操作(创建、更新、删除等)
2@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
3
4// 级联特定操作
5@OneToMany(mappedBy = "parent",
6 cascade = {CascadeType.PERSIST, CascadeType.MERGE})
7
8// 孤儿删除(当子对象不再被引用时自动删除)
9@OneToMany(mappedBy = "parent", orphanRemoval = true)

主要的级联选项:

  • CascadeType.PERSIST:级联保存
  • CascadeType.MERGE:级联更新
  • CascadeType.REMOVE:级联删除
  • CascadeType.REFRESH:级联刷新
  • CascadeType.DETACH:级联脱管
  • CascadeType.ALL:包含所有级联操作

评论