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与其他持久化技术对比
| 特性 | JPA | Hibernate | MyBatis | 纯JDBC |
|---|---|---|---|---|
| 类型 | 规范 | 框架(JPA实现) | SQL映射框架 | 底层API |
| 抽象级别 | 高 | 高 | 中 | 低 |
| 学习曲线 | 中等 | 陡峭 | 平缓 | 简单 |
| 标准化程度 | 标准规范 | 遵循JPA规范 | 非标准 | 标准API |
| SQL控制 | 自动生成 | 自动生成 | 手动编写 | 完全手动 |
| 性能控制 | 中等 | 中等 | 高 | 完全控制 |
| 配置方式 | 注解/XML | 注解/XML | XML/注解 | 代码配置 |
| 可移植性 | 高 | 中 | 低 | 高 |
| 适用场景 | 企业应用 | 复杂对象模型 | SQL优化场景 | 底层控制 |
1.2 JPA架构与核心组件
JPA定义了一套完整的对象关系映射组件和API,包括:
1.2.1 核心接口
JPA的核心接口体系包括:
- EntityManagerFactory:创建EntityManager实例的工厂
- EntityManager:管理实体生命周期的核心接口
- EntityTransaction:控制事务操作
- Query与TypedQuery:执行查询操作
- Persistence:引导类,创建EntityManagerFactory
-
EntityManagerFactory:
- 创建EntityManager实例的工厂
- 线程安全,通常在应用程序启动时创建一次
- 对应一个持久化单元(persistence unit)
- 初始化成本高,应被缓存并重用
-
EntityManager:
- JPA的核心接口,管理实体的持久化操作
- 相当于Hibernate的Session
- 非线程安全,通常与事务绑定
- 提供实体的CRUD操作、查询功能和事务管理
-
EntityTransaction:
- 管理资源层事务操作
- 控制事务的开始、提交和回滚
- 仅在非JTA环境中使用
-
Query与TypedQuery:
- 执行JPQL查询和原生SQL查询
- TypedQuery提供类型安全的查询结果
-
Persistence:
- 引导类,用于获取EntityManagerFactory
- 读取persistence.xml配置文件
1.2.2 JPA实现
JPA只是一套规范,需要具体的实现才能使用。市场上主要的JPA实现包括:
| 实现 | 组织 | 特点 | 市场份额 |
|---|---|---|---|
| Hibernate | Red Hat | 最成熟、功能最丰富的JPA实现 | 高 |
| EclipseLink | Eclipse Foundation | JPA参考实现,来自TopLink | 中 |
| OpenJPA | Apache Software Foundation | 轻量级实现,扩展性好 | 低 |
| DataNucleus | DataNucleus | 支持多种数据存储类型 | 低 |
1.2.3 工作流程
JPA的典型工作流程如下:
- 配置persistence.xml定义持久化单元
- 通过Persistence类创建EntityManagerFactory
- 从EntityManagerFactory获取EntityManager
- 开始事务
- 执行持久化操作(CRUD)
- 提交或回滚事务
- 关闭EntityManager
1.3 JPA实体生命周期
JPA定义了实体对象的四种状态,描述实体在持久化过程中的不同阶段。
1.3.1 实体状态
- 新建态(New/Transient):
- 刚创建的对象,未与EntityManager关联
- 没有持久化标识符或数据库表示
- 对此对象的更改不会影响数据库
1// 新建态实体2User user = new User();3user.setName("张三");4// 此时对象不在EntityManager管理下,对其修改不会反映到数据库- 托管态(Managed):
- 与当前EntityManager上下文关联的实体
- 有持久化标识符,对象状态被EntityManager跟踪
- 对托管态实体的更改会在事务提交时同步到数据库
1EntityManager em = emf.createEntityManager();2EntityTransaction tx = em.getTransaction();3tx.begin();45// 通过persist()方法使对象进入托管态6em.persist(user);7// 或通过find()方法获取的对象直接处于托管态8User managedUser = em.find(User.class, 1L);910// 托管态对象的修改会被自动跟踪11managedUser.setEmail("zhangsan@example.com");12// 不需要显式update,修改会在事务提交时同步到数据库1314tx.commit();- 游离态(Detached):
- 曾经处于托管态,但当前不在EntityManager管理下
- 有持久化标识符,但修改不会同步到数据库
- 通过显式调用merge()方法可重新变为托管态
1// EntityManager关闭后,托管态对象变为游离态2em.close();3// 此时managedUser已经是游离态4managedUser.setPhone("13800138000");5// 这个修改不会被同步到数据库67// 重新获取EntityManager8EntityManager newEm = emf.createEntityManager();9EntityTransaction newTx = newEm.getTransaction();10newTx.begin();1112// 将游离态对象重新变为托管态13User mergedUser = newEm.merge(managedUser);14// 现在mergedUser是托管态,而managedUser仍然是游离态1516newTx.commit();17newEm.close();- 移除态(Removed):
- 被EntityManager标记为待删除的实体
- 事务提交后将从数据库中删除
1EntityManager em = emf.createEntityManager();2EntityTransaction tx = em.getTransaction();3tx.begin();45User 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()
实体生命周期状态图:
- 新建态(New/Transient) - 刚创建的对象,未与持久化上下文关联
- 托管态(Managed) - 被EntityManager管理的对象,自动跟踪变更
- 游离态(Detached) - 曾经被管理但当前不在持久化上下文中的对象
- 移除态(Removed) - 被标记为删除的对象,事务提交时将从数据库中删除
状态转换由EntityManager的方法触发,如persist()、merge()、remove()等。
1.4 环境搭建与配置
1.4.1 Maven依赖
在Maven项目中添加JPA依赖:
1<!-- JPA API -->2<dependency>3 <groupId>jakarta.persistence</groupId>4 <artifactId>jakarta.persistence-api</artifactId>5 <version>3.1.0</version>6</dependency>78<!-- Hibernate实现(或其他JPA实现) -->9<dependency>10 <groupId>org.hibernate</groupId>11 <artifactId>hibernate-core-jakarta</artifactId>12 <version>5.6.15.Final</version>13</dependency>1415<!-- 数据库驱动(以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文件:
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/persistence4 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&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
1// 创建EntityManagerFactory2EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPersistenceUnit");34// 获取EntityManager5EntityManager em = emf.createEntityManager();67try {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 // 关闭EntityManager22 em.close();23}2425// 应用关闭时关闭EntityManagerFactory26emf.close();1.4.4 主要配置选项
| 配置项 | 说明 | 示例值 |
|---|---|---|
jakarta.persistence.jdbc.driver | JDBC驱动类 | com.mysql.cj.jdbc.Driver |
jakarta.persistence.jdbc.url | 数据库连接URL | jdbc:mysql://localhost:3306/testdb |
jakarta.persistence.jdbc.user | 数据库用户名 | root |
jakarta.persistence.jdbc.password | 数据库密码 | password |
hibernate.dialect | 数据库方言 | org.hibernate.dialect.MySQL8Dialect |
hibernate.show_sql | 显示SQL | true, false |
hibernate.format_sql | 格式化SQL | true, 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 实体类定义
1import jakarta.persistence.*;2import java.time.LocalDateTime;34@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日期时间API23 @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}3940// 枚举类41public enum UserStatus {42 ACTIVE, INACTIVE, SUSPENDED43}2.1.2 主键生成策略
JPA支持多种主键生成策略:
1// 自增长(依赖数据库的自增特性)2@Id3@GeneratedValue(strategy = GenerationType.IDENTITY)4private Long id;56// 序列生成器(适用于Oracle等支持序列的数据库)7@Id8@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq")9@SequenceGenerator(name = "user_seq", sequenceName = "USER_SEQ", allocationSize = 1)10private Long id;1112// 表生成器(独立于特定数据库)13@Id14@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;1920// 自动选择(让JPA提供者选择适当的策略)21@Id22@GeneratedValue(strategy = GenerationType.AUTO)23private Long id;2425// UUID生成(需要自定义实现)26@Id27@Column(length = 36)28private String id;2930@PrePersist31protected void onCreate() {32 if (id == null) {33 id = UUID.randomUUID().toString();34 }35}2.1.3 字段映射
JPA提供了多种字段映射注解:
1@Entity2public 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 @Lob12 @Column(name = "description")13 private String description;14 15 // 大二进制对象16 @Lob17 @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 @Embedded35 private Audit audit;36}3738@Embeddable39public 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)
1// 基于外键的一对一映射2@Entity3@Table(name = "users")4public class User {5 @Id6 @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}1819@Entity20@Table(name = "user_profiles")21public class UserProfile {22 @Id23 @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)
1// 部门与员工的一对多关系2@Entity3@Table(name = "departments")4public class Department {5 @Id6 @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}2829@Entity30@Table(name = "employees")31public class Employee {32 @Id33 @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)
1// 学生和课程的多对多关系2@Entity3@Table(name = "students")4public class Student {5 @Id6 @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}3233@Entity34@Table(name = "courses")35public class Course {36 @Id37 @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)选项控制关联对象的操作传播:
1// 级联所有操作(创建、更新、删除等)2@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)34// 级联特定操作5@OneToMany(mappedBy = "parent", 6 cascade = {CascadeType.PERSIST, CascadeType.MERGE})78// 孤儿删除(当子对象不再被引用时自动删除)9@OneToMany(mappedBy = "parent", orphanRemoval = true)主要的级联选项:
- CascadeType.PERSIST:级联保存
- CascadeType.MERGE:级联更新
- CascadeType.REMOVE:级联删除
- CascadeType.REFRESH:级联刷新
- CascadeType.DETACH:级联脱管
- CascadeType.ALL:包含所有级联操作
评论