Appearance
JPA 概述
JPA(Java Persistence API):Java 持久化 API。是一套 Sun Java 官方制定的 ORM 标准。Hibernate 对 JPA 标准有两套支持:
- 兼容性的支持:操作的接口是 Hibernate 原来框架的,只有映射注解使用 JPA 标准接口提供。
- 完全性的支持:操作的接口和映射的注解全部使用 JPA 的标准。
Hibernate JPA 兼容性的支持(了解)
操作的接口是 Hibernate 框架提供的,只有映射注解使用 JPA 标准接口提供。操作步骤如下:
- 其他操作的类不变,删除映射文件。修改实体类加上映射的注解(与完全性支持一样)。
- 修改配置文件的加载为实体类。
xml
<!-- 加载映射文件(必须) -->
<mapping class="com.moon.entity.Customer"/>Hibernate JPA 完全性的支持
完全性的支持是指,操作的 API 和映射的注解全部使用 JPA 的标准。
配置流程说明

- 需要一个总配置文件存储框架需要的信息。注意:配置文件必须要放在
classpath:/META-INF/persistence.xml。 - 需要通过
Persistence类来获得配置文件的信息,构造EntityManagerFactory(实体管理工厂)。 - 通过实体管理工厂的对象获得操作类对象,实体管理类对象。
- 操作类操作数据库表之前,必须要配置一个和数据表关联的实体类(有关联注解的),不要忘了在总配置文件加载。
配置步骤示例(待补充测试用例)
- 导入包。除了 hibernate 核心包和 mysql 驱动包外,还需要导入 JPA 标准规则包。传统应用程序导入 hibernate-release-5.0.12.Final\lib\jpa;Maven 方式引入以下依赖:
xml
<!-- https://mvnrepository.com/artifact/jakarta.persistence/jakarta.persistence-api -->
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.x.x</version>
</dependency>- 创建一个总配置文件(persistence.xml)。注意此文件必须放在 classpath:/META-INF/persistence.xml,Eclipse 已经支持了 JPA 框架,所有不需要配置 xsd 文件,直接使用。

