文章目录
一、JPA概述
Java Persistence API:用于持久化对象的API
Java EE5.0平台标准的ORM规范,使得应用程序以统一的方式访问持久层
JPA和Hibernate的关系:JPA是hibernate的一个抽象,JDBC和JDBC驱动的关系
JPA是规范:JPA本质上就是一种ORM规范,不是ORM框架。因为JPA并未提供ORM实现,它只是制定了一些规范,提供了一些编程的API接口,但具体实现则由ORM厂商提供实现
Hibernate实现:Hibernate作为ORM框架之外,它也是一种JPA实现
从功能上来说,JPA是Hibernate功能的一个子集
JAP的优势
标准化: 提供相同的 API,这保证了基于JPA 开发的企业应用能够经过少量的修改就能够在不同的 JPA 框架下运行。
简单易用,集成方便: JPA 的主要目标之一就是提供更加简单的编程模型,在 JPA 框架下创建实体和创建 Java 类一样简单,只需要使用 javax.persistence.Entity 进行注释;JPA 的框架和接口也都非常简单
可媲美JDBC的查询能力: JPA的查询语言是面向对象的,JPA定义了独特的JPQL,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。
支持面向对象的高级特性: JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,最大限度的使用面向对象的模型
支持以下技术
ORM 映射元数据:JPA 支持 XML 和 JDK 5.0 注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中。
JPA 的 API:用来操作实体对象,执行CRUD操作,框架在后台完成所有的事情,开发者从繁琐的 JDBC和 SQL代码中解脱出来。
查询语言(JPQL):这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序和具体的 SQL 紧密耦合。
二、使用步骤
1.创建 persistence.xml
创建 persistence.xml, 在这个文件中配置持久化单元
1.需要指定跟哪个数据库进行交互;
2.需要指定 JPA 使用哪个持久化的框架以及配置该框架的基本属性
创建实体类, 使用 annotation 来描述实体类跟数据库表之间的映射关系.
使用 JPA API 完成数据增加、删除、修改和查询操作
创建 EntityManagerFactory (对应 Hibernate 中的 SessionFactory);
创建 EntityManager (对应 Hibernate 中的Session);
2.创建JPA项目
1.在 IDEA 下创建一个 JPA 项目,并实现基本的 CRUD
2.导入相应的 Maven 依赖
pom.xml
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.1.Final</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence 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://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="JPA_PRO" transaction-type="RESOURCE_LOCAL">
<!--
配置使用什么ORM 产品作为JPA 的实现
1.实际上配置的是 javax.persistence.spi.PersistenceProvider 接口的实现类
2.若JPA项目中只有一个JPA的实现产品,则也可以不配置该节点。
-->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!-- 添加持久化类-->
<class>com.ming.entity.Customer</class>
<properties>
<!-- 连接数据库的基本信息 -->
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3307/jpa?useSSL=false&serverTimezone=Asia/Shanghai"/>
<property name="hibernate.connection.driver_class" value="com.mysql.cj.jdbc.Driver"/>
<property name="hibernate.connection.username" value="liming"/>
<property name="hibernate.connection.password" value="liming"/>
<property name="hibernate.archive.autodetection" value="class"/>
<!-- 配置JPA 实现产品的基本属性,配置hibernate 的基本属性 -->
<!-- 自动显示SQL -->
<property name="hibernate.show_sql" value="true"/>
<!-- 格式化sql -->
<property name="hibernate.format_sql" value="true"/>
<!--生成数据表的策略-->
<!--注意这个属性,自动生成的文件前面没有 hibernate,要加上 hibernate -->
<property name="hibernate.hbm2ddl.auto" value="update"/>
<!-- 使用 MySQL8Dialect -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/>
</properties>
</persistence-unit>
</persistence>
public static void main(String[] args) {
//1.创建EntityManagerFactory
String persistenceUnitName = "JPA_PRO";
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName);
//2.创建EntityManager
EntityManager entityManager = entityManagerFactory.createEntityManager();
//3.开启事物
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
//4.进行持久化操作
Customer customer = new Customer();
customer.setAge(12);
customer.setEmail("565665@qq.com");
customer.setLastName("tom");
entityManager.persist(customer);
//5.提交事物
transaction.commit();
//6.关闭EntityManager
entityManager.close();
//7.关闭EntityManagerFactory
entityManagerFactory.close();
}
三、JPA基本注解
@Entity 标注用于实体类声明语句之前,指出该Java 类为实体类,将映射到指定的数据库表。如声明一个实体类 Customer,它将映射到数据库中的 customer 表上。
当实体类与其映射的数据库表名不同名时需要使用 @Table 标注说明,该标注与 @Entity 标注并列使用,置于实体类声明语句之前,可写于单独语句行,也可与声明语句同行。
@Table 标注的常用选项是 name,用于指明数据库的表名
@Table标注还有一个两个选项 catalog 和 schema 用于设置表所属的数据库目录或模式,通常为数据库名。uniqueConstraints 选项用于设置约束条件,通常不须设置。
import javax.persistence.*;
//关联数据表
@Table(name = "JAP_CUSTOMERS")
//持久化类
@Entity
public class Customer {}
@Id 标注用于声明一个实体类的属性映射为数据库的主键列。该属性通常置于属性声明语句之前,可与声明语句同行,也可写在单独行上。
@Id标注也可置于属性的getter方法之前。
//映射主键及数据列(在get方法上)
//数据列的列名( 如果相同可不写 )
@Column(name = "ID")
//生成主键的方式
@GeneratedValue(strategy= GenerationType.AUTO)
@Id
public Integer getId() {
return id;
}
@GeneratedValue 用于标注主键的生成策略,通过 strategy 属性指定。默认情况下,JPA 自动选择一个最适合底层数据库的主键生成策略:SqlServer 对应 identity,MySQL 对应 auto increment。
在 javax.persistence.GenerationType 中定义了以下几种可供选择的策略:
IDENTITY:采用数据库 ID自增长的方式来自增主键字段,Oracle 不支持这种方式;
AUTO: JPA自动选择合适的策略,是默认选项;
SEQUENCE:通过序列产生主键,通过 @SequenceGenerator 注解指定序列名,MySql 不支持这种方式
TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植。
@Column(name = "ID")
//生成主键的方式
@TableGenerator(name = "ID_GENERATOR",
table = "jpa_id_generators",
pkColumnName = "PK_NAME",
pkColumnValue = "CUSTOMER_ID",
valueColumnName = "PK_VALUE",
allocationSize = 100)
@GeneratedValue(strategy= GenerationType.TABLE,generator = "ID_GENERATOR")
@Id
public Integer getId() {
return id;
}
@Transient表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性.如果一个属性并非数据库表的字段映射,就务必将其标示为@Transient,否则,ORM框架默认其注解为@Basic
@Temporal在核心的 Java API 中并没有定义 Date 类型的精度(temporal precision). 而在数据库中,表示 Date 类型的数据有 DATE, TIME, 和 TIMESTAMP 三种精度(即单纯的日期,时间,或者两者 兼备). 在进行属性映射时可使用@Temporal注解来调整精度.
@Basic 表示一个简单的属性到数据库表的字段的映射,对于没有任何标注的 getXxxx() 方法,默认即为@Basic
fetch: 表示该属性的读取策略,有 EAGER 和 LAZY 两种,分别表示主支抓取和延迟加载,默认为 EAGER.
optional:表示该属性是否允许为null, 默认为true
四、JPA API
Persistence 类是用于获取 EntityManagerFactory 实例。该类包含一个名为 createEntityManagerFactory 的 静态方法 。
createEntityManagerFactory 方法有如下两个重载版本。
带有一个参数的方法以 JPA 配置文件 persistence.xml 中的持久化单元名为参数
带有两个参数的方法:前一个参数含义相同,后一个参数 Map类型,用于设置 JPA 的相关属性,这时将忽略其它地方设置的属性。Map 对象的属性名必须是 JPA 实现库提供商的名字空间约定的属性名。
//1.创建EntityManagerFactory
String persistenceUnitName = "JPA_PRO";
Map<String,Object> map = new HashMap<String,Object>();
map.put("hibernate.show_sql",true);//map存放的是property的属性name
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName,map);
EntityManagerFactory 接口主要用来创建 EntityManager 实例。该接口约定了如下4个方法:
createEntityManager():用于创建实体管理器对象实例。
createEntityManager(Map map):用于创建实体管理器对象实例的重载方法,Map 参数用于提供 EntityManager 的属性。
isOpen():检查 EntityManagerFactory 是否处于打开状态。实体管理器工厂创建后一直处于打开状态,除非调用close()方法将其关闭。
close():关闭 EntityManagerFactory 。 EntityManagerFactory 关闭后将释放所有资源,isOpen()方法测试将返回 false,其它方法将不能调用,否则将导致IllegalStateException异常。
EntityManager
find (Class entityClass,Object primaryKey):返回指定的 OID 对应的实体类对象,如果这个实体存在于当前的持久化环境,则返回一个被缓存的对象;否则会创建一个新的 Entity, 并加载数据库中相关信息;若 OID 不存在于数据库中,则返回一个 null。第一个参数为被查询的实体类类型,第二个参数为待查找实体的主键值。
getReference (Class entityClass,Object primaryKey):与find()方法类似,不同的是:如果缓存中不存在指定的 Entity, EntityManager 会创建一个 Entity 类的代理,但是不会立即加载数据库中的信息,只有第一次真正使用此 Entity 的属性才加载,所以如果此 OID 在数据库不存在,getReference() 不会返回 null 值, 而是抛出EntityNotFoundException
persist (Object entity):用于将新创建的 Entity 纳入到 EntityManager 的管理。该方法执行后,传入 persist() 方法的 Entity 对象转换成持久化状态。
如果传入 persist() 方法的 Entity 对象已经处于持久化状态,则 persist() 方法什么都不做。
如果对删除状态的 Entity 进行 persist() 操作,会转换为持久化状态。
如果对游离状态的实体执行 persist() 操作,可能会在 persist() 方法抛出 EntityExistException(也有可能是在flush或事务提交后抛出)
remove (Object entity):删除实例。如果实例是被管理的,即与数据库实体记录关联,则同时会删除关联的数据库记录。
package com.ming;
import com.ming.entity.Customer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class JPATest {
private EntityManagerFactory entityManagerFactory;
private EntityManager entityManager;
private EntityTransaction transaction;
@Before
public void init() {
entityManagerFactory = Persistence.createEntityManagerFactory("JPA_PRO");
entityManager = entityManagerFactory.createEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
}
@After
public void destroy() {
transaction.commit();
entityManager.close();
entityManagerFactory.close();
}
/**
* 类似于hibernate中的session的get方法
*/
@Test
public void testFind() {
Customer customer = entityManager.find(Customer.class, 6);
System.out.println(customer);
}
/**
* 相当于Hibernate中的session的load方法
*/
@Test
public void testGetReference() {
Customer customer = entityManager.getReference(Customer.class, 6);
System.out.println(customer);
}
/**
* 雷士与Hibernate的save方法.使对象由临时状态变为持久化状态
* 和hibernate的save方法不同之处:如对象由id不能执行insert操作,会抛出异常
*/
@Test
public void testPersistence() {
Customer customer = new Customer();
customer.setAge(12);
customer.setEmail("2656556565@qq.com");
customer.setLastName("xiaomi");
entityManager.persist(customer);
}
/**
* 类似于hibernate的session的delete方法。把对象对应的记录从数据库中移除
* 注意:该方法只能移除持久化对象,hibernate的delete方法实际上还可以移除游离对象
*/
@Test
public void testRemove() {
// Customer customer = new Customer();
// customer.setId(6);
Customer customer = entityManager.find(Customer.class, 6);
entityManager.remove(customer);
}
}
merge (T entity):merge() 用于处理 Entity 的同步。即数据库的插入和更新操作
/**
* 总的来说:类似于hibernate session的saveOrUpdate方法
*
* 1.若传入一个临时对象
* 会创建一个新的对象,把临时对象的属性复制到到新的对象中,然后对新的对象执行持久化操作。
* 所以新的对象中有id值,当以前的临时对象中没有id
*/
@Test
public void testMerge1(){
Customer customer = new Customer();
customer.setAge(12);
customer.setEmail("2656556565@qq.com");
customer.setLastName("xiaomi");
Customer merge2 = entityManager.merge(customer);
System.out.println(customer.getId());
System.out.println(merge2.getId());
}
/**
* 若传入的是一个游离对象,即传入的对象有OID
* 1.若在entityManager 缓存中没有该对象
* 2.若在数据库中没有对应的记录
* 3.JPA会创建一个新的对象,然后把当前游离对象的属性复制到新创建的对象中
* 4.对新创建的对象执行insert操作
*/
@Test
public void testMerge2(){
Customer customer = new Customer();
customer.setAge(12);
customer.setEmail("2656556565@qq.com");
customer.setLastName("xiaomi");
customer.setId(100);
Customer merge2 = entityManager.merge(customer);
System.out.println(customer.getId());
System.out.println(merge2.getId());
}
/**
* 若传入的是一个游离对象,即传入的对象有OID
* 1.若在entityManager 缓存中没有该对象
* 2.若在数据库中有对应的记录
* 3.JPA会查询对应的记录,然后返回该记录对应的对象,再然后会把游离对象的属复制到查询到的对象中
* 4.对查询到的对象执行update操作
*/
@Test
public void testMerge3(){
Customer customer = new Customer();
customer.setAge(12);
customer.setEmail("33@qq.com");
customer.setLastName("xiaomi3");
customer.setId(8);
Customer merge2 = entityManager.merge(customer);
System.out.println(customer==merge2);//false
}
/**
* 若传入的是一个游离对象,即传入的对象有OID
* 1.若在entityManager 缓存中有对应的对象
* 2.JPA会把游离对象的属性复制到EntityManager缓存的对象中
* 3.然后对EntityManager缓存的对象执行update
*/
@Test
public void testMerge4(){
Customer customer = new Customer();
customer.setAge(12);
customer.setEmail("344@qq.com");
customer.setLastName("xiaomi4");
customer.setId(8);
Customer customer1 = entityManager.find(Customer.class, 8);
Customer merge2 = entityManager.merge(customer);
System.out.println(customer==customer1);//false
}
flush ():同步持久上下文环境,即将持久上下文环境的所有未保存实体的状态信息保存到数据库中。
setFlushMode (FlushModeType flushMode):设置持久上下文环境的Flush模式。参数可以取2个枚举
FlushModeType.AUTO 为自动更新数据库实体
FlushModeType.COMMIT 为直到提交事务时才更新数据库记录。
getFlushMode ():获取持久上下文环境的Flush模式。返回FlushModeType类的枚举值。
五、映射关联关系
1.单向多对一
package com.ming;
import com.ming.entity.Customer;
import com.ming.entity.Order;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class JPAORMTest {
private EntityManagerFactory entityManagerFactory;
private EntityManager entityManager;
private EntityTransaction transaction;
@Before
public void init() {
entityManagerFactory = Persistence.createEntityManagerFactory("JPA_PRO");
entityManager = entityManagerFactory.createEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
}
@After
public void destroy() {
transaction.commit();
entityManager.close();
entityManagerFactory.close();
}
/**
* 保存多对一时,先保存1的一端,再保存n的一端,这样不会多出额外的update语句
*/
@Test
public void ManyToOneTest() {
Customer customer = new Customer();
customer.setAge(12);
customer.setEmail("2656556565@qq.com");
customer.setLastName("xiaoc");
Order order1 = new Order();
order1.setOrderName("c-1");
Order order2 = new Order();
order2.setOrderName("c-2");
//设置关联关系
order1.setCustomer(customer);
order2.setCustomer(customer);
//执行保存操作
entityManager.persist(customer);
entityManager.persist(order1);
entityManager.persist(order2);
}
/**
* 默认情况下使用左连接的方式获取n的一端的对象和其关联的1的一端的对象
* 改为懒加载@ManyToOne(fetch = FetchType.LAZY)修改默认的关联属性的加载策略
*/
@Test
public void ManyToOneFindTest() {
Order order = entityManager.find(Order.class, 10);
System.out.println(order.getOrderName());
System.out.println(order.getCustomer().getLastName());
}
/**
* 不能直接删除1的一端,因为有外键约束
*/
@Test
public void ManyToOneRemoveTest() {
// Order order = entityManager.find(Order.class, 10);
// entityManager.remove(order);
Customer customer = entityManager.find(Customer.class, 9);
entityManager.remove(customer);
}
@Test
public void ManyToOneUpdateTest() {
Order order = entityManager.find(Order.class, 10);
order.getCustomer().setLastName("c2");
}
}
2.单向一对多
/**
* 单向一对多:关联关系执行保存时一定会多出update语句
* 因为多的一端在插入时不会同时插入外键列
*/
@Test
public void oneToMany(){
Customer customer = new Customer();
customer.setAge(12);
customer.setEmail("adong5@qq.com");
customer.setLastName("adong");
Order order1 = new Order();
order1.setOrderName("adong-1");
Order order2 = new Order();
order2.setOrderName("adong-2");
//设置关联关系
customer.getOrders().add(order1);
customer.getOrders().add(order2);
//执行保存操作
entityManager.persist(customer);
entityManager.persist(order1);
entityManager.persist(order2);
}
/**
* 默认对关联多的一方使用懒加载的策略
*/
@Test
public void oneToManyFind(){
Customer customer = entityManager.find(Customer.class, 12);
System.out.println(customer.getLastName());
System.out.println(customer.getOrders().size());
}
/**
* 默认情况下若删除1的一端先把关联的多的一端的外键置空,然后进行删除
* @ OneToMany(fetch = FetchType.EAGER,cascade = {CascadeType.REMOVE})可以进行级联删除
*/
@Test
public void oneToManyRemove(){
Customer customer = entityManager.find(Customer.class, 12);
entityManager.remove(customer);
}
@Test
public void oneToManyUpdate(){
Customer customer = entityManager.find(Customer.class, 9);
customer.getOrders().iterator().next().setOrderName("hukaili");
}
3.双向多对一
双向一对多关系中,必须存在一个关系维护端,在 JPA 规范中,要求 many 的一方作为关系的维护端(owner side), one 的一方作为被维护端(inverse side)。
可以在 one 方指定 @OneToMany 注释并设置 mappedBy 属性,以指定它是这一关联中的被维护端,many 为维护端。
在 many 方指定 @ManyToOne 注释,并使用 @JoinColumn 指定外键名称
备注:双向多对一和双向一对多是一回事,注意两边的@JoinColumn列名是一致的
/**
* 单向一对多:关联关系执行保存时一定会多出update语句
* 因为多的一端在插入时不会同时插入外键列
*
* 若是双向1-n的关联关系,执行保存时
* 若先保存n多的一端,在保存1的一端,默认情况下回多出4条update语句
* 若先保存1的一端,则会多出n条update语句
* 建议在进行双向1-n关联关系时,使用多的一方维护关联关系,而1的一方不维护关联关系(放弃维护关联关系mappedBy,但是不能定义JoinColumn),这样会有效的减少sql
* @ OneToMany(fetch = FetchType.EAGER,cascade = {CascadeType.REMOVE},mappedBy = "customer")
*/
@Test
public void oneToMany(){
Customer customer = new Customer();
customer.setAge(12);
customer.setEmail("kaige@qq.com");
customer.setLastName("kaige");
Order order1 = new Order();
order1.setOrderName("kaige-1");
Order order2 = new Order();
order2.setOrderName("kaige-2");
//设置关联关系
customer.getOrders().add(order1);
customer.getOrders().add(order2);
order1.setCustomer(customer);
order2.setCustomer(customer);
//执行保存操作
entityManager.persist(customer);
entityManager.persist(order1);
entityManager.persist(order2);
}
4.双向一对一
基于外键的 1-1 关联关系:在双向的一对一关联中,需要在关系被维护端(inverse side)中的 @OneToOne 注释中指定 mappedBy,以指定是这一关联中的被维护端。同时需要在关系维护端(owner side)建立外键列指向关系被维护端的主键列。
/**
* 双向1-1的关联关系,建议先保存不维护关联关系的一方,既没有外键的一方,这样就不会多出Update语句
*/
@Test
public void oneToOneTest() {
Manager manager = new Manager();
manager.setMgrName("李经理");
Department department = new Department();
department.setDeptName("技术部");
//设置关联关系
manager.setDept(department);
department.setMgr(manager);
//执行保存操作
entityManager.persist(manager);
entityManager.persist(department);
}
//1. 默认情况下, 若获取不维护关联关系的一方, 则也会通过左外连接获取其关联的对象.
//可以通过 @OneToOne 的 fetch 属性来修改加载策略. 但依然会再发送 SQL 语句来初始化其关联的对象
//这说明在不维护关联关系的一方, 不建议修改 fetch 属性.
@Test
public void testOneToOneFind2(){
Manager mgr = entityManager.find(Manager.class, 1);
System.out.println(mgr.getMgrName());
System.out.println(mgr.getDept().getClass().getName());
}
//1.默认情况下, 若获取维护关联关系的一方, 则会通过左外连接获取其关联的对象.
//但可以通过 @OneToOne(fetch = FetchType.LAZY) 属性来修改加载策略.
@Test
public void testOneToOneFind(){
Department dept = entityManager.find(Department.class, 1);
System.out.println(dept.getDeptName());
System.out.println(dept.getMgr().getClass().getName());
}
5.双向多对多
在双向多对多关系中,我们必须指定一个关系维护端(owner side),可以通过 @ManyToMany 注释中指定 mappedBy 属性来标识其为关系维护端。
@Table(name="JPA_ITEMS")
@Entity
public class Item {
private Integer id;
private String itemName;
private Set<Category> categories = new HashSet<>();
@GeneratedValue
@Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name="ITEM_NAME")
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
//使用 @ManyToMany 注解来映射多对多关联关系
//使用 @JoinTable 来映射中间表
//1. name 指向中间表的名字
//2. joinColumns 映射当前类所在的表在中间表中的外键
//2.1 name 指定外键列的列名
//2.2 referencedColumnName 指定外键列关联当前表的哪一列
//3. inverseJoinColumns 映射关联的类所在中间表的外键
@JoinTable(name="ITEM_CATEGORY",
joinColumns={@JoinColumn(name="ITEM_ID", referencedColumnName="ID")},
inverseJoinColumns={@JoinColumn(name="CATEGORY_ID", referencedColumnName="ID")})
@ManyToMany
public Set<Category> getCategories() {
return categories;
}
public void setCategories(Set<Category> categories) {
this.categories = categories;
}
}
@Table(name="JPA_CATEGORIES")
@Entity
public class Category {
private Integer id;
private String categoryName;
private Set<Item> items = new HashSet<>();
@GeneratedValue
@Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name="CATEGORY_NAME")
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
@ManyToMany(mappedBy="categories")
public Set<Item> getItems() {
return items;
}
public void setItems(Set<Item> items) {
this.items = items;
}
}
//对于关联的集合对象, 默认使用懒加载的策略.
//使用维护关联关系的一方获取, 还是使用不维护关联关系的一方获取, SQL 语句相同.
@Test
public void testManyToManyFind(){
// Item item = entityManager.find(Item.class, 5);
// System.out.println(item.getItemName());
//
// System.out.println(item.getCategories().size());
Category category = entityManager.find(Category.class, 3);
System.out.println(category.getCategoryName());
System.out.println(category.getItems().size());
}
//多对多的保存
@Test
public void testManyToManyPersist(){
Item i1 = new Item();
i1.setItemName("i-1");
Item i2 = new Item();
i2.setItemName("i-2");
Category c1 = new Category();
c1.setCategoryName("C-1");
Category c2 = new Category();
c2.setCategoryName("C-2");
//设置关联关系
i1.getCategories().add(c1);
i1.getCategories().add(c2);
i2.getCategories().add(c1);
i2.getCategories().add(c2);
c1.getItems().add(i1);
c1.getItems().add(i2);
c2.getItems().add(i1);
c2.getItems().add(i2);
//执行保存
entityManager.persist(i1);
entityManager.persist(i2);
entityManager.persist(c1);
entityManager.persist(c2);
}
六、二级缓存
节点:若 JPA 实现支持二级缓存,该节点可以配置在当前的持久化单元中是否启用二级缓存,可配置如下值:
ALL:所有的实体类都被缓存
NONE:所有的实体类都不被缓存.
ENABLE_SELECTIVE:标识 @Cacheable(true) 注解的实体类将被缓存
DISABLE_SELECTIVE:缓存除标识 @Cacheable(false) 以外的所有实体类
UNSPECIFIED:默认值,JPA 产品默认值将被使用
<?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://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="jpa-1" transaction-type="RESOURCE_LOCAL">
<!--
配置使用什么 ORM 产品来作为 JPA 的实现
1. 实际上配置的是 javax.persistence.spi.PersistenceProvider 接口的实现类
2. 若 JPA 项目中只有一个 JPA 的实现产品, 则也可以不配置该节点.
-->
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<!-- 添加持久化类 -->
<class>com.atguigu.jpa.helloworld.Customer</class>
<class>com.atguigu.jpa.helloworld.Order</class>
<class>com.atguigu.jpa.helloworld.Department</class>
<class>com.atguigu.jpa.helloworld.Manager</class>
<class>com.atguigu.jpa.helloworld.Item</class>
<class>com.atguigu.jpa.helloworld.Category</class>
<!--
配置二级缓存的策略
ALL:所有的实体类都被缓存
NONE:所有的实体类都不被缓存.
ENABLE_SELECTIVE:标识 @Cacheable(true) 注解的实体类将被缓存
DISABLE_SELECTIVE:缓存除标识 @Cacheable(false) 以外的所有实体类
UNSPECIFIED:默认值,JPA 产品默认值将被使用
-->
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<properties>
<!-- 连接数据库的基本信息 -->
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="1230"/>
<!-- 配置 JPA 实现产品的基本属性. 配置 hibernate 的基本属性 -->
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<!-- 二级缓存相关 -->
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
<property name="hibernate.cache.use_query_cache" value="true"/>
</properties>
</persistence-unit>
</persistence>
七、JPQL
JPQL语言,即 Java Persistence Query Language 的简称。JPQL 是一种和 SQL 非常类似的中间性和对象化查询语言,它最终会被编译成针对不同底层数据库的 SQL 查询,从而屏蔽不同数据库的差异。
JPQL语言的语句可以是 select 语句、update 语句或delete语句,它们都通过 Query 接口封装执行
八、JPA整合Spring
三种整合方式:
LocalEntityManagerFactoryBean:适用于那些仅使用 JPA 进行数据访问的项目,该 FactoryBean 将根据JPA PersistenceProvider 自动检测配置文件进行工作,一般从“META-INF/persistence.xml”读取配置信息,这种方式最简单,但不能设置 Spring 中定义的DataSource,且不支持 Spring 管理的全局事务
从JNDI中获取:用于从 Java EE 服务器获取指定的EntityManagerFactory,这种方式在进行 Spring 事务管理时一般要使用 JTA 事务管理
LocalContainerEntityManagerFactoryBean:适用于所有环境的 FactoryBean,能全面控制 EntityManagerFactory 配置,如指定 Spring 定义的 DataSource 等等。