persistence.xml 配置示例:
xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd ">
<!-- 持久单元,作用:配置一个数据库的连接 name:就是持久单元名,是一个标识符 -->
<persistence-unit name="crm">
<!-- 加载映射实体类,不写也是默认加载,但建议写 -->
<class>com.moon.entity.Customer</class>
<!-- 四要素 -->
<properties>
<!-- 如果使用Hibernate实现的JPA,使用的就是原来Hibernate的环境参数 -->
<property name="hibernate.connection.driver_class" value="com.mysql.cj.jdbc.Driver"/>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/hibernate_db?allowPublicKeyRetrieval=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="123456"/>
<!-- 配置hibernate可选常用属性 -->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
</properties>
</persistence-unit>
</persistence>- 创建一个工具类
JPAUtils,获得操作对象EntityManager。
java
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
/**
* 获取JPA操作对象工具类
*/
public class JPAUtil {
// 获取唯一的实体管理工厂
public static EntityManagerFactory emf;
// 创建静态代码块,类加载时创建实体管理工厂
static {
// 读取配置文件,参数persistenceUnitName是配置文件中persistence-unit的name
emf = Persistence.createEntityManagerFactory("crm");
}
// 获取操作对象的静态方法
public static EntityManager getEntityManager() {
return emf.createEntityManager();
}
}- 创建一个实体类,并且配置映射的注解。
java
// 配置类与表的关系
@Entity
@Table(name = "cst_customer")
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
// 配置属性与字段的关系
// 配置ID与主键的生成策略
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cust_id")
private Long custId;
// 注意,如果属性名和字段同名,可以不配置@Column
@Column(name = "cust_name")
private String custName;
@Column(name = "cust_source")
private String custSource;
@Column(name = "cust_industry")
private String custIndustry;
@Column(name = "cust_level")
private String custLevel;
// 配置在属性上面的注解可以配置 get方法的上面(建议放在属性上面)
public Set<Linkman> getLinkmans() {
return linkmans;
}
// ...省略 getter/setter
}- 操作测试(待补充)
使用 JPA 注解来替代配置文件
JPA 注解的作用其实就是代替 XML 配置文件,将程序的元数据写在代码上。而元数据就是,启动程序必须依赖的数据。
配置文件不是编程语言的语法,所以是无法断点调试的。而注解是 Java 语法,报错的时候,可以快速的定位问题。
使用 JPA 注解的注意事项:
- 如果属性名和数据库表的字段名相同,可以不配置
@Column。 - 配置在属性上面的注解也可以配置 get 方法的上面(建议放在属性上面),并且要统一配置在属性或 get 方法。
将 Hibernate 配置的纯 JPA 的代码移植到 OpenJPA(了解)
如果代码使用纯 JPA 标准编写,不修改代码只修改一下配置文件,即可将 Hibernate 的代码移植到其他 OpenJPA 和 EclipseTOP 等 JPA 框架。
注意:OpenJPA 使用是 JPA2.0 规则,而 Hibernate 使用的 JPA2.1 规范。所以将 Hibernate 编写的 JPA 代码切换 OpenJPA 的代码,需要修改配置文件版本。配置文件中的属性名必须大写开头。
示例:修改配置文件,openJPA 可以直接使用。
xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_0.xsd ">
<!--
更改成openJPA即可,注意OpenJPA使用是JPA2.0规则
-->
<!-- 持久单元,作用:配置一个数据库的连接 -->
<persistence-unit name="crm">
<!-- 加载映射实体类,不写也是默认加载,但建议写 -->
<class>day61.entity.Customer</class>
<!-- 四要素 -->
<properties>
<!-- org.apache.openjpa.conf.OpenJPAConfigurationImpl -->
<!-- 使用openjpa实现的JPA -->
<!-- 属性必须大写开头 -->
<property name="openjpa.ConnectionDriverName" value="com.mysql.jdbc.Driver"/>
<property name="openjpa.ConnectionURL" value="jdbc:mysql://localhost:3306/day58_hibernate"/>
<property name="openjpa.ConnectionUserName" value="root"/>
<property name="openjpa.ConnectionPassword" value="123456"/>
</properties>
</persistence-unit>
</persistence>JPA 映射注解汇总
@Entity:声明该类是一个 JPA 标准的实体类。标识此注解会在创建 SessionFactory 时,加载映射配置。@Table:指定实体类关联的表。name:指定数据库表的名称。注意如果不指定表名,默认使用类名对应表名。不区别大小写。
@Column:指定实体类属性和数据库表字段之间的对应关系。可选属性如下:name:指定数据库表的列名称。如果属性和字段一致,可以省略不写unique:是否唯一nullable:是否可以为空inserttable:是否可以插入updateable:是否可以更新
@Id:声明属性是一个 OID,对应的一定是数据库的主键字段。@GenerateValue:声明 OID 的主键策略。可选属性如下:strategy:主键生成策略。可选值如下:IDENTITY:主键由数据库自动生成(主要是支持自动增长的数据库,如 mysql)TABLE:使用一个特定的数据库表格来保存主键。SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。这个值要与 generator 一起使用,generator 指定生成主键使用的生成器(可能是 orcale 中自己编写的序列)。AUTO:主键由程序控制,也是GenerationType的默认值。
generator:引用自定义生成策略,值与@GenericGenerator的name属性一致。
@GenericGenerator:自定义主键生成策略。可选属性如下:name:指定自定义的主键生成策略的名字strategy:自定义主键生成策略
@SequenceGenerate:使用 SEQUENCE 策略时,用于设置策略的参数。@TableGenerate:使用 TABLE 主键策略时,用于设置策略的参数。@JoinTable:关联查询时,表与表是多对多的关系时,指定多对多关联表中间表的参数。针对中间表的配置。name:配置中间表的名称joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段inverseJoinColumn:中间表的外键字段关联对方表的主键字段
@JoinColumn:关联查询时,表与表是一对多或者多对一的关系时,声明表关联的外键字段作为连接表的条件。必须配合关联表的注解一起使用。name:指定外键字段的名称referencedColumnName:指定引用主表的主键字段名称unique:是否唯一。默认值不唯一nullable:是否允许为空。默认值允许。insertable:是否允许插入。默认值允许。updatable:是否允许更新。默认值允许。columnDefinition:列的定义信息。
@OneToMany:关联表注解,表示对应的实体和本类是一对多的关系。targetEntityClass:指定多的多方的类的字节码mappedBy:指定从表实体类中引用主表对象的名称。cascade:指定要使用的级联操作fetch:指定是否采用延迟加载orphanRemoval:是否使用孤儿删除
@ManyToOne:关联表注解,表示对应的实体和本类是多对一的关系。targetEntityClass:指定一的一方实体类字节码cascade:指定要使用的级联操作fetch:指定是否采用延迟加载optional:关联是否可选。如果设置为false,则必须始终存在非空关系。
@ManyToMany:关联表注解,表示对应的实体和本类是多对多的关系。cascade:配置级联操作。fetch:配置是否采用延迟加载。targetEntity:配置目标的实体类。映射多对多的时候不用写。
@Transient:使用该注解标识的字段,不与数据库表字段进行映射。(注:就算属性标识@Column的注解,也不会与数据库相应名字的字段进行映射。)
主键生成策略
所谓的主键生成策略即使,自动生成 ID 列值的策略。JPA 提供了四种主键生成策略:
IDENTITY:ID 自增长策略。只能用于支持 ID 自增长的数据库,并且只能用于数字类型。
java
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private BigInteger id;SEQUENCE:序列生成策略,用于有序列的数据库,如 Oracle,并且只能用于数字类型。指定使用 SEQUENCE 策略时和 Hibernate 的 xml 一样用法。如果在 MySQL 里面使用一个表模拟序列;如果使用在 Oracle 里面直接使用序列。
java
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private BigInteger id;TABLE(不常用),不管什么数据库,都是使用一个表来模拟序列。
java
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private BigInteger id;AUTO(了解):由框架决定根据不同的数据库指定一个策略。由于结果的不固定性。基本上没有人使用。
java
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private BigInteger id;自定义主键生成策略(待补充)
TODO: 参考项目一持久化类
@Transient
核心作用
在 JPA 的 Entity 中增加不映射到数据库表的字段,可以通过 @Transient 注解实现,它会告诉 JPA 忽略该字段的持久化。
- 该字段不会出现在 JPA 生成的 SQL 中(查询 / 插入 / 更新都不会包含)
- 数据库表中不会生成对应的列,仅作为 Entity 的内存临时字段使用。
使用方法
- 加在字段上:要求 Entity 的注解策略是 FIELD(默认,或
@Access(AccessType.FIELD)) - 加在 getter 方法上:要求注解策略是 PROPERTY(
@Access(AccessType.PROPERTY))。
注意事项
- 字段的初始化:
@Transient字段不会从数据库读取值,需要自己在代码中(比如构造器、业务逻辑)手动赋值。(或通过 getter 自动计算) - 框架兼容性:如果用了 Hibernate 等实现,
@Transient是标准 JPA 注解,完全支持;若用 Lombok 的@Data,也不会影响该字段的生成。
JPA 的增删改查(待完善)
基础增删改查
java
// 获取JPA实体管理类对象
EntityManager em = JPAUtil.getEntityManager();
// 创建对象
Customer c = new Customer();
c.setCustName("撼地神牛");
em.persist(c);
// 注意:使用JPA删除数据也必须使用持久化对象
// Customer c = new Customer();
// c.setCustId(24L);;
// 删除对象,获取持久态对象
Customer c = em.find(Customer.class, 24L);
em.remove(c);
// 更新数据
Customer c = new Customer();
c.setCustId(25L);
c.setCustName("是不是更新的?");
c.setCustIndustry("xx");
em.merge(c);
// 无延迟查找数据
Customer c = em.find(Customer.class, 22L);
// 有延迟查找数据
Customer c = em.getReference(Customer.class, 22L);注意:使用 JPA 删除数据也必须使用持久化对象,否则报错 java.lang.IllegalArgumentException: Removing a detached instance day61.entity.Customer#24
JPQL 操作(删除,修改)
JPQL(Java Persistence Query Language):Java 持久化查询语言。几乎等同 HQL,一样直接操作的是对象,不是表。
注意:与 HQL 有所区别,在 JPQL 中,如果使用 ? 设置参数,必须要在 ? 后面指定下标,并且下标必须不能是负数
java
/**
* JPQL操作Query(删除,修改)
*/
public class JPA_JPQLTest {
// 1.需求:通过客户名删除客户记录
public void remove() {
// 获取JPA实体管理类对象
EntityManager em = JPAUtil.getEntityManager();
// 获取事务对象
EntityTransaction transaction = em.getTransaction();
// 开启事务
transaction.begin();
// 获取JPQL操作对象
// JPQL,如果使用?设置参数,必须要在?后面指定下标。下标必须不能是负数
Query query = em.createQuery("delete from Customer c where c.custName like ?0");
// 设置?参数值
query.setParameter(0, "%是%");
// 执行操作
int row = query.executeUpdate();
System.out.println("成功执行的行数是:" + row);
// 提交事务
transaction.commit();
// 关闭资源
em.close();
}
// 需求:将名字为"敌法师"的客户修改来源为,DOTA2
public void marge() {
// 获取JPA实体管理类对象
EntityManager em = JPAUtil.getEntityManager();
// 获取事务对象
EntityTransaction transaction = em.getTransaction();
// 开启事务
transaction.begin();
// 获取JPQL操作对象,使用命名参数
Query query = em.createQuery("update Customer c set c.custSource=:source where c.custName=:name");
// 设置命名参数值
query.setParameter("source", "DOTA2");
query.setParameter("name", "敌法师");
// 执行操作
int row = query.executeUpdate();
System.out.println("成功执行的行数是:" + row);
// 提交事务
transaction.commit();
// 关闭资源
em.close();
}
}JPQL 查询
java
/**
* JPQL操作TypedQuery(查询)
*/
public class JPA_JPQLTest2 {
// 1.通过JPQL查询所有客户的数据
@Test
public void findAll() {
// 获取JPA实体管理类对象
EntityManager em = JPAUtil.getEntityManager();
// 获取JPQL操作对象,标准JPQL建议加上select
TypedQuery<Customer> query = em.createQuery("select c from Customer c", Customer.class);
// 查询结果集合
List<Customer> resultList = query.getResultList();
// 输出结果
for (Customer customer : resultList) {
System.out.println("客户名称:" + customer.getCustName());
}
// 关闭资源
em.close();
}
// 2.条件查询。查询有“剑”的客户
@Test
public void findByCustName() {
// 获取JPA实体管理类对象
EntityManager em = JPAUtil.getEntityManager();
// 获取JPQL操作对象,标准JPQL建议加上select
TypedQuery<Customer> query = em.createQuery("select c from Customer c where c.custName like ?0", Customer.class);
// 设置参数值
query.setParameter(0, "%剑%");
// 查询结果集合
List<Customer> resultList = query.getResultList();
// 输出结果
for (Customer customer : resultList) {
System.out.println("客户名称:" + customer.getCustName());
}
// 关闭资源
em.close();
}
// 3.分页,需求,从第三条取,取四条数据
@Test
public void findByPage() {
// 获取JPA实体管理类对象
EntityManager em = JPAUtil.getEntityManager();
// 获取JPQL操作对象,标准JPQL建议加上select
TypedQuery<Customer> query = em.createQuery("select c from Customer c", Customer.class);
// 设置分页参数,索引从0开始
query.setFirstResult(2);
query.setMaxResults(4);
// 查询结果集合
List<Customer> resultList = query.getResultList();
// 输出结果
for (Customer customer : resultList) {
System.out.println("客户名称:" + customer.getCustName());
}
// 关闭资源
em.close();
}
// 返回,客户表的记录数
@Test
public void count() {
// 获取JPA实体管理类对象
EntityManager em = JPAUtil.getEntityManager();
// 获取JPQL操作对象,标准JPQL建议加上select
TypedQuery<Long> query = em.createQuery("select count(*) from Customer", Long.class);
// 查询结果集合
Long count = query.getSingleResult();
// 输出结果
System.out.println(count);
// 关闭资源
em.close();
}
// 投影查询,就是使构造方法设置指定的属性值。
@Test
public void findAll2() {
// 获取JPA实体管理类对象
EntityManager em = JPAUtil.getEntityManager();
// 获取JPQL操作对象,标准JPQL建议加上select
TypedQuery<Customer> query = em.createQuery("select new Customer(c.custName,c.custSource) from Customer c", Customer.class);
// 查询结果集合
List<Customer> resultList = query.getResultList();
// 输出结果
for (Customer c : resultList) {
System.out.println("客户名称:" + c.getCustName() + " 来源:" + c.getCustSource());
}
// 关闭资源
em.close();
}
}Criteria 查询(了解)
java
// 1.通过Criteria查询所有客户的数据
public void findAll() {
// 1.获得操作对象,entitymanager
EntityManager manager = JPAUtil.getEntityManager();
// 2.获得Criteria操作对象
CriteriaBuilder builder = manager.getCriteriaBuilder();
// 3. 指定操作的实体类,获得操作对象
CriteriaQuery<Customer> criteriaQuery = builder.createQuery(Customer.class);
// 4.获得设置查询的条件的对象
criteriaQuery.from(Customer.class);
// 5.将Criteria查询对象交个通用的查询对象
TypedQuery<Customer> query = manager.createQuery(criteriaQuery);
// 6.通过通用的查询对象查询数据
List<Customer> customers = query.getResultList();
// 7.输出
for (Customer c : customers) {
System.out.println(c.getCustName());
}
manager.close();
}JPA 多表关联查询(待整理)
多表关联查询的作用就是,实现使用一个实体类操作或者查询多个表的数据。
回顾:配置多表联系查询必须有两个步骤:
- 在实体类里面建立表与表之间的关系。
- 配置关联关系,JPA 使用注解配置。
@JoinColumn:配置外键。name:外键的字段名。referencedColumnName:外键的值来源于主表的主键,配置的是主表的主键。