# jpa **Repository Path**: tangcco/jpa ## Basic Information - **Project Name**: jpa - **Description**: spring data jpa 与springmvc 和 springboot整合 demo01 maven项目 基本CRUD demo02 maven项目基本JPQL查询 demo03 整合springmvc 基本CRUD 和 JPQL demo04 整合springmvc 多表操作 解决json'循环引用引发的堆栈异常 demo05 整合springboot 以上demo已全部完结springboot擦拭代码为demo03和demo05的整合故没有编写笔记 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2021-01-11 - **Last Updated**: 2022-06-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README --- typora-copy-images-to: img --- # JPA 从入门到精通 ### orm思想和hibernate以及jpa的概述 > 1. orm思想 > 主要目的:操作实体类就相当于操作数据库表 > 建立两个映射关系: > 实体类和表的映射关系 >  实体类中属性和表中字段的映射关系 > 不再重点关注:具体的sql语句 > >   实现了ORM思想的框架:mybatis,hibernate > > 2. hibernate框架介绍 > Hibernate是一个开放源代码的对象关系映射框架, > 它对JDBC进行了封装, > 它将POJO与数据库表建立映射关系,是一个全自动的orm框架 > > 3. JPA规范 > JPA的全称是Java Persistence API,jpa是一套基于ORM的规范,内部是由接口和抽象类组成。 > > ![jpa与Hibernate关系](./img/01.png) ### springDataJpa的概述 > Spring Data JPA 让我们解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现,在实际的工作工程中,**推荐使用Spring Data JPA + ORM(如:hibernate)完成操作**,这样在切换不同的ORM框架时提供了极大的方便,同时也使数据库层操作 > > 更加简单,方便解耦。 > > SpringData Jpa 极大简化了数据库访问层代码。 如何简化的呢? **使用了SpringDataJpa,我们的dao层中只需要写接口**,就自动具有了增删改查、分页查询等方法。 ### Spring Data JPA 与 JPA和hibernate之间的关系 > Spring Data JPA是Spring提供的一套对JPA操作更加高级的封装,是在JPA规范下的专门用来进行数据持久化的解决方案。 > > JPA是一套规范,内部是有接口和抽象类组成的。hibernate是一套成熟的ORM框架,而且Hibernate实现了JPA规范,所以也可以称hibernate为JPA的一种实现方式。 > > ![spring-data-jpa与jpa与Hibernate三者关系](./img/02.jpg) ### [01.jpa入门案例](demo01) #### 1.1 需求介绍 本章节我们是实现的功能是数据库表基本CRUD。 #### 1.2 开发包介绍 > 由于JPA是sun公司制定的API规范,所以我们不需要导入额外的JPA相关的jar包,只需要导入JPA的提供商的jar包。我们选择Hibernate作为JPA的提供商,所以需要通过Maven导入Hibernate的相关jar包。 > > https://sourceforge.net/projects/hibernate/files/hibernate-orm/ > > ![image-20210114092109511](D:\dev\code\jpa\img\image-20210114092109511.png) > #### 1.3 搭建开发环境 创建的数据库表(存储毒鸡汤) ```sql DROP TABLE IF EXISTS `dujitang`; CREATE TABLE `dujitang` ( `id` int NOT NULL AUTO_INCREMENT, `text` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; ``` > 搭建环境的过程 >   1.创建maven工程导入坐标 >   2.需要配置jpa的核心配置文件 > 位置:配置到类路径下的一个叫做 META-INF 的文件夹下 > 命名:persistence.xml >   3.编写客户的实体类 >   4.配置实体类和表,类中属性和表中字段的映射关系 >   5.保存客户到数据库中 ##### 1.3.1 maven工程导入坐标 ```xml 15 15 5.7.0 5.4.27.Final 1.18.16 1.2.75 8.0.22 2.14.0 org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test org.hibernate hibernate-entitymanager ${hibernate.version} org.hibernate hibernate-c3p0 ${hibernate.version} mysql mysql-connector-java ${mysql-connector.version} org.apache.logging.log4j log4j-core ${log4j-core.version} org.projectlombok lombok ${lombok.version} provided com.alibaba fastjson ${fastjson.version} ``` ##### 1.3.2 需要配置jpa的核心配置文件 maven项目的resources目录下新建一个Directory ``` META-INF ``` 在resources/META-INF目录下新建一个XML配置文件 ``` persistence.xml ``` ```xml org.hibernate.jpa.HibernatePersistenceProvider ``` ##### 1.3.3 编写实体类和数据库表的映射配置 在实体类上使用JPA注解的形式配置映射关系 ```java package cn.kgc.tangcco.pojo; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; import javax.persistence.*; import java.io.Serializable; /** * @author 李昊哲 * @Description * @create 2020/12/29 14:34 */ @Setter @Getter @NoArgsConstructor @AllArgsConstructor @Accessors(chain = true) @Entity @Table(name = "dujitang") public class Dujitang implements Serializable { private static final long serialVersionUID = 2566271842043865021L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Integer id; @Column(name = "text") private String Text; public Dujitang(String text) { Text = text; } } ``` ##### 1.3.4 实现CRUD基本操作 在src/test/java 目录下新建一个Class > cn.kgc.tangcc.jpa.DujitangTest > ```java package cn.kgc.tangcc.jpa; import cn.kgc.tangcco.pojo.Dujitang; import com.alibaba.fastjson.JSON; import org.junit.jupiter.api.Test; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import java.util.HashMap; /** * @author 李昊哲 * @Description * @create 2021/1/11 12:14 * 1、加载配置文件创建工厂 * 2、通过实体管理器工厂获取实体管理器 * 3、获取事务对象并开启事务 * 4、完成CRUD操作 * 5、提交事务 * 6、释放资源 */ public class DujitangTest { @Test public void selectOne() { // 1、加载配置文件创建工厂 EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = factory.createEntityManager(); // Dujitang dujitang = manager.find(Dujitang.class, 1158); Dujitang dujitang = manager.getReference(Dujitang.class, 1158); if (dujitang != null) { System.out.printf(JSON.toJSONString(dujitang)); }else{ System.out.printf("没有查询到结果"); } manager.close(); factory.close(); } @Test public void insert() { // 1、加载配置文件创建工厂 EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = factory.createEntityManager(); // 3、获取事务对象并开启事务 EntityTransaction transaction = manager.getTransaction(); // 开启事务 transaction.begin(); // 创建实体对象 Dujitang dujitang = new Dujitang("桃李不言下自成蹊"); try { // 4、保存对象 manager.persist(dujitang); // 5、提交事务 transaction.commit(); } catch (Exception e) { e.printStackTrace(); transaction.rollback(); } finally { // 6、释放资源 manager.close(); factory.close(); } } @Test public void update() { // 1、加载配置文件创建工厂 EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = factory.createEntityManager(); // 3、获取事务对象并开启事务 EntityTransaction transaction = manager.getTransaction(); // 开启事务 transaction.begin(); // 创建实体对象 Dujitang dujitang = new Dujitang(1159,"你是我疲惫满身突然的轻松"); try { // 4、更新对象 manager.merge(dujitang); // 5、提交事务 transaction.commit(); } catch (Exception e) { e.printStackTrace(); transaction.rollback(); } finally { // 6、释放资源 manager.close(); factory.close(); } } @Test public void delete() { // 1、加载配置文件创建工厂 EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = factory.createEntityManager(); // 3、获取事务对象并开启事务 EntityTransaction transaction = manager.getTransaction(); // 开启事务 transaction.begin(); // 创建实体对象 Dujitang dujitang = new Dujitang(); dujitang.setId(1159); try { // 4、更新对象 manager.remove(manager.merge(dujitang)); // 5、提交事务 transaction.commit(); } catch (Exception e) { e.printStackTrace(); transaction.rollback(); } finally { // 6、释放资源 manager.close(); factory.close(); } } } ``` #### 1.4 JPA中的主键生成策略 > 通过annotation(注解)来映射hibernate实体的,基于annotation的hibernate主键标识为@Id, 其生成规则由@GeneratedValue设定的.这里的@id和@GeneratedValue都是JPA的标准用法。 **IDENTITY:** 主键由数据库自动生成(主要是自动增长型) mysql ```java @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; ``` **SEQUENCE**:根据底层数据库的序列来生成主键,条件是数据库支持序列。 oracle **AUTO**: 主键由程序控制 **TABLE**: 使用一个特定的数据库表格来保存主键 #### 1.5 JPA的API介绍 ##### 1.5.1 Persistence对象 > **Persistence对象**主要作用是**用于获取EntityManagerFactory对象**的 。通过调用该类的createEntityManagerFactory静态方法,根据配置文件中持久化单元名称创建EntityManagerFactory。 ```java //1.加载配置文件创建(实体管理器)工厂对象 EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJpa"); ``` ##### 1.5.2 EntityManagerFactory > EntityManagerFactory 接口主要用来创建 EntityManager 实例 ```java   //创建实体管理类   EntityManager em = factory.createEntityManager(); ``` > 由于EntityManagerFactory 是一个线程安全的对象(即多个线程访问同一个EntityManagerFactory 对象不会有线程安全问题),并且EntityManagerFactory 的创建极其浪费资源,所以在使用JPA编程时,我们可以对EntityManagerFactory 的创建进行优化,只需**要做到一个工程只存在一个EntityManagerFactory 即可。** ##### 1.5.3 EntityManager > 我们可以通过调用EntityManager的方法完成获取**事务**,以及**持久化数据库**的操作 方法说明: -  getTransaction : 获取事务对象 - persist : 保存操作 - merge : 更新操作 - remove : 删除操作 - find/getReference : 根据id查询 ##### 1.5.4 EntityTransaction > 在 JPA 规范中, EntityTransaction是完成事务操作的核心对象,对于EntityTransaction在我们的java代码中承接的功能比较简单。 - begin:开启事务 - commit:提交事务 - rollback:回滚事务 ``` jpa操作的操作步骤 1.加载配置文件创建实体管理器工厂 Persisitence:静态方法(根据持久化单元名称创建实体管理器工厂) createEntityMnagerFactory(持久化单元名称) 作用:创建实体管理器工厂 2.根据实体管理器工厂,创建实体管理器 EntityManagerFactory :获取EntityManager对象 方法:createEntityManager * 内部维护的很多的内容 内部维护了数据库信息, 维护了缓存信息 维护了所有的实体管理器对象 再创建EntityManagerFactory的过程中会根据配置创建数据库表 * EntityManagerFactory的创建过程比较浪费资源 特点:线程安全的对象 多个线程访问同一个EntityManagerFactory不会有线程安全问题 * 如何解决EntityManagerFactory的创建过程浪费资源(耗时)的问题? 思路:创建一个公共的EntityManagerFactory的对象 * 静态代码块的形式创建EntityManagerFactory 3.创建事务对象,开启事务 EntityManager对象:实体类管理器 beginTransaction : 创建事务对象 presist : 保存 merge : 更新 remove : 删除 find/getRefrence : 根据id查询 Transaction 对象 : 事务 begin:开启事务 commit:提交事务 rollback:回滚 4.增删改查操作 5.提交事务 6.释放资源 ``` #### 1.6-抽取jpaUtils工具类 ```java package cn.kgc.tangcco.uitls.jpa; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; /** * @author 李昊哲 * @Description * @create 2021/1/11 13:26 * 解决实体管理器工厂的浪费资源和耗时问题 * 通过静态代码块的形式,当程序第一次访问此工具类时,创建一个公共的实体管理器工厂对象 * * 第一次访问getEntityManager方法:经过静态代码块创建一个factory对象,再调用方法创建一个EntityManager对象 * 第二次方法getEntityManager方法:直接通过一个已经创建好的factory对象,创建EntityManager对象 */ public abstract class BaseJpaUtil { private static EntityManagerFactory entityManagerFactory; static { entityManagerFactory = Persistence.createEntityManagerFactory("kgcJPA"); } /** * 获取EntityManager对象 */ public static EntityManager getEntityManager() { return entityManagerFactory.createEntityManager(); } } ``` 测试工具类的使用 ```java package cn.kgc.tangcc.jpa; import cn.kgc.tangcco.pojo.Dujitang; import cn.kgc.tangcco.uitls.jpa.BaseJpaUtil; import com.alibaba.fastjson.JSON; import org.junit.jupiter.api.Test; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; /** * @author 李昊哲 * @Description * @create 2021/1/11 13:31 * JPA 中 find() 和 getReference() 的区别 * 在查询的时候有两个方法:find()和getReference(),这两个方法的参数以及调用方式都相同。那么这两个方法有什么不一样的呢? * find()称为 立即加载,顾名思义就是在调用的时候立即执行查询语句。 * getReference()称为 延迟加载 或者 懒加载,它在调用的时候不会立即执行查询语句,而是在使用到查询对象的时候才会执行查询语句。 *

* 具体如下: *

* 使用finde方法查询: * 查询的对象就是当前对象本身 * 在调用find方法的时候,就会执行sql语句查询数据库 * 使用getReference方法查询: * 查询到的对象是一个动态代理对象,并不是对象本身 * 在调用getReference方法的时候,不会执行sql语句查询数据库。当调用查询结果对象的时候,才会执行查询sql语句(什么时候用什么时候执行sql语句) */ public class BaseJpaUtilTest { @Test public void find() { // 1、加载配置文件创建工厂 // EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = BaseJpaUtil.getEntityManager(); Dujitang dujitang = manager.find(Dujitang.class, 1158); if (dujitang != null) { System.out.printf(JSON.toJSONString(dujitang)); } else { System.out.printf("没有查询到结果"); } manager.close(); } @Test public void reference() { // 1、加载配置文件创建工厂 // EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = BaseJpaUtil.getEntityManager(); Dujitang dujitang = manager.getReference(Dujitang.class, 1158); if (dujitang != null) { System.out.printf(JSON.toJSONString(dujitang)); } else { System.out.printf("没有查询到结果"); } manager.close(); } @Test public void insert() { // 1、加载配置文件创建工厂 // EntityManagerFactory factory = BaseJpaUtil.getEntityManagerFactory(); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = BaseJpaUtil.getEntityManager(); // 3、获取事务对象并开启事务 EntityTransaction transaction = manager.getTransaction(); // 开启事务 transaction.begin(); // 创建实体对象 Dujitang dujitang = new Dujitang("桃李不言下自成蹊"); try { // 4、保存对象 manager.persist(dujitang); // 5、提交事务 transaction.commit(); } catch (Exception e) { e.printStackTrace(); transaction.rollback(); } finally { // 6、释放资源 manager.close(); } } @Test public void update() { // 1、加载配置文件创建工厂 // EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = BaseJpaUtil.getEntityManager(); // 3、获取事务对象并开启事务 EntityTransaction transaction = manager.getTransaction(); // 开启事务 transaction.begin(); // 创建实体对象 Dujitang dujitang = new Dujitang(1159, "你是我疲惫满身突然的轻松"); try { // 4、更新对象 manager.merge(dujitang); // 5、提交事务 transaction.commit(); } catch (Exception e) { e.printStackTrace(); transaction.rollback(); } finally { // 6、释放资源 manager.close(); } } @Test public void delete() { // 1、加载配置文件创建工厂 // EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = BaseJpaUtil.getEntityManager(); // 从数据库中查询该条记录 Dujitang dujitang = manager.find(Dujitang.class, 1160); if (dujitang != null) { // 3、获取事务对象并开启事务 EntityTransaction transaction = manager.getTransaction(); // 开启事务 transaction.begin(); try { // 4、更新对象 manager.remove(dujitang); // 5、提交事务 transaction.commit(); } catch (Exception e) { e.printStackTrace(); transaction.rollback(); } finally { // 6、释放资源 manager.close(); } } else { System.out.printf("欲被删除的记录不存在"); } } } ``` #### 1.7 find方法:根据id查询 ```java /** * 根据id查询 * 使用find方法查询: * 1.查询的对象就是当前对象本身 * 2.在调用find方法的时候,就会发送sql语句查询数据库 * * 立即加载 * */ @Test public void find() { // 1、加载配置文件创建工厂 // EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = BaseJpaUtil.getEntityManager(); Dujitang dujitang = manager.find(Dujitang.class, 1158); if (dujitang != null) { System.out.printf(JSON.toJSONString(dujitang)); } else { System.out.printf("没有查询到结果"); } manager.close(); } ``` #### 1.8 延迟加载与立即加载 ```java /** * 根据id查询客户 * getReference方法 * 1.获取的对象是一个动态代理对象 * 2.调用getReference方法不会立即发送sql语句查询数据库 * * 当调用查询结果对象的时候,才会发送查询的sql语句:什么时候用,什么时候发送sql语句查询数据库 * * 延迟加载(懒加载) * * 得到的是一个动态代理对象 * * 什么时候用,什么使用才会查询 */ @Test public void reference() { // 1、加载配置文件创建工厂 // EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = BaseJpaUtil.getEntityManager(); try { Dujitang dujitang = manager.getReference(Dujitang.class, 2000); System.out.printf(JSON.toJSONString(dujitang)); }catch (Exception e){ System.out.printf("没有查询到结果"); }finally { manager.close(); } } ``` #### 1.9 根据id删除 ```java @Test public void delete() { // 1、加载配置文件创建工厂 // EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = BaseJpaUtil.getEntityManager(); // 从数据库中查询该条记录 Dujitang dujitang = manager.find(Dujitang.class, 1160); if (dujitang != null) { // 3、获取事务对象并开启事务 EntityTransaction transaction = manager.getTransaction(); // 开启事务 transaction.begin(); try { // 4、更新对象 manager.remove(dujitang); // 5、提交事务 transaction.commit(); } catch (Exception e) { e.printStackTrace(); transaction.rollback(); } finally { // 6、释放资源 manager.close(); } } else { System.out.printf("欲被删除的记录不存在"); } } ``` #### 1.10 更新操作 ```java @Test public void update() { // 1、加载配置文件创建工厂 // EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = BaseJpaUtil.getEntityManager(); // 3、获取事务对象并开启事务 EntityTransaction transaction = manager.getTransaction(); // 开启事务 transaction.begin(); // 创建实体对象 Dujitang dujitang = new Dujitang(1159, "你是我疲惫满身突然的轻松"); try { // 4、更新对象 manager.merge(dujitang); // 5、提交事务 transaction.commit(); } catch (Exception e) { e.printStackTrace(); transaction.rollback(); } finally { // 6、释放资源 manager.close(); } } ``` #### 以上部分有关Jpa的小结: ``` 完成基本CRUD案例 persist: 保存 merge: 更新 remove: 删除(注意删除的时候应当为merge状态) find/getRefrence: 根据id查询 ``` ------ ### [02.JPQL查询](demo02) #### 2.1 需求介绍 本章节我们是实现的功能是使用 JPA JPQL 数据库表基本查询。 ``` jpql查询 sql:查询的是表和表中的字段 jpql:查询的是实体类和类中的属性 * jpql和sql语句的语法相似 1.查询全部 2.分页查询 3.统计查询 4.条件查询 5.排序 ``` #### 2.2 开发包介绍 > [开发包介绍](#1.2 开发包介绍) #### 2.3 搭建开发环境 创建的数据库表(存储信息) ```sql DROP TABLE IF EXISTS `clazz`; CREATE TABLE `clazz` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '班级主键', `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '班级名称', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `student`; CREATE TABLE `student` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `uuid` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '系统唯一标识符', `id_card` varchar(18) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '中国身份证号', `first_name` varchar(15) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '曾用名同身份证姓名', `last_name` varchar(15) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '现用名 生活中的名字', `mobile` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '中国手机号码', `auth_text` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '密码 DES加密', `cid` int DEFAULT NULL COMMENT '所在班级编号', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ``` > 搭建环境的过程 >   1.创建maven工程导入坐标 >   2.需要配置jpa的核心配置文件 > 位置:配置到类路径下的一个叫做 META-INF 的文件夹下 > 命名:persistence.xml >   3.编写客户的实体类 >   4.配置实体类和表,类中属性和表中字段的映射关系 >   5.保存客户到数据库中 ##### 2.3.1 maven工程导入坐标 > [maven工程导入坐标](#1.3.1 maven工程导入坐标 ) ##### 2.3.2 需要配置jpa的核心配置文件 > [需要配置jpa的核心配置文件](#1.3.2 需要配置jpa的核心配置文件 ) ##### 2.3.3 编写实体类和数据库表的映射配置 在实体类上使用JPA注解的形式配置映射关系 ```java package cn.kgc.tangcco.pojo; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; import javax.persistence.*; import java.io.Serializable; /** * @author 李昊哲 * @Description * @create 2020/10/30 9:23 */ @NoArgsConstructor @AllArgsConstructor @Getter @Setter @Accessors(chain = true) @Table(name = "student") @Entity public class Student implements Serializable { private static final long serialVersionUID = 7938491898853912340L; /** * 学生编号 */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Integer id; /** * 学生唯一标志 */ @Column(name = "uuid") private String uuid; /** * 学生曾用名 */ @Column(name = "first_name") private String firstName; /** * 学生现用名 */ @Column(name = "last_name") private String lastName; /** * 学生身份证 */ @Column(name = "id_card") private String idCard; /** * 学生手机号 */ @Column(name = "mobile") private String mobile; /** * 密码 */ @Column(name = "auth_text") private String authText; /** * 学生所在班级编号 */ @Column(name = "cid") private Integer cid; /** * 学生所在班级信息 */ // private Clazz clazz; public Student(String uuid, String firstName, String lastName, String idCard, String mobile, String authText, Integer cid) { this.uuid = uuid; this.firstName = firstName; this.lastName = lastName; this.idCard = idCard; this.mobile = mobile; this.authText = authText; this.cid = cid; } } ``` ```java package cn.kgc.tangcco.pojo; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; import javax.persistence.*; import java.io.Serializable; /** * @author 李昊哲 * @Description * @create 2020/10/30 9:19 */ @NoArgsConstructor @AllArgsConstructor @Getter @Setter @Accessors(chain = true) @Table(name = "clazz") @Entity public class Clazz implements Serializable { private static final long serialVersionUID = -5611362264083468063L; /** * 班级编号 */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Integer id; /** * 班级名称 */ @Column(name = "name") private String name; } ``` ##### 2.3.4 jpql查询: 在src/test/java 目录下新建一个Class > cn.kgc.tangcco.jpa.JpqlTest ```java package cn.kgc.tangcco.jpa; import cn.kgc.tangcco.pojo.Clazz; import cn.kgc.tangcco.pojo.Student; import cn.kgc.tangcco.utils.jpa.BaseJpaUtil; import com.alibaba.fastjson.JSON; import org.junit.jupiter.api.Test; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.Query; import java.util.List; /** * @author 李昊哲 * @Description * @create 2021/1/11 14:35 */ public class JpqlTest { @Test public void selectAll() { // 1、加载配置文件创建工厂 EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = factory.createEntityManager(); String jpql = "from Clazz"; Query query = manager.createQuery(jpql, Clazz.class); List list = query.getResultList(); manager.close(); list.forEach(clazz -> System.out.println(JSON.toJSONString(clazz))); System.out.println("查询结束"); } @Test public void selectOrder() { // 1、加载配置文件创建工厂 EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = factory.createEntityManager(); String jpql = "from Clazz order by id desc"; Query query = manager.createQuery(jpql, Clazz.class); List list = query.getResultList(); manager.close(); list.forEach(clazz -> System.out.println(JSON.toJSONString(clazz))); System.out.println("查询结束"); } @Test public void selectCount() { // 1、加载配置文件创建工厂 EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = factory.createEntityManager(); String jpql = "count(id) from Clazz"; Query query = manager.createQuery(jpql, Clazz.class); Object count = query.getSingleResult(); manager.close(); System.out.println("count >>> " + count); System.out.println("查询结束"); } @Test public void selectPage() { // 1、加载配置文件创建工厂 EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = factory.createEntityManager(); String jpql = "from Student order by id desc"; Query query = manager.createQuery(jpql, Student.class); // 起始索引 query.setFirstResult(0); // 每页记录数 query.setMaxResults(5); List list = query.getResultList(); manager.close(); list.forEach(student -> System.out.println(JSON.toJSONString(student))); System.out.println("查询结束"); } @Test public void selectLike() { // 1、加载配置文件创建工厂 EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = factory.createEntityManager(); String jpql = "from Student where firstName like ?1 or lastName like ?2"; Query query = manager.createQuery(jpql); // 对占位符赋值 query.setParameter(1, "%孙%"); query.setParameter(2, "%孙%"); // 起始索引 query.setFirstResult(0); // 每页记录数 query.setMaxResults(10); List list = query.getResultList(); manager.close(); list.forEach(student -> System.out.println(JSON.toJSONString(student))); System.out.println("查询结束"); } } ``` #### 2.4 jpql查询: 全部 ```java @Test public void selectAll() { // 1、加载配置文件创建工厂 EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = factory.createEntityManager(); String jpql = "from Clazz"; Query query = manager.createQuery(jpql, Clazz.class); List list = query.getResultList(); manager.close(); list.forEach(clazz -> System.out.println(JSON.toJSONString(clazz))); System.out.println("查询结束"); } ``` #### 2.5 jpql查询: 倒序 ```java @Test public void selectOrder() { // 1、加载配置文件创建工厂 EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = factory.createEntityManager(); String jpql = "from Clazz order by id desc"; Query query = manager.createQuery(jpql, Clazz.class); List list = query.getResultList(); manager.close(); list.forEach(clazz -> System.out.println(JSON.toJSONString(clazz))); System.out.println("查询结束"); } ``` #### 2.6 jpql查询: 统计查询 ```java @Test public void selectCount() { // 1、加载配置文件创建工厂 EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = factory.createEntityManager(); String jpql = "count(id) from Clazz"; Query query = manager.createQuery(jpql, Clazz.class); Object count = query.getSingleResult(); manager.close(); System.out.println("count >>> " + count); System.out.println("查询结束"); } ``` #### 2.7 jpql查询: 分页查询 ```java @Test public void selectPage() { // 1、加载配置文件创建工厂 EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = factory.createEntityManager(); String jpql = "from Student order by id desc"; Query query = manager.createQuery(jpql, Student.class); // 起始索引 query.setFirstResult(0); // 每页记录数 query.setMaxResults(5); List list = query.getResultList(); manager.close(); list.forEach(student -> System.out.println(JSON.toJSONString(student))); System.out.println("查询结束"); } ``` #### 2.8 jpql查询: 模糊条件查询 ```java @Test public void selectLike() { // 1、加载配置文件创建工厂 EntityManagerFactory factory = Persistence.createEntityManagerFactory("kgcJPA"); // 2、通过实体管理器工厂获取实体管理器 EntityManager manager = factory.createEntityManager(); String jpql = "from Student where firstName like ?1 or lastName like ?2"; Query query = manager.createQuery(jpql); // 对占位符赋值 query.setParameter(1, "%孙%"); query.setParameter(2, "%孙%"); // 起始索引 query.setFirstResult(0); // 每页记录数 query.setMaxResults(10); List list = query.getResultList(); manager.close(); list.forEach(student -> System.out.println(JSON.toJSONString(student))); System.out.println("查询结束"); } ``` ### [03.springmvc +spring-data-jpa单表操作](demo03) #### 3.1 需求介绍 本章节我们是实现的功能是spring data jpa 数据库表操作。 ``` springDataJpa的入门操作 案例:基本CRUD 搭建环境 创建工程导入坐标 配置spring的配置文件(配置spring Data jpa的整合) 编写实体类,使用jpa注解配置映射关系 编写一个符合springDataJpa的dao层接口 * 只需要编写dao层接口,不需要编写dao层接口的实现类 * dao层接口规范 1.需要继承两个接口(JpaRepository,JpaSpecificationExecutor) 2.需要提供响应的泛型 ``` 基本方法 | 方法名 | 参数 | 用途 | | --------------- | ---- | -------------------------------------------------------- | | findAll() | 无 | 查询全部 | | findById(id) | id | 根据id查询 | | getOne(id) | id | 根据id查询 | | saveAndFlush(t) | 对象 | 保存或者更新(依据:传递的实体类对象中,是否包含id属性) | | deleteById(id); | id | 根据id删除 内部先执行一次查询然后再执行删除 | | count() | 无 | 统计记录数 | | existsById(id) | id | 根据id判断该条记录是否存在 | #### 3.2 开发包介绍 > [开发包介绍](#1.2 开发包介绍) #### 3.3 搭建开发环境 创建的数据库表(存储信息) ```sql DROP TABLE IF EXISTS `clazz`; CREATE TABLE `clazz` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '班级主键', `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '班级名称', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `student`; CREATE TABLE `student` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `uuid` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '系统唯一标识符', `id_card` varchar(18) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '中国身份证号', `first_name` varchar(15) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '曾用名同身份证姓名', `last_name` varchar(15) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '现用名 生活中的名字', `mobile` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '中国手机号码', `auth_text` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '密码 DES加密', `cid` int DEFAULT NULL COMMENT '所在班级编号', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ``` ##### 3.3.1 maven工程导入坐标 ```xml 4.0.0 cn.kgc.tangcco demo03 1.0-SNAPSHOT demo03 war 15 15 15 15 utf-8 utf-8 UTF-8 true true 1.9.6 0.9.5.5 3.3.0 1.4 2.8.0 3.11 3.6 1.2.4 3.9.0 1.2.75 1.1.2 30.0-jre 4.2.3 5.5.7 1.1.2 2.10.8 5.4.27.Final 5.7.0 1.2.17 1.18.16 8.0.22 4.0.1 1.7.30 1.7.30 5.3.2 javax.servlet javax.servlet-api 4.0.1 org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test junit junit 4.13.1 test com.github.binarywang java-testdata-generator ${generator.version} org.projectlombok lombok ${lombok.version} provided org.springframework spring-context ${spring.version} org.springframework spring-orm ${spring.version} org.springframework spring-aop ${spring.version} org.springframework spring-aspects ${spring.version} org.springframework spring-web ${spring.version} org.springframework spring-webmvc ${spring.version} org.springframework spring-jdbc ${spring.version} org.springframework spring-tx ${spring.version} org.springframework spring-test ${spring.version} org.aspectj aspectjrt ${aspectj.version} org.aspectj aspectjweaver ${aspectj.version} cglib cglib ${cglib.version} mysql mysql-connector-java ${mysql.version} com.mchange c3p0 ${c3p0.version} com.alibaba druid ${druid.version} commons-logging commons-logging 1.2 log4j log4j ${log4j.version} org.slf4j slf4j-api ${slf4j-api.version} org.slf4j slf4j-log4j12 ${slf4j-log4j12.version} test com.alibaba fastjson ${fastjson.version} cn.hutool hutool-all ${hutool-all.version} org.apache.commons commons-lang3 ${commons-lang3.varsion} commons-io commons-io ${commons-io.varsion} commons-fileupload commons-fileupload ${commons-fileupload.varsion} commons-net commons-net ${commons-net.varsion} com.google.inject guice ${guice.version} com.google.guava guava ${guava.version} joda-time joda-time ${joda-time.version} com.github.binarywang java-testdata-generator ${java-testdata-generator.version} org.hibernate hibernate-core ${hibernate.version} org.hibernate hibernate-entitymanager ${hibernate.version} org.hibernate hibernate-c3p0 ${hibernate.version} org.hibernate hibernate-validator 7.0.0.Final javax.validation validation-api 2.0.1.Final org.springframework.data spring-data-jpa 2.4.2 javax.el javax.el-api 3.0.0 org.glassfish javax.el 3.0.0 org.apache.maven.plugins maven-war-plugin 3.3.0 maven-clean-plugin 3.1.0 maven-resources-plugin 3.1.0 maven-install-plugin 2.5.2 maven-deploy-plugin 2.8.2 maven-compiler-plugin 3.8.1 UTF-8 ${jdk.version} ${jdk.version} org.apache.maven.plugins maven-surefire-plugin 2.22.2 true org.apache.maven.plugins maven-source-plugin 3.2.1 attach-sources jar org.apache.maven.plugins maven-surefire-plugin 2.22.2 true org.apache.tomcat.maven tomcat7-maven-plugin 2.2 8080 / UTF-8 kgc tangcco http://localhost:8080/manager/text org.eclipse.jetty jetty-maven-plugin 9.4.35.v20201120 / 8080 ``` ##### 3.3.2 spring框架的配置文件 ###### db.properties ```properties jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://47.94.130.233:3306/ssm?useUnicode=true&characterEncoding=UTF8&useSSL=true&serverTimeZone=Aisa/Shanghai jdbc.username=root jdbc.password=123456 jdbc.removeAbandoned=true jdbc.removeAbandonedTimeout=1800 jdbc.logAbandoned=true ``` ###### applicationContext.xml ```xml true none none ``` ###### springmvc-server.xml ```xml text/html;charset=UTF-8 application/json;charset=UTF-8 ``` ###### web.xml ```xml springMVC org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:spring/spring-servlet.xml 1 springMVC / CharacterEncodingFilter org.springframework.web.filter.CharacterEncodingFilter encoding utf-8 CharacterEncodingFilter / org.springframework.web.context.ContextLoaderListener contextConfigLocation classpath:spring/applicationContext.xml OpenSessionInViewFilter org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter OpenSessionInViewFilter /* ``` ##### 3.3.3 编写实体类和数据库表的映射配置 > [编写实体类和数据库表的映射配置 ](#2.3.3 编写实体类和数据库表的映射配置 ) #### 3.4 spring Data JPA入门案例 ##### 3.4.1 编写符合spring Data JPA规范的dao层接口 ```java package cn.kgc.tangcco.dao; import cn.kgc.tangcco.pojo.Clazz; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.stereotype.Repository; /** * @author 李昊哲 * @Description * @create 2021/1/12 9:26 * JpaRepository T操作实体类泛型 ID实体类中主键属性的泛型 * JpaSpecificationExecutor T操作实体类泛型 */ public interface ClazzDao extends JpaRepository , JpaSpecificationExecutor { } ``` ##### 3.4.2 编写service层接口的实现类 ```java package cn.kgc.tangcco.service.impl; import cn.kgc.tangcco.dao.ClazzDao; import cn.kgc.tangcco.model.ResponseText; import cn.kgc.tangcco.model.ResultCode; import cn.kgc.tangcco.pojo.Clazz; import cn.kgc.tangcco.service.ClazzService; import com.alibaba.fastjson.JSON; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.persistence.EntityNotFoundException; import java.util.ArrayList; import java.util.List; import java.util.Optional; /** * @author 李昊哲 * @Description * @create 2021/1/12 9:37 */ @Service public class ClazzServiceImpl implements ClazzService { @Autowired private ClazzDao clazzDao; @Override public ResponseText> queryClazzs(Clazz clazz) { List clazzList = clazzDao.findAll(); if (clazzList == null || clazzList.size() == 0) { return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), new ArrayList<>()); } return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), clazzList); } @Override public ResponseText queryClazz(Integer id) { if (id == null) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } Optional optional = clazzDao.findById(id); if (optional.isEmpty()) { return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), null); } return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), optional.get()); } @Override @Transactional(readOnly = true)//查询的只读就行 public ResponseText getClazz(Integer id) { if (id == null) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } try { Clazz clazz = clazzDao.getOne(id); if (clazz != null && !StringUtils.isBlank(clazz.getName())) { return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), clazz); } return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), null); } catch (Exception exception) { return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), null); } } @Transactional(rollbackFor = {Exception.class, RuntimeException.class}, propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false) @Override public ResponseText saveOrUpdate(Clazz clazz) { if (clazz == null || StringUtils.isEmpty(clazz.getName())) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), ResultCode.FAILED.getDesc()); } if (clazz != null && clazz.getId() != null && clazz.getId() > 0) { Optional optional = clazzDao.findById(clazz.getId()); if (optional.isEmpty()) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } else { clazz.setId(optional.get().getId()); } } clazz = clazzDao.saveAndFlush(clazz); if (clazz == null) { return new ResponseText<>(ResultCode.FAILED.getCode(), ResultCode.FAILED.getMsg(), ResultCode.FAILED.getDesc()); } return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), clazz); } @Transactional(rollbackFor = {Exception.class, RuntimeException.class}, propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false) @Override public ResponseText remove(Integer id) { if (id == null) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), ResultCode.PARAM_ERROR.getDesc()); } clazzDao.deleteById(id); return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), ResultCode.SUCCESS.getDesc()); } @Override public ResponseText count() { long count = clazzDao.count(); if (count > 0) { return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), count); } return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), 0L); } @Override public ResponseText exists(Integer id) { if (id == null) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), false); } return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), clazzDao.existsById(id)); } } ``` ##### 3.4.3 编写Handler接收请求响应操作后的数据 ```java package cn.kgc.tangcco.action; import cn.kgc.tangcco.model.ResponseText; import cn.kgc.tangcco.pojo.Clazz; import cn.kgc.tangcco.service.ClazzService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @author 李昊哲 * @Description * @create 2020/10/30 15:09 */ @RestController @RequestMapping(value = "/clazz") public class ClazzAction { @Autowired private ClazzService clazzService; @GetMapping(value = "/list") public ResponseText> queryClazzList() { return clazzService.queryClazzs(new Clazz()); } @GetMapping(value = "/findOne") public ResponseText queryClazz(Integer id) { return clazzService.queryClazz(id); } @GetMapping(value = "/getOne") public ResponseText getClazz(Integer id) { return clazzService.getClazz(id); } @PutMapping(value = "/save") public ResponseText save(Clazz clazz, BindingResult bindingResult) { return clazzService.saveOrUpdate(clazz); } @DeleteMapping(value = "/remove/{id}") public ResponseText removeClazz(@PathVariable(name = "id") Integer id) { return clazzService.remove(id); } @GetMapping(value = "/count") public ResponseText count() { return clazzService.count(); } @GetMapping(value = "/exists") public ResponseText exists(Integer id) { return clazzService.exists(id); } } ``` ##### 3.4.4 查询所有并使用PostMan测试 ```java @Override public ResponseText> queryClazzs(Clazz clazz) { List clazzList = clazzDao.findAll(); if (clazzList == null || clazzList.size() == 0) { return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(),null); } return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), clazzList); } ``` PonsMan测试 ​ 请求地址:http://localhost:8080/clazz/list ​ 请求方式:GET 返回结果: ```json { "code": "200", "data": [ { "id": 1, "name": "kjde1021" }, { "id": 2, "name": "kjde1022" }, { "id": 3, "name": "kjde1023" }, { "id": 4, "name": "kjde1024" }, { "id": 5, "name": "kjde1025" } ], "msg": "操作成功" } ``` ##### 3.4.5 根据ID立即查询PostMan测试 ```java @Override public ResponseText queryClazz(Integer id) { if (id == null) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } Optional optional = clazzDao.findById(id); if (optional.isEmpty()) { return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), null); } return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), optional.get()); } ``` PonsMan测试 ​ 请求地址:http://localhost:8080/clazz/findOne ​ 请求方式:GET ​ 请求参数: | key | value | | ---- | ----- | | id | 1 | 返回结果: ```json { "code": "200", "data": { "id": 1, "name": "kjde1021" }, "msg": "操作成功" } ``` ##### 3.4.6 根据ID懒加载查询PostMan测试 ```java @Override @Transactional(readOnly = true)//查询的只读就行 public ResponseText getClazz(Integer id) { if (id == null) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } try { Clazz clazz = clazzDao.getOne(id); if (clazz != null && !StringUtils.isBlank(clazz.getName())) { return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), clazz); } return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), null); } catch (Exception exception) { return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), null); } } ``` PonsMan测试 ​ 请求地址:http://localhost:8080/clazz/getOne ​ 请求方式:GET ​ 请求参数: | key | value | | ---- | ----- | | id | 2 | 返回结果: ```json { "code": "200", "data": { "id": 2, "name": "kjde1022" }, "msg": "操作成功" } ``` ##### 3.4.7 新增数据并使用PostMan测试 ```java @Override @Transactional(rollbackFor = {Exception.class,RuntimeException.class},propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,readOnly = false) public ResponseText saveOrUpdate(Clazz clazz) { if (clazz == null || StringUtils.isEmpty(clazz.getName())) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), ResultCode.FAILED.getDesc()); } if (clazz != null && clazz.getId() != null && clazz.getId() > 0) { Optional optional = clazzDao.findById(clazz.getId()); if (optional.isEmpty()) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } else { clazz.setId(optional.get().getId()); } clazz = clazzDao.saveAndFlush(clazz); if (clazz == null) { return new ResponseText<>(ResultCode.FAILED.getCode(), ResultCode.FAILED.getMsg(), ResultCode.FAILED.getDesc()); } return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), clazz); } ``` PonsMan测试 ​ 请求地址:http://localhost:8080/clazz/save ​ 请求方式:PUT ​ 请求参数: | key | value | | ---- | -------- | | name | kjde1030 | 返回结果: ```json { "code": "200", "data": { "id": 6, "name": "kjde1030" }, "msg": "操作成功" } ``` ##### 3.4.8 根据ID修改数据并使用PostMan测试 ```java @Override @Transactional(rollbackFor = {Exception.class,RuntimeException.class},propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,readOnly = false) public ResponseText saveOrUpdate(Clazz clazz) { if (clazz == null || StringUtils.isEmpty(clazz.getName())) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), ResultCode.FAILED.getDesc()); } if (clazz != null && clazz.getId() != null && clazz.getId() > 0) { Optional optional = clazzDao.findById(clazz.getId()); if (optional.isEmpty()) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } else { clazz.setId(optional.get().getId()); } clazz = clazzDao.saveAndFlush(clazz); if (clazz == null) { return new ResponseText<>(ResultCode.FAILED.getCode(), ResultCode.FAILED.getMsg(), ResultCode.FAILED.getDesc()); } return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), clazz); } ``` PonsMan测试 ​ 请求地址:http://localhost:8080/clazz/save ​ 请求方式:PUT ​ 请求参数: | | | | ---- | -------- | | id | 6 | | name | kade2021 | 返回结果: ```json { "code": "200", "data": { "id": 6, "name": "kjde1030" }, "msg": "操作成功" } ``` ##### 3.4.9 根据ID删除数据PostMan测试 ```java @Override @Transactional(rollbackFor = {Exception.class,RuntimeException.class},propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,readOnly = false) public ResponseText remove(Integer id) { if (id == null) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), ResultCode.PARAM_ERROR.getDesc()); } clazzDao.deleteById(id); return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), ResultCode.SUCCESS.getDesc()); } ``` PonsMan测试 ​ 请求地址:http://localhost:8080/clazz/remove/6 ​ 请求方式:DELETE 返回结果: ```json { "code": "200", "data": "请求成功", "msg": "操作成功" } ``` ##### 3.4.10 查询总记录数并使用PostMan测试 ```java @Override public ResponseText count() { long count = clazzDao.count(); if (count > 0) { return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), count); } return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), 0L); } ``` PonsMan测试 ​ 请求地址:http://localhost:8080/clazz/count ​ 请求方式:GET 返回结果: ```json { "code": "200", "data": 5, "msg": "操作成功" } ``` ##### 3.4.11 根据ID判断该条记录是否存在PostMan测试 ```java @Override public ResponseText exists(Integer id) { if (id == null) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(),false); } return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), clazzDao.existsById(id)); } ``` PonsMan测试 ​ 请求地址:http://localhost:8080/clazz/exists ​ 请求方式:GET ​ 请求参数: | key | value | | ---- | ----- | | id | 1 | 返回结果: ```json { "code": "200", "data": true, "msg": "操作成功" } ``` #### 3.5 spring Data JPA JPQL > 使用Spring Data JPA提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造查询条件,这时就可以使用@Query注解,结合JPQL的语句方式完成查询。 ``` jpql的查询方式 jpql : jpa query language (jpq查询语言) 特点:语法或关键字和sql语句类似 查询的是类和类中的属性 需要将JPQL语句配置到接口方法上 1.特有的查询:需要在dao接口上配置方法 2.在新添加的方法上,使用注解的形式配置jpql查询语句 3.注解 : @Query 4.使用原生SQL需要在@Query注解中增加nativeQuery = true 例如:@Query(value = "原生SQL语句",nativeQuery = true) ``` #### 3.6 使用jpql完成基本操作 > 注意:jpql不支持insert操作,执行update和delete操作需要在持久层接口方法增加@Modifying注解 ##### 3.6.1 编写符合spring Data JPA规范的dao层接口 ```java package cn.kgc.tangcco.dao; import cn.kgc.tangcco.pojo.Student; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import java.util.List; /** * @author 李昊哲 * @Description * @create 2021/1/12 9:26 * JpaRepository T操作实体类泛型 ID实体类中主键属性的泛型 * JpaSpecificationExecutor T操作实体类泛型 */ public interface StudentDao extends JpaRepository, JpaSpecificationExecutor { /** * 根据ID查找学生信息 * * @param id 学生id * @return 学生信息 */ @Query("from Student where id = ?1") public Student selectOne(Integer id); /** * 按照名字模糊查找 * * @param firstName 学生模糊姓名 * @return 模糊姓名查找返回的学生列表 */ @Query(value = "from Student where firstName like ?1 or lastName like ?2") public List findStudentLikeName(String firstName, String lastName); /** * 更新数据 * * @param id 更新条件 * @param firstName 曾用名 * @param lastName 现用名 * @param idCard 身份证 * @param mobile 手机号 * @param authText 密码,des加密后的字符串 * @param cid 所在班级编号 */ @Modifying @Query(value = "update Student set firstName = ?2,lastName = ?3, idCard = ?4, mobile = ?5, authText = ?6,cid = ?7 where id = ?1") public int update(Integer id, String firstName, String lastName, String idCard, String mobile, String authText, Integer cid); /** * 使用原生SQL模糊查找某班级的学生信息列表 * * @param cid 班级编号 * @param firstName 曾用名 * @param lastName 现用名 * @return 模糊查找某班级的学生信息列表 */ @Query(value = "SELECT * FROM `student` WHERE cid = ?1 AND (first_name like ?2 OR last_name like ?3)", nativeQuery = true) public List nativeQuery(Integer cid, String firstName, String lastName); } ``` ##### 3.6.2 编写service层接口的实现类 ```java package cn.kgc.tangcco.service.impl; import cn.kgc.tangcco.dao.StudentDao; import cn.kgc.tangcco.model.ResponseText; import cn.kgc.tangcco.model.ResultCode; import cn.kgc.tangcco.pojo.Clazz; import cn.kgc.tangcco.pojo.Student; import cn.kgc.tangcco.service.StudentService; import cn.kgc.tangcco.utils.cryptography.BaseCryptographyUtils; import cn.kgc.tangcco.utils.reflect.BaseReflect; import com.alibaba.fastjson.JSON; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.persistence.criteria.*; import java.util.ArrayList; import java.util.List; import java.util.Optional; /** * @author 李昊哲 * @Description * @create 2021/1/12 14:44 */ @Service public class StudentServiceImpl implements StudentService { private final static String key = "lihaozhelihaozhelihaozhelihaozhelihaozhelihaozhelihaozhelihaozhe"; @Autowired private StudentDao studentDao; @Override public ResponseText findOne(Integer id) { if (id == null) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } Optional optional = studentDao.findById(id); if (optional.isEmpty()) { return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), null); } Student student = optional.get(); student.setAuthText(BaseCryptographyUtils.desDecodeHexString(student.getAuthText(), key)); return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), student); } @Override public ResponseText getOne(Integer id) { if (id == null) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } try { Student student = studentDao.getOne(id); student.setAuthText(BaseCryptographyUtils.desDecodeHexString(student.getAuthText(), key)); return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), student); }catch (Exception exception){ return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), null); } } @Override public ResponseText selectOne(Integer id) { if (id == null) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } Student student = studentDao.selectOne(id); if (student == null) { return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), null); } student.setAuthText(BaseCryptographyUtils.desDecodeHexString(student.getAuthText(), key)); return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), student); } @Override public ResponseText> findLike(String firstName) { if (StringUtils.isEmpty(firstName)) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), new ArrayList<>()); } StringBuilder param = new StringBuilder(); param.append("%"); param.append(firstName); param.append("%"); List students = studentDao.findStudentLikeName(param.toString(), param.toString()); if (students != null && students.size() > 0) { return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), students); } return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), new ArrayList<>()); } @Transactional(rollbackFor = {Exception.class, RuntimeException.class}, propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false) @Override public ResponseText update(Student student) { if (student == null || student.getId() == null) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } if (!studentDao.existsById(student.getId())) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } String authText = BaseCryptographyUtils.desEncodeHexString(student.getAuthText(), key); int status = studentDao.update(student.getId(), student.getFirstName(), student.getLastName(), student.getIdCard(), student.getMobile(), authText, student.getCid()); // System.out.println(1 / 0); if (status > 0) { return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), student); } return new ResponseText<>(ResultCode.FAILED.getCode(), ResultCode.FAILED.getMsg(), student); } @Override public ResponseText> nativeQuery(Student student) { if (student == null || student.getCid() == null || StringUtils.isBlank(student.getFirstName())) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } StringBuilder nickname = new StringBuilder(); nickname.append("%"); nickname.append(student.getFirstName()); nickname.append("%"); List students = studentDao.nativeQuery(student.getCid(), nickname.toString(), nickname.toString()); if (students != null && students.size() > 0) { return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), students); } return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), new ArrayList<>()); } } ``` ##### 3.6.3 编写Handler接收请求响应操作后的数据 ```java package cn.kgc.tangcco.action; import cn.kgc.tangcco.model.ResponseText; import cn.kgc.tangcco.pojo.Clazz; import cn.kgc.tangcco.pojo.Student; import cn.kgc.tangcco.service.StudentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @author 李昊哲 * @Description * @create 2021/1/12 14:48 */ @RestController @RequestMapping(value = "/student") public class StudentAction { @Autowired private StudentService studentService; @GetMapping(value = "/findOne") public ResponseText findOne(Integer id) { return studentService.findOne(id); } @GetMapping(value = "/getOne") public ResponseText getOne(Integer id) { return studentService.getOne(id); } @GetMapping(value = "/selectOne") public ResponseText selectOne(Integer id) { return studentService.getOne(id); } @GetMapping(value = "/findLike") public ResponseText> findLike(String firstName) { return studentService.findLike(firstName); } @PutMapping(value = "/update") public ResponseText update(Student student) { return studentService.update(student); } @PostMapping(value = "/nativeQuery") public ResponseText> nativeQuery(Student student) { return studentService.nativeQuery(student); } } ``` ##### 3.6.4 根据ID查询PostMan测试 > findById(id)与getOne(id)略 > > 注意getOne(id)使用异常判断返回结果 dao层接口 ```java /** * 根据ID查找学生信息 * * @param id 学生id * @return 学生信息 */ @Query("from Student where id = ?1") public Student selectOne(Integer id); ``` service层实现类 ```java @Override public ResponseText selectOne(Integer id) { if (id == null) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } Student student = studentDao.selectOne(id); if (student == null) { return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), null); } student.setAuthText(BaseCryptographyUtils.desDecodeHexString(student.getAuthText(),key)); return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), student); } ``` PonsMan测试 ​ 请求地址:http://localhost:8080/clazz/selectOne ​ 请求方式:GET ​ 请求参数: | key | value | | ---- | ----- | | id | 1 | 返回结果: ```json { "code": "200", "data": { "authText": "123456", "cid": 2, "firstName": "夏侯勺", "id": 1, "idCard": "238108199212286379", "lastName": "夏侯勺", "mobile": "13712323067", "uuid": "f3b4a8b465dc4ad483c1ca54d671b163" }, "msg": "操作成功" } ``` ##### 3.6.5 多占位符模糊查询并使用PostMan测试 dao层接口 ```java /** * 按照名字模糊查找 * * @param firstName 学生模糊姓名 * @return 模糊姓名查找返回的学生列表 */ @Query(value = "from Student where firstName like ?1 or lastName like ?2") public List findStudentLikeName(String firstName, String lastName); ``` service层实现类 ```java @Override public ResponseText> findLike(String firstName) { if (StringUtils.isEmpty(firstName)) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } StringBuilder param = new StringBuilder(); param.append("%"); param.append(firstName); param.append("%"); List students = studentDao.findStudentLikeName(param.toString(), param.toString()); if (students != null && students.size() > 0) { return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), students); } return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), null); } ``` PonsMan测试 ​ 请求地址:http://localhost:8080/student/findLike ​ 请求方式:GET ​ 请求参数: | name | value | | --------- | ----- | | firstName | 孙 | 返回结果: ```json { "code": "200", "data": [ { "authText": "1f4a79344c3e1f58", "cid": 1, "firstName": "公孙憎", "id": 12, "idCard": "448456198211048370", "lastName": "公孙憎", "mobile": "13720463226", "uuid": "c4da87838fa74d1daa8286f8f4b0e38a" }, { "authText": "1f4a79344c3e1f58", "cid": 2, "firstName": "孙盯", "id": 61, "idCard": "542406198509154162", "lastName": "孙盯", "mobile": "15862199605", "uuid": "f1a7304c1be747b39b1535130fd717eb" }, { "authText": "1f4a79344c3e1f58", "cid": 3, "firstName": "孙章", "id": 102, "idCard": "646615197302024483", "lastName": "孙章", "mobile": "15573942169", "uuid": "09a6e376e25745afa9e372be1606ee59" }, { "authText": "1f4a79344c3e1f58", "cid": 3, "firstName": "公孙萝倘", "id": 110, "idCard": "454080199509165300", "lastName": "公孙萝倘", "mobile": "13100349323", "uuid": "334b107f91f3490a89dffa85ae8cc09f" }, { "authText": "1f4a79344c3e1f58", "cid": 5, "firstName": "公孙绚吃", "id": 114, "idCard": "71350119840129870X", "lastName": "公孙绚吃", "mobile": "17642217181", "uuid": "f36a08fbfbc74976bafd186ece1fe909" }, { "authText": "1f4a79344c3e1f58", "cid": 1, "firstName": "公孙侯锦", "id": 118, "idCard": "411287198403086218", "lastName": "公孙侯锦", "mobile": "13721248393", "uuid": "7e31bb44ae8e4d918f4542f5e5139598" }, { "authText": "1f4a79344c3e1f58", "cid": 4, "firstName": "孙捶", "id": 128, "idCard": "339375198205213949", "lastName": "孙捶", "mobile": "13026338019", "uuid": "4ae0d4d1d0ba4b5ea1ed226108a54535" }, { "authText": "1f4a79344c3e1f58", "cid": 5, "firstName": "公孙婴", "id": 130, "idCard": "617514199408240160", "lastName": "公孙婴", "mobile": "13449740380", "uuid": "5f5525b2e5184628836ef9942e321e82" } ], "msg": "操作成功" } ``` ##### 3.6.6 更新数据并使用PostMan测试 dao层接口 ```java /** * 更新数据 * * @param id 更新条件 * @param firstName 曾用名 * @param lastName 现用名 * @param idCard 身份证 * @param mobile 手机号 * @param authText 密码,des加密后的字符串 * @param cid 所在班级编号 */ @Modifying @Query(value = "update Student set firstName = ?2,lastName = ?3, idCard = ?4, mobile = ?5, authText = ?6,cid = ?7 where id = ?1") public void update(Integer id, String firstName, String lastName, String idCard, String mobile, String authText, Integer cid); ``` service层实现类 ```java @Override @Transactional(rollbackFor = {Exception.class,RuntimeException.class},propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,readOnly = false) public ResponseText update(Student student) { if (student == null || student.getId() == null) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } if (!studentDao.existsById(student.getId())) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } String authText = BaseCryptographyUtils.desEncodeHexString(student.getAuthText(), key); studentDao.update(student.getId(), student.getFirstName(), student.getLastName(), student.getIdCard(), student.getMobile(), authText, student.getCid()); // System.out.println(1 / 0); if (status > 0){ return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), student); } return new ResponseText<>(ResultCode.FAILED.getCode(), ResultCode.FAILED.getMsg(), student); } ``` PonsMan测试 ​ 请求地址:http://localhost:8080/student/update ​ 请求方式:PUT ​ 请求参数: | key | value | | --------- | ------------------ | | id | 1 | | firstName | 王柔 | | lastName | 王柔 | | idCard | 653863198001212849 | | mobile | 17004762790 | | authText | 654321 | | cid | 5 | 返回结果: ```json { "code": "200", "data": { "authText": "654321", "cid": 5, "firstName": "王柔", "id": 1, "idCard": "653863198001212849", "lastName": "王柔", "mobile": "17004762790", "uuid": "f3b4a8b465dc4ad483c1ca54d671b163" }, "msg": "操作成功" } ``` ##### 3.6.7 jpql使用原生SQL语句 dao层接口 ```java /** * 使用原生SQL模糊查找某班级的学生信息列表 * * @param cid 班级编号 * @param firstName 曾用名 * @param lastName 现用名 * @return 模糊查找某班级的学生信息列表 */ @Query(value = "SELECT * FROM `student` WHERE cid = ?1 AND (first_name like ?2 OR last_name like ?3)", nativeQuery = true) public List nativeQuery(Integer cid, String firstName, String lastName); ``` service层实现类 ```java @Override public ResponseText> nativeQuery(Student student) { if (student == null || student.getCid() == null || StringUtils.isBlank(student.getFirstName())) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } StringBuilder nickname = new StringBuilder(); nickname.append("%"); nickname.append(student.getFirstName()); nickname.append("%"); List students = studentDao.nativeQuery(student.getCid(), nickname.toString(), nickname.toString()); if (students != null && students.size() > 0) { return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), students); } return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), new ArrayList<>()); } ``` PonsMan测试 ​ 请求地址:http://localhost:8080/student/nativeQuery ​ 请求方式:POST ​ 请求参数: | key | value | | --------- | ----- | | cid | 3 | | firstName | 孙 | 返回结果: ```json { "code": "200", "data": [ { "authText": "1f4a79344c3e1f58", "cid": 3, "firstName": "孙章", "id": 102, "idCard": "646615197302024483", "lastName": "孙章", "mobile": "15573942169", "uuid": "09a6e376e25745afa9e372be1606ee59" }, { "authText": "1f4a79344c3e1f58", "cid": 3, "firstName": "公孙萝倘", "id": 110, "idCard": "454080199509165300", "lastName": "公孙萝倘", "mobile": "13100349323", "uuid": "334b107f91f3490a89dffa85ae8cc09f" } ], "msg": "操作成功" } ``` #### 3.7 spring data jpa Specification动态查询 ```java public interface JpaSpecificationExecutor { // 按照指定条件查询单个结果 Optional findOne(@Nullable Specification var1); // 按照查询所有符合条件的结果 List findAll(@Nullable Specification var1); // 按照指定条件分页查询 Page findAll(@Nullable Specification var1, Pageable var2); // 按照查询所有符合条件的结果 并安装指定条件排序 List findAll(@Nullable Specification var1, Sort var2); // 统计分析 long count(@Nullable Specification var1); } ``` > Specification 查询条件对象 ##### 3.7.1 编写符合spring Data JPA规范的dao层接口 ```java package cn.kgc.tangcco.dao; import cn.kgc.tangcco.pojo.Student; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import java.util.List; /** * @author 李昊哲 * @Description * @create 2021/1/12 9:26 * JpaRepository T操作实体类泛型 ID实体类中主键属性的泛型 * JpaSpecificationExecutor T操作实体类泛型 */ public interface StudentDao extends JpaRepository, JpaSpecificationExecutor {} ``` ##### 3.7.2 编写service层接口的实现类 ```java package cn.kgc.tangcco.service.impl; import cn.kgc.tangcco.dao.StudentDao; import cn.kgc.tangcco.model.PageParam; import cn.kgc.tangcco.model.ResponseText; import cn.kgc.tangcco.model.ResultCode; import cn.kgc.tangcco.pojo.Clazz; import cn.kgc.tangcco.pojo.Student; import cn.kgc.tangcco.service.StudentService; import cn.kgc.tangcco.utils.cryptography.BaseCryptographyUtils; import cn.kgc.tangcco.utils.reflect.BaseReflect; import com.alibaba.fastjson.JSON; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.persistence.criteria.*; import java.util.ArrayList; import java.util.List; import java.util.Optional; /** * @author 李昊哲 * @Description * @create 2021/1/12 14:44 */ @Service public class StudentServiceImpl implements StudentService { private final static String key = "lihaozhelihaozhelihaozhelihaozhelihaozhelihaozhelihaozhelihaozhe"; @Autowired private StudentDao studentDao; @Override public ResponseText loginByMobileAndAuthText(Student student) { if (student == null || StringUtils.isBlank(student.getMobile()) || StringUtils.isBlank(student.getAuthText())) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } student.setAuthText(BaseCryptographyUtils.desEncodeHexString(student.getAuthText(), key)); try { return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), studentDao.findOne((root, cq, cb) -> { return cb.and(cb.equal(root.get("mobile"), student.getMobile()), cb.equal(root.get("authText"), student.getAuthText())); }).get()); } catch (Exception exception) { return new ResponseText<>(ResultCode.LOGIN_FAILED.getCode(), ResultCode.LOGIN_FAILED.getMsg(), null); } } @Override public ResponseText> specificationLike(String firstName) { if (StringUtils.isBlank(firstName)) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), new ArrayList<>()); } List students = studentDao.findAll((root, cq, cb) -> { StringBuilder nickname = new StringBuilder(); nickname.append("%"); nickname.append(firstName); nickname.append("%"); return cb.or(cb.like(root.get("firstName").as(String.class), nickname.toString()), cb.like(root.get("lastName").as(String.class), nickname.toString())); }, Sort.by(Sort.Direction.DESC, "cid")); if (students != null && students.size() > 0) { return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), students); } return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), new ArrayList<>()); } @Override public Page page(Student student, PageParam pageParam) { return studentDao.findAll((root, cq, cb) -> { Predicate cid = null; Predicate nickName = null; if (student.getCid() != null && student.getCid() > 0) { cid = cb.equal(root.get("cid"), student.getCid()); } if (!StringUtils.isBlank(student.getFirstName())) { StringBuilder nickname = new StringBuilder(); nickname.append("%"); nickname.append(student.getFirstName()); nickname.append("%"); nickName = cb.or(cb.like(root.get("firstName").as(String.class), nickname.toString()), cb.like(root.get("lastName").as(String.class), nickname.toString())); } if (cid != null && nickName != null) { return cb.and(cid, nickName); } else if (cid != null && nickName == null) { return cid; } else if (cid == null && nickName != null) { return nickName; } else { return null; } }, PageRequest.of(pageParam.getPageNum() - 1, pageParam.getPageSize(), Sort.by(Sort.Direction.DESC, "id"))); } } ``` ##### 3.7.3 编写Handler接收请求响应操作后的数据 ```java package cn.kgc.tangcco.action; import cn.kgc.tangcco.model.PageParam; import cn.kgc.tangcco.model.ResponseText; import cn.kgc.tangcco.pojo.Clazz; import cn.kgc.tangcco.pojo.Student; import cn.kgc.tangcco.service.StudentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @author 李昊哲 * @Description * @create 2021/1/12 14:48 */ @RestController @RequestMapping(value = "/student") public class StudentAction { @Autowired private StudentService studentService; @PostMapping(value = "/loginByMobileAndAuthText") public ResponseText loginByMobileAndAuthText(Student student) { return studentService.loginByMobileAndAuthText(student); } @GetMapping(value = "/specificationLike") public ResponseText> specificationLike(String firstName) { return studentService.specificationLike(firstName); } @PostMapping(value = "/page") public Page page(Student student, @RequestParam(value = "page", required = false, defaultValue = "1") Integer page, @RequestParam(value = "limit", required = false, defaultValue = "5") Integer limit) { return studentService.page(student, new PageParam(page, limit)); } } ``` ##### 3.7.4 多条件查询实现登录功能并使用PostMan测试 dao层接口 ```java package cn.kgc.tangcco.dao; import cn.kgc.tangcco.pojo.Student; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import java.util.List; /** * @author 李昊哲 * @Description * @create 2021/1/12 9:26 * JpaRepository T操作实体类泛型 ID实体类中主键属性的泛型 * JpaSpecificationExecutor T操作实体类泛型 */ public interface StudentDao extends JpaRepository, JpaSpecificationExecutor {} ``` service层实现类 ```java @Override public ResponseText loginByMobileAndAuthText(Student student) { if (student == null || StringUtils.isBlank(student.getMobile()) || StringUtils.isBlank(student.getAuthText())) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), null); } student.setAuthText(BaseCryptographyUtils.desEncodeHexString(student.getAuthText(), key)); try { return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), studentDao.findOne((root, cq, cb) -> { return cb.and(cb.equal(root.get("mobile"), student.getMobile()), cb.equal(root.get("authText"), student.getAuthText())); }).get()); }catch (Exception exception){ return new ResponseText<>(ResultCode.LOGIN_FAILED.getCode(), ResultCode.LOGIN_FAILED.getMsg(), null); } } ``` PonsMan测试 ​ 请求地址:http://localhost:8080/student/loginByMobileAndAuthText ​ 请求方式:POST ​ 请求参数: | name | value | | -------- | ----------- | | mobile | 15311484568 | | authText | 123456 | 登录成功返回结果: ```json { "code": "200", "data": { "authText": "1f4a79344c3e1f58", "cid": 4, "firstName": "王柔", "id": 1, "idCard": "653863198001212849", "lastName": "王柔", "mobile": "17004762790", "uuid": "f3b4a8b465dc4ad483c1ca54d671b163" }, "msg": "操作成功" } ``` 登录失败返回结果: ```json { "code": "10002", "msg": "登录失败" } ``` ##### 3.7.5 模糊查询后排序并使用PostMan测试 dao层接口 ```java package cn.kgc.tangcco.dao; import cn.kgc.tangcco.pojo.Student; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import java.util.List; /** * @author 李昊哲 * @Description * @create 2021/1/12 9:26 * JpaRepository T操作实体类泛型 ID实体类中主键属性的泛型 * JpaSpecificationExecutor T操作实体类泛型 */ public interface StudentDao extends JpaRepository, JpaSpecificationExecutor {} ``` service层实现类 ```java @Override public ResponseText> specificationLike(String firstName) { if (StringUtils.isBlank(firstName)) { return new ResponseText<>(ResultCode.PARAM_ERROR.getCode(), ResultCode.PARAM_ERROR.getMsg(), new ArrayList<>()); } List students = studentDao.findAll((root, cq, cb) -> { StringBuilder nickname = new StringBuilder(); nickname.append("%"); nickname.append(firstName); nickname.append("%"); return cb.or(cb.like(root.get("firstName").as(String.class), nickname.toString()), cb.like(root.get("lastName").as(String.class), nickname.toString())); },Sort.by(Sort.Direction.DESC, "cid")); if (students != null && students.size() > 0) { return new ResponseText<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), students); } return new ResponseText<>(ResultCode.DATA_EMPTY.getCode(), ResultCode.DATA_EMPTY.getMsg(), new ArrayList<>()); } ``` PonsMan测试 ​ 请求地址:http://localhost:8080/student/specificationLike ​ 请求方式:GET ​ 请求参数: | name | value | | --------- | ----- | | firstName | 孙 | 登录成功返回结果: ```json { "code": "200", "data": { "authText": "1f4a79344c3e1f58", "cid": 4, "firstName": "王柔", "id": 1, "idCard": "653863198001212849", "lastName": "王柔", "mobile": "17004762790", "uuid": "f3b4a8b465dc4ad483c1ca54d671b163" }, "msg": "操作成功" } ``` 登录失败返回结果: ```json { "code": "200", "data": [ { "authText": "1f4a79344c3e1f58", "cid": 5, "firstName": "公孙绚吃", "id": 114, "idCard": "71350119840129870X", "lastName": "公孙绚吃", "mobile": "17642217181", "uuid": "f36a08fbfbc74976bafd186ece1fe909" }, { "authText": "1f4a79344c3e1f58", "cid": 5, "firstName": "公孙婴", "id": 130, "idCard": "617514199408240160", "lastName": "公孙婴", "mobile": "13449740380", "uuid": "5f5525b2e5184628836ef9942e321e82" }, { "authText": "1f4a79344c3e1f58", "cid": 4, "firstName": "孙捶", "id": 128, "idCard": "339375198205213949", "lastName": "孙捶", "mobile": "13026338019", "uuid": "4ae0d4d1d0ba4b5ea1ed226108a54535" }, { "authText": "1f4a79344c3e1f58", "cid": 3, "firstName": "孙章", "id": 102, "idCard": "646615197302024483", "lastName": "孙章", "mobile": "15573942169", "uuid": "09a6e376e25745afa9e372be1606ee59" }, { "authText": "1f4a79344c3e1f58", "cid": 3, "firstName": "公孙萝倘", "id": 110, "idCard": "454080199509165300", "lastName": "公孙萝倘", "mobile": "13100349323", "uuid": "334b107f91f3490a89dffa85ae8cc09f" }, { "authText": "1f4a79344c3e1f58", "cid": 2, "firstName": "孙盯", "id": 61, "idCard": "542406198509154162", "lastName": "孙盯", "mobile": "15862199605", "uuid": "f1a7304c1be747b39b1535130fd717eb" }, { "authText": "1f4a79344c3e1f58", "cid": 1, "firstName": "公孙憎", "id": 12, "idCard": "448456198211048370", "lastName": "公孙憎", "mobile": "13720463226", "uuid": "c4da87838fa74d1daa8286f8f4b0e38a" }, { "authText": "1f4a79344c3e1f58", "cid": 1, "firstName": "公孙侯锦", "id": 118, "idCard": "411287198403086218", "lastName": "公孙侯锦", "mobile": "13721248393", "uuid": "7e31bb44ae8e4d918f4542f5e5139598" } ], "msg": "操作成功" } ``` ##### 3.7.6 动态SQL分页查询后排序并使用PostMan测试 dao层接口 ```java package cn.kgc.tangcco.dao; import cn.kgc.tangcco.pojo.Student; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import java.util.List; /** * @author 李昊哲 * @Description * @create 2021/1/12 9:26 * JpaRepository T操作实体类泛型 ID实体类中主键属性的泛型 * JpaSpecificationExecutor T操作实体类泛型 */ public interface StudentDao extends JpaRepository, JpaSpecificationExecutor {} ``` service层实现类 ```java @Override public Page page(Student student, PageParam pageParam) { return studentDao.findAll((root, cq, cb) -> { Predicate cid = null; Predicate nickName = null; if (student.getCid() != null && student.getCid() > 0) { cid = cb.equal(root.get("cid"), student.getCid()); } if (!StringUtils.isBlank(student.getFirstName())) { StringBuilder nickname = new StringBuilder(); nickname.append("%"); nickname.append(student.getFirstName()); nickname.append("%"); nickName = cb.or(cb.like(root.get("firstName").as(String.class), nickname.toString()), cb.like(root.get("lastName").as(String.class), nickname.toString())); } if (cid != null && nickName != null) { return cb.and(cid, nickName); } else if (cid != null && nickName == null) { return cid; } else if (cid == null && nickName != null) { return nickName; } else { return null; } }, PageRequest.of(pageParam.getPageNum() - 1, pageParam.getPageSize(), Sort.by(Sort.Direction.DESC, "id"))); } ``` PonsMan测试 ​ 请求地址:http://localhost:8080/student/page?page=2&limit5 > ​ page : 查询页面号 > > ​ limit: 每页记录数 ​ 请求方式:POST ​ 请求参数: | name | value | | --------- | ----- | | cid | 3 | | firstName | 李 | 第一页返回结果: ```json { "content": [ { "authText": "1f4a79344c3e1f58", "cid": 1, "firstName": "宋棍糜", "id": 200, "idCard": "916281198302191822", "lastName": "宋棍糜", "mobile": "18067192779", "uuid": "6d0bd2ccb31b433dbacd51ec65f90275" }, { "authText": "1f4a79344c3e1f58", "cid": 1, "firstName": "李招", "id": 199, "idCard": "211497198511043617", "lastName": "李招", "mobile": "18722348916", "uuid": "050b90568f5c42848f11ea2e631701cd" }, { "authText": "1f4a79344c3e1f58", "cid": 4, "firstName": "朱私", "id": 198, "idCard": "643284198309191316", "lastName": "朱私", "mobile": "18708644764", "uuid": "f4e153078ada4050a488dcc1296cd9af" }, { "authText": "1f4a79344c3e1f58", "cid": 4, "firstName": "孔紊渗", "id": 197, "idCard": "350518199010187684", "lastName": "孔紊渗", "mobile": "18992171633", "uuid": "892604fce5db4999956aff79a1928564" }, { "authText": "1f4a79344c3e1f58", "cid": 4, "firstName": "郑截", "id": 196, "idCard": "234689197612188615", "lastName": "郑截", "mobile": "17659261592", "uuid": "c34eff3134094f9d87067390516a1076" } ], "empty": false, "first": true, "last": false, "number": 0, "numberOfElements": 5, "pageable": { "offset": 0, "pageNumber": 0, "pageSize": 5, "paged": true, "sort": { "empty": false, "sorted": true, "unsorted": false }, "unpaged": false }, "size": 5, "sort": { "$ref": "$.pageable.sort" }, "totalElements": 200, "totalPages": 40 } ``` 第二页返回结果: ```json { "content": [ { "authText": "1f4a79344c3e1f58", "cid": 3, "firstName": "李鲜", "id": 14, "idCard": "648815198506187214", "lastName": "鲜于饥贴", "mobile": "14780974323", "uuid": "f14b8d3b15d645d0b8d5aa19ed8f76df" }, { "authText": "1f4a79344c3e1f58", "cid": 3, "firstName": "李夯瑞", "id": 13, "idCard": "427623199708063537", "lastName": "任夯瑞", "mobile": "18459766061", "uuid": "34094b667bb64740b8f63d342ce79994" }, { "authText": "1f4a79344c3e1f58", "cid": 3, "firstName": "李矩", "id": 3, "idCard": "213665200001241025", "lastName": "阎矩痒", "mobile": "13549677158", "uuid": "aa25f6ac018c422987bfbe0723040925" } ], "empty": false, "first": false, "last": true, "number": 1, "numberOfElements": 3, "pageable": { "offset": 5, "pageNumber": 1, "pageSize": 5, "paged": true, "sort": { "empty": false, "sorted": true, "unsorted": false }, "unpaged": false }, "size": 5, "sort": { "$ref": "$.pageable.sort" }, "totalElements": 8, "totalPages": 2 } ``` #### 3.8 方法名称命名规则查询kgc ``` 规则: findBy(关键字)+属性名称(属性名称的首字母大写)+查询条件(首字母大写) ``` | 关键字 | 方法命名 | sql where 字句 | | ------------------ | ------------------------------ | -------------------------- | | And | findByNameAndPwd | where name= ? and pwd =? | | Or | findByNameOrSex | where name= ? or sex=? | | Is,Equal | findById, | findByIdEquals | | Between | findByIdBetween | where id between ? and ? | | LessThan | findByIdLessThan | where id < ? | | LessThanEqual | findByIdLessThanEquals | where id <= ? | | GreaterThan | findByIdGreaterThan | where id > ? | | GreaterThanEqual | findByIdGreaterThanEquals | where id > = ? | | After | findByIdAfter | where id > ? | | Before | findByIdBefore | where id < ? | | IsNull | findByNameIsNull | where name is null | | isNotNull,Not Null | findByNameNotNull | where name is not | | Like | findByNameLike | where name like ? | | NotLike | findByNameNotLike | where name not like ? | | StartingWith | findByNameStartingWith | where name like '?%' | | EndingWith | findByNameEndingWith | where name like '%?' | | Containing | findByNameContaining | where name like '%?%' | | OrderBy | findByIdOrderByXDesc | where id=? order by x desc | | Not | findByNameNot | where name <> ? | | In | findByIdIn(Collection c) | where id in (?) | | NotIn | findByIdNotIn(Collection c) | where id not in (?) | | True | findByAaaTue | where aaa = true | | False | findByAaaFalse | where aaa = false | | IgnoreCase | findByNameIgnoreCase | where UPPER(name)=UPPER(?) | ### [04.springmvc+spring-data-jpa多表操作](demo04) #### 4.1 需求介绍 本章节我们是实现的功能是spring data jpa 数据库多表操作。 注解说明 > - **@OneToMany:** > 作用:建立一对多的关系映射 > 属性: > **targetEntityClass**:指定多的多方的类的字节码 > **mappedBy**:指定从表实体类中引用主表对象的名称。 > **cascade**:指定要使用的级联操作 > **fetch**:指定是否采用延迟加载 > **orphanRemoval**:是否使用孤儿删除 > - **@ManyToOne** > 作用:建立多对一的关系 > 属性: > **targetEntityClass**:指定一的一方实体类字节码 > **cascade**:指定要使用的级联操作 > **fetch**:指定是否采用延迟加载 > **optional**:关联是否可选。如果设置为false,则必须始终存在非空关系。 > - **@JoinColumn** > 作用:用于定义主键字段和外键字段的对应关系。 > 属性: > **name**:指定外键字段的名称 > **referencedColumnName**:指定引用主表的主键字段名称 > **unique**:是否唯一。默认值不唯一 > **nullable**:是否允许为空。默认值允许。 > **insertable**:是否允许插入。默认值允许。 > **updatable**:是否允许更新。默认值允许。 > **columnDefinition**:列的定义信息。 ``` true none none ``` #### 4.2 开发包介绍 > [开发包介绍](#1.2 开发包介绍) #### 4.3 搭建开发环境 > 程序运行自动创建表 > > 注意json循环依赖,在实体类代码中有具体体现 ##### 4.3.1 maven工程导入坐标 ```xml 4.0.0 cn.kgc.tangcco demo04 1.0-SNAPSHOT demo04 war 15 15 15 15 utf-8 utf-8 UTF-8 true true 1.9.6 0.9.5.5 3.3.0 1.4 2.8.0 3.11 3.6 1.2.4 3.9.0 1.2.75 1.1.2 30.0-jre 4.2.3 5.5.7 2.11.3 1.1.2 2.10.8 5.4.27.Final 5.7.0 1.2.17 1.18.16 8.0.22 4.0.1 1.7.30 1.7.30 5.3.2 javax.servlet javax.servlet-api 4.0.1 org.junit.jupiter junit-jupiter-api ${junit.version} test org.junit.jupiter junit-jupiter-engine ${junit.version} test junit junit 4.13.1 test com.github.binarywang java-testdata-generator ${generator.version} org.projectlombok lombok ${lombok.version} provided org.springframework spring-context ${spring.version} org.springframework spring-orm ${spring.version} org.springframework spring-aop ${spring.version} org.springframework spring-aspects ${spring.version} org.springframework spring-web ${spring.version} org.springframework spring-webmvc ${spring.version} org.springframework spring-jdbc ${spring.version} org.springframework spring-tx ${spring.version} org.springframework spring-test ${spring.version} org.aspectj aspectjrt ${aspectj.version} org.aspectj aspectjweaver ${aspectj.version} cglib cglib ${cglib.version} mysql mysql-connector-java ${mysql.version} com.mchange c3p0 ${c3p0.version} com.alibaba druid ${druid.version} commons-logging commons-logging 1.2 log4j log4j ${log4j.version} org.slf4j slf4j-api ${slf4j-api.version} org.slf4j slf4j-log4j12 ${slf4j-log4j12.version} test com.alibaba fastjson ${fastjson.version} cn.hutool hutool-all ${hutool-all.version} org.apache.commons commons-lang3 ${commons-lang3.varsion} commons-io commons-io ${commons-io.varsion} commons-fileupload commons-fileupload ${commons-fileupload.varsion} commons-net commons-net ${commons-net.varsion} com.google.inject guice ${guice.version} com.google.guava guava ${guava.version} joda-time joda-time ${joda-time.version} com.github.binarywang java-testdata-generator ${java-testdata-generator.version} org.hibernate hibernate-core ${hibernate.version} org.hibernate hibernate-entitymanager ${hibernate.version} org.hibernate hibernate-c3p0 ${hibernate.version} org.hibernate hibernate-validator 7.0.0.Final javax.validation validation-api 2.0.1.Final org.springframework.data spring-data-jpa 2.4.2 javax.el javax.el-api 3.0.0 org.glassfish javax.el 3.0.0 com.fasterxml.jackson.core jackson-core ${jackson.version} com.fasterxml.jackson.core jackson-databind ${jackson.version} com.fasterxml.jackson.core jackson-annotations ${jackson.version} org.apache.maven.plugins maven-war-plugin 3.3.0 maven-clean-plugin 3.1.0 maven-resources-plugin 3.1.0 maven-install-plugin 2.5.2 maven-deploy-plugin 2.8.2 maven-compiler-plugin 3.8.1 UTF-8 ${jdk.version} ${jdk.version} org.apache.maven.plugins maven-surefire-plugin 2.22.2 true org.apache.maven.plugins maven-source-plugin 3.2.1 attach-sources jar org.apache.maven.plugins maven-surefire-plugin 2.22.2 true org.apache.tomcat.maven tomcat7-maven-plugin 2.2 8080 / UTF-8 kgc tangcco http://localhost:8080/manager/text org.eclipse.jetty jetty-maven-plugin 9.4.35.v20201120 / 8080 ``` ##### 4.3.2 spring框架的配置文件 ###### db.properties ```properties jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://47.94.130.233:3306/ssm?useUnicode=true&characterEncoding=UTF8&useSSL=true&serverTimeZone=Aisa/Shanghai jdbc.username=root jdbc.password=123456 jdbc.removeAbandoned=true jdbc.removeAbandonedTimeout=1800 jdbc.logAbandoned=true ``` ###### applicationContext.xml ```xml true update none ``` ###### springmvc-server.xml ```xml ``` ###### web.xml ```xml springMVC org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:spring/spring-servlet.xml 1 springMVC / CharacterEncodingFilter org.springframework.web.filter.CharacterEncodingFilter encoding utf-8 CharacterEncodingFilter / org.springframework.web.context.ContextLoaderListener contextConfigLocation classpath:spring/applicationContext.xml OpenSessionInViewFilter org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter OpenSessionInViewFilter /* ``` #### 4.4 一对一 一对多 多对一 ##### 4.4.1 编写实体类和数据库表的映射配置 ```java package cn.kgc.tangcco.pojo; import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; import javax.persistence.*; import java.io.Serializable; import java.util.List; /** * @author 李昊哲 * @Description * @create 2020/10/30 9:19 */ @NoArgsConstructor @AllArgsConstructor @Getter @Setter @Accessors(chain = true) @Table(name = "clazz") @Entity @JsonIgnoreProperties(value = {"hibernateLazyInitializer", "handler", "fieldHandler"}) public class Clazz implements Serializable { private static final long serialVersionUID = -6973342665464585093L; /** * 班级编号 */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Integer id; /** * 班级名称 */ @Column(name = "name") private String name; @JsonBackReference // @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "id") @OneToMany(targetEntity = Student.class, mappedBy = "clazz", cascade = {CascadeType.ALL}, orphanRemoval = false, fetch = FetchType.LAZY) public List students; public Clazz(String name) { this.name = name; } public Clazz(Integer id, String name) { this.id = id; this.name = name; } public Clazz(String name, List students) { this.name = name; this.students = students; } } ``` ```java package cn.kgc.tangcco.pojo; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; import javax.persistence.*; import java.io.Serializable; /** * @author 李昊哲 * @Description * @create 2020/10/30 9:23 */ @NoArgsConstructor @AllArgsConstructor @Getter @Setter @Accessors(chain = true) @Table(name = "student") @Entity @JsonIgnoreProperties(value = {"hibernateLazyInitializer", "handler", "fieldHandler"}) public class Student implements Serializable { private static final long serialVersionUID = -2373830287539636924L; /** * 学生编号 */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Integer id; /** * 学生唯一标志 */ @Column(name = "uuid") private String uuid; /** * 学生曾用名 */ @Column(name = "first_name") private String firstName; /** * 学生现用名 */ @Column(name = "last_name") private String lastName; /** * 学生身份证 */ @Column(name = "id_card") private String idCard; /** * 学生手机号 */ @Column(name = "mobile") private String mobile; /** * 密码 */ @Column(name = "auth_text") private String authText; /** * 学生所在班级编号 */ @Column(name = "cid") private Integer cid; /** * 学生所在班级信息 */ @ManyToOne(targetEntity = Clazz.class, cascade = {CascadeType.MERGE}, fetch = FetchType.LAZY, optional = false) @JoinColumn(name = "cid", referencedColumnName = "id", insertable = false, updatable = false, nullable = true) private Clazz clazz; public Student(String uuid, String firstName, String lastName, String idCard, String mobile, String authText) { this.uuid = uuid; this.firstName = firstName; this.lastName = lastName; this.idCard = idCard; this.mobile = mobile; this.authText = authText; } public Student(String uuid, String firstName, String lastName, String idCard, String mobile, String authText, Integer cid) { this.uuid = uuid; this.firstName = firstName; this.lastName = lastName; this.idCard = idCard; this.mobile = mobile; this.authText = authText; this.cid = cid; } public Student(String uuid, String firstName, String lastName, String idCard, String mobile, String authText, Clazz clazz) { this.uuid = uuid; this.firstName = firstName; this.lastName = lastName; this.idCard = idCard; this.mobile = mobile; this.authText = authText; this.clazz = clazz; } public Student(String uuid, String firstName, String lastName, String idCard, String mobile, String authText, Integer cid, Clazz clazz) { this.uuid = uuid; this.firstName = firstName; this.lastName = lastName; this.idCard = idCard; this.mobile = mobile; this.authText = authText; this.cid = cid; this.clazz = clazz; } } ``` ##### 4.4.2 编写符合spring Data JPA规范的dao层接口 ```java package cn.kgc.tangcco.dao; import cn.kgc.tangcco.pojo.Clazz; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** * @author 李昊哲 * @Description * @create 2021/1/12 9:26 * JpaRepository T操作实体类泛型 ID实体类中主键属性的泛型 * JpaSpecificationExecutor T操作实体类泛型 */ public interface ClazzDao extends JpaRepository , JpaSpecificationExecutor {} ``` ```java package cn.kgc.tangcco.dao; import cn.kgc.tangcco.pojo.Student; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import java.util.List; /** * @author 李昊哲 * @Description * @create 2021/1/12 9:26 * JpaRepository T操作实体类泛型 ID实体类中主键属性的泛型 * JpaSpecificationExecutor T操作实体类泛型 */ public interface StudentDao extends JpaRepository, JpaSpecificationExecutor { /** * 根据ID查找学生信息 * * @param id 学生id * @return 学生信息 */ @Query("from Student where id = ?1") public Student selectOne(Integer id); /** * 按照名字模糊查找 * * @param firstName 学生模糊姓名 * @return 模糊姓名查找返回的学生列表 */ @Query(value = "from Student where firstName like ?1 or lastName like ?2") public List findStudentLikeName(String firstName, String lastName); /** * 更新数据 * * @param id 更新条件 * @param firstName 曾用名 * @param lastName 现用名 * @param idCard 身份证 * @param mobile 手机号 * @param authText 密码,des加密后的字符串 * @param cid 所在班级编号 */ @Modifying @Query(value = "update Student set firstName = ?2,lastName = ?3, idCard = ?4, mobile = ?5, authText = ?6,cid = ?7 where id = ?1") public int update(Integer id, String firstName, String lastName, String idCard, String mobile, String authText, Integer cid); /** * 再删除之前 * @param cid * @return */ @Modifying @Query(value = "update Student set cid = 0 where cid = ?1") public int nativUpdate(Integer cid); /** * 使用原生SQL模糊查找某班级的学生信息列表 * * @param cid 班级编号 * @param firstName 曾用名 * @param lastName 现用名 * @return 模糊查找某班级的学生信息列表 */ @Query(value = "SELECT * FROM `student` WHERE cid = ?1 AND (first_name like ?2 OR last_name like ?3)", nativeQuery = true) public List nativeQuery(Integer cid, String firstName, String lastName); } ``` ##### 4.4.3 编写Handler接收请求响应操作后的数据 ```java package cn.kgc.tangcco.action; import cn.kgc.tangcco.model.ResponseText; import cn.kgc.tangcco.pojo.Clazz; import cn.kgc.tangcco.service.ClazzService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author 李昊哲 * @Description * @create 2020/10/30 15:09 */ @RestController @RequestMapping(value = "/clazz") public class ClazzAction { @Autowired private ClazzService clazzService; @GetMapping(value = "/list") public ResponseText> queryClazzList() { return clazzService.queryClazzs(new Clazz()); } @GetMapping(value = "/findOne") public ResponseText queryClazz(Integer id) { return clazzService.queryClazz(id); } @GetMapping(value = "/getOne") public ResponseText getClazz(Integer id) { return clazzService.getClazz(id); } @GetMapping(value = "/getOneJson") public ResponseText> getOneJson(Integer id) { Map map = new HashMap<>(); ResponseText responseText = clazzService.getClazz(id); Clazz clazz = responseText.getData(); map.put("clazz", clazz); map.put("students", clazz.getStudents()); return new ResponseText<>(responseText.getCode(), responseText.getMsg(), map); } @PutMapping(value = "/save") public ResponseText save(Clazz clazz, BindingResult bindingResult) { return clazzService.saveOrUpdate(clazz); } @DeleteMapping(value = "/remove/{id}") public ResponseText removeClazz(@PathVariable(name = "id") Integer id) { return clazzService.remove(id); } @GetMapping(value = "/count") public ResponseText count() { return clazzService.count(); } @GetMapping(value = "/exists") public ResponseText exists(Integer id) { return clazzService.exists(id); } } ``` ```java package cn.kgc.tangcco.action; import cn.kgc.tangcco.model.PageParam; import cn.kgc.tangcco.model.ResponseText; import cn.kgc.tangcco.pojo.Student; import cn.kgc.tangcco.service.StudentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @author 李昊哲 * @Description * @create 2021/1/12 14:48 */ @RestController @RequestMapping(value = "/student") public class StudentAction { @Autowired private StudentService studentService; @GetMapping(value = "/findOne") public ResponseText findOne(Integer id) { return studentService.findOne(id); } @GetMapping(value = "/getOne") public ResponseText getOne(Integer id) { return studentService.getOne(id); } @GetMapping(value = "/selectOne") public ResponseText selectOne(Integer id) { return studentService.getOne(id); } @GetMapping(value = "/findLike") public ResponseText> findLike(String firstName) { return studentService.findLike(firstName); } @PutMapping(value = "/update") public ResponseText update(Student student) { return studentService.update(student); } @PostMapping(value = "/nativeQuery") public ResponseText> nativeQuery(Student student) { return studentService.nativeQuery(student); } @PostMapping(value = "/loginByMobileAndAuthText") public ResponseText loginByMobileAndAuthText(Student student) { return studentService.loginByMobileAndAuthText(student); } @GetMapping(value = "/specificationLike") public ResponseText> specificationLike(String firstName) { return studentService.specificationLike(firstName); } @PostMapping(value = "/page") public Page page(Student student, @RequestParam(value = "page", required = false, defaultValue = "1") Integer page, @RequestParam(value = "limit", required = false, defaultValue = "5") Integer limit) { return studentService.page(student, new PageParam(page, limit)); } } ``` ##### 4.4.4 编写测试类测试接口 在src/test/java 目录下新建测试Class > cn.kgc.tangcco.dao.ClazzDaoTest > > cn.kgc.tangcco.dao.StudentDaoTest ```java package cn.kgc.tangcco.dao; import cn.binarywang.tools.generator.ChineseIDCardNumberGenerator; import cn.binarywang.tools.generator.ChineseMobileNumberGenerator; import cn.binarywang.tools.generator.ChineseNameGenerator; import cn.hutool.core.util.IdUtil; import cn.kgc.tangcco.pojo.Clazz; import cn.kgc.tangcco.pojo.Student; import cn.kgc.tangcco.utils.cryptography.BaseCryptographyUtils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; /** * @author 李昊哲 * @Description * @create 2021/1/16 8:22 */ @RunWith(SpringRunner.class) @ContextConfiguration(locations = "classpath:spring/applicationContext.xml") public class ClazzDaoTest { @Autowired private ClazzDao clazzDao; @Autowired private StudentDao studentDao; private final static String key = "lihaozhelihaozhelihaozhelihaozhelihaozhelihaozhelihaozhelihaozhe"; @Test @Transactional() @Rollback(value = false) public void test01() { Clazz clazz = clazzDao.saveAndFlush(new Clazz("kjde1021")); String authText = BaseCryptographyUtils.desEncodeHexString("123456", key); String uuid = IdUtil.fastSimpleUUID(); String firstName = ChineseNameGenerator.getInstance().generate(); String lastName = firstName; String idCard = ChineseIDCardNumberGenerator.getInstance().generate(); String mobile = ChineseMobileNumberGenerator.getInstance().generate(); studentDao.saveAndFlush(new Student(uuid, firstName, lastName, idCard, mobile, authText, clazz.getId())); System.out.println("程序执行结束"); } @Test @Transactional() @Rollback(value = false) public void test02() { List clazzes = new ArrayList<>() {{ add(new Clazz("kjde1022")); add(new Clazz("kjde1023")); }}; List clazzList = clazzDao.saveAll(clazzes); clazzList = clazzDao.findAll(); String authText = BaseCryptographyUtils.desEncodeHexString("123456", key); List students = new ArrayList<>(); Student student = null; for (int i = 0; i < 20; i++) { String uuid = IdUtil.fastSimpleUUID(); String firstName = ChineseNameGenerator.getInstance().generate(); String lastName = firstName; String idCard = ChineseIDCardNumberGenerator.getInstance().generate(); String mobile = ChineseMobileNumberGenerator.getInstance().generate(); Integer cid = clazzList.get(ThreadLocalRandom.current().nextInt(clazzList.size())).getId(); student = new Student(uuid, firstName, lastName, idCard, mobile, authText, cid); students.add(student); } List studentList = studentDao.saveAll(students); System.out.println("程序执行结束"); } @Test @Transactional(readOnly = true) public void test03() { Clazz clazz = clazzDao.getOne(1); System.out.println(clazz.getName()); ObjectMapper objectMapper = new ObjectMapper(); clazz.getStudents().forEach(student -> { try { System.out.println(objectMapper.writeValueAsString(student)); } catch (JsonProcessingException e) { e.printStackTrace(); } }); System.out.println("程序执行结束"); } @Test @Transactional() @Rollback(value = false) public void test04() { clazzDao.deleteById(1); System.out.println("程序执行结束"); } @Test @Transactional() @Rollback(value = false) public void test05() { Optional optional = clazzDao.findById(2); if (!optional.isEmpty()) { Clazz clazz = optional.get(); List students = studentDao.findAll((root, cq, cb) -> { return cb.equal(root.get("cid"), clazz.getId()); }); students.forEach(student -> { student.setCid(null); studentDao.saveAndFlush(student); }); clazzDao.deleteById(clazz.getId()); } System.out.println("程序执行结束"); } @Test @Transactional() @Rollback(value = false) public void test06() { Optional optional = clazzDao.findById(3); if (!optional.isEmpty()) { Clazz clazz = optional.get(); List students = studentDao.findAll((root, cq, cb) -> { return cb.equal(root.get("cid"), clazz.getId()); }); List studentList = students.stream().map(student -> student.setCid(null)).collect(Collectors.toList()); studentList = studentDao.saveAll(studentList); studentDao.flush(); clazzDao.deleteById(clazz.getId()); } System.out.println("程序执行结束"); } } ``` ```java package cn.kgc.tangcco.dao; import cn.kgc.tangcco.pojo.Student; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; /** * @author 李昊哲 * @Description * @create 2021/1/16 20:38 */ @RunWith(SpringRunner.class) @ContextConfiguration(locations = "classpath:spring/applicationContext.xml") public class StudentDaoTest { @Autowired private StudentDao studentDao; @Autowired private ClazzDao clazzDao; private final static String key = "lihaozhelihaozhelihaozhelihaozhelihaozhelihaozhelihaozhelihaozhe"; @Test @Transactional(readOnly = true) public void test01() { Student student = studentDao.getOne(1); System.out.println(student.getFirstName()); System.out.println(student.getClazz().getName()); System.out.println("程序执行结束"); } @Test @Transactional() @Rollback(value = false) public void test02() { studentDao.deleteById(1); System.out.println("程序执行结束"); } } ``` 生成数据 > 班级数据 > > | id | name | > | ---- | -------- | > | 1 | kjde1021 | > | 2 | kjde1022 | > > 学生数据 > > | id | uuid | id_card | first_name | last_name | mobile | auth_text | cid | > | ---- | -------------------------------- | ------------------ | ---------- | --------- | ----------- | ---------------- | ---- | > | 1 | cf8b4c2cbb8946379277d7e8389bcac1 | 430180199102068443 | 梁馏寇 | 梁馏寇 | 13081906675 | 1f4a79344c3e1f58 | 1 | > | 2 | 80ebbfe1e8194a1794fd25415bc4edc9 | 456239199801296670 | 贾同 | 贾同 | 13419949602 | 1f4a79344c3e1f58 | 2 | > | 3 | 786c245cdda54a6180e90d85299d787c | 451231198410076996 | 杜焰 | 杜焰 | 15267959641 | 1f4a79344c3e1f58 | 2 | > | 4 | 591fd1cc61114389badf171c729fb9fd | 222239199603173673 | 邹惨 | 邹惨 | 18793527857 | 1f4a79344c3e1f58 | 2 | > | 5 | 1a125ba3694a4a6c94dac07510aaba60 | 712375197410218386 | 尉迟啪控 | 尉迟啪控 | 18700478053 | 1f4a79344c3e1f58 | 2 | > | 6 | f4f6215837c741dda6f9078eb21a8950 | 613442199005136568 | 叶美 | 叶美 | 13858900318 | 1f4a79344c3e1f58 | 1 | > | 7 | 60d86f00ddbb43c59c92c4c5123ab81e | 512320199710237454 | 萧芽害 | 萧芽害 | 18731751624 | 1f4a79344c3e1f58 | 2 | > | 8 | aaeeaf3f47c148cab88dce259e4e73dc | 615334199911208988 | 蒋沂 | 蒋沂 | 18973341708 | 1f4a79344c3e1f58 | 1 | > | 9 | 007cb4ac62454beea19eac8767510bb4 | 422166199507256648 | 雷择 | 雷择 | 13741116395 | 1f4a79344c3e1f58 | 2 | > | 10 | 68638cdc9d41488eb13a36dbe49dbb06 | 339873198607013637 | 苏价 | 苏价 | 18790712153 | 1f4a79344c3e1f58 | 2 | > log日志 发现 saveAll方法只是多次调用insert ```sql Hibernate: insert into clazz (name) values (?) Hibernate: insert into clazz (name) values (?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) ``` ##### 4.4.5 一对多新增数据 > cn.kgc.tangcco.dao.ClazzDaoTest ```java @Test @Transactional() @Rollback(value = false) public void test01() { Clazz clazz = clazzDao.saveAndFlush(new Clazz("kjde1021")); String authText = BaseCryptographyUtils.desEncodeHexString("123456", key); String uuid = IdUtil.fastSimpleUUID(); String firstName = ChineseNameGenerator.getInstance().generate(); String lastName = firstName; String idCard = ChineseIDCardNumberGenerator.getInstance().generate(); String mobile = ChineseMobileNumberGenerator.getInstance().generate(); studentDao.saveAndFlush(new Student(uuid, firstName, lastName, idCard, mobile, authText, clazz.getId())); System.out.println("程序执行结束"); } ``` 截取部分log可以看到当数据库中表不存在程序帮我们自动建表并插入数据: ```sql Hibernate: create table clazz (id integer not null auto_increment, name varchar(255), primary key (id)) engine=InnoDB Hibernate: create table student (id integer not null auto_increment, auth_text varchar(255), cid integer, first_name varchar(255), id_card varchar(255), last_name varchar(255), mobile varchar(255), uuid varchar(255), primary key (id)) engine=InnoDB Hibernate: alter table student add constraint FK13o0qe2l7dyf11s1arwi7w5aq foreign key (cid) references clazz (id) Hibernate: insert into clazz (name) values (?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) ``` 生成后的数据: 班级表 | id | name | | ---- | -------- | | 1 | kjde1021 | 学生表 | id | auth_text | cid | first_name | id_card | last_name | mobile | uuid | | ---- | ---------------- | ---- | ---------- | ------------------ | --------- | ----------- | -------------------------------- | | 1 | 1f4a79344c3e1f58 | 1 | 金笑 | 654878199212019213 | 金笑 | 13563168744 | d2b8883635ed4e67a57b1e5eeaac47b4 | ##### 4.4.6 一对多新增多条数据 > cn.kgc.tangcco.dao.ClazzDaoTest ```java @Test @Transactional() @Rollback(value = false) public void test02() { List clazzes = new ArrayList<>() {{ add(new Clazz("kjde1022")); add(new Clazz("kjde1023")); }}; List clazzList = clazzDao.saveAll(clazzes); clazzList = clazzDao.findAll(); String authText = BaseCryptographyUtils.desEncodeHexString("123456", key); List students = new ArrayList<>(); Student student = null; for (int i = 0; i < 20; i++) { String uuid = IdUtil.fastSimpleUUID(); String firstName = ChineseNameGenerator.getInstance().generate(); String lastName = firstName; String idCard = ChineseIDCardNumberGenerator.getInstance().generate(); String mobile = ChineseMobileNumberGenerator.getInstance().generate(); Integer cid = clazzList.get(ThreadLocalRandom.current().nextInt(clazzList.size())).getId(); student = new Student(uuid, firstName, lastName, idCard, mobile, authText, cid); students.add(student); } List studentList = studentDao.saveAll(students); System.out.println("程序执行结束"); } ``` log日志 发现 saveAll方法只是多次调用insert ```sql Hibernate: insert into clazz (name) values (?) Hibernate: insert into clazz (name) values (?) Hibernate: select clazz0_.id as id1_0_, clazz0_.name as name2_0_ from clazz clazz0_ Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into student (auth_text, cid, first_name, id_card, last_name, mobile, uuid) values (?, ?, ?, ?, ?, ?, ?) ``` 生成后的数据: 班级表 | id | name | | ---- | -------- | | 1 | kjde1021 | | 2 | kjde1022 | | 3 | kjde1023 | 学生表 | id | auth_text | cid | first_name | id_card | last_name | mobile | uuid | | ---- | ---------------- | ---- | ---------- | ------------------ | --------- | ----------- | -------------------------------- | | 1 | 1f4a79344c3e1f58 | 1 | 金笑 | 654878199212019213 | 金笑 | 13563168744 | d2b8883635ed4e67a57b1e5eeaac47b4 | | 2 | 1f4a79344c3e1f58 | 1 | 黄狭 | 372852199301102315 | 黄狭 | 14738828014 | d29d5613aeda422cbf4595ec75973d84 | | 3 | 1f4a79344c3e1f58 | 2 | 龙屿卜 | 544901199903087203 | 龙屿卜 | 15349187917 | 58c681a115ba4c4d94e6015ac4180316 | | 4 | 1f4a79344c3e1f58 | 1 | 李几交 | 357696199602024193 | 李几交 | 15747982902 | 2dd53148c3014ebeaa113d36b71a2b25 | | 5 | 1f4a79344c3e1f58 | 3 | 许换 | 616470197707275756 | 许换 | 18195217069 | 696bedbc55ee4f3da99192de2c2a50a2 | | 6 | 1f4a79344c3e1f58 | 1 | 董荣猿 | 467473199903179922 | 董荣猿 | 17895607382 | 0fddb8d29594497e989a53da4c5dacc1 | | 7 | 1f4a79344c3e1f58 | 3 | 万挪 | 510012199204145890 | 万挪 | 18715316438 | bdfd501ec8df4be0ad0889d392691459 | | 8 | 1f4a79344c3e1f58 | 1 | 钟辽莽 | 82703719930114815X | 钟辽莽 | 13215491337 | 49251922a84f4ad397fc68f512a9e407 | | 9 | 1f4a79344c3e1f58 | 2 | 石眶倪 | 312454198410269400 | 石眶倪 | 17019601898 | 98daf580299d44459c13ba3492ac2e77 | | 10 | 1f4a79344c3e1f58 | 3 | 尹戴 | 316768197210016015 | 尹戴 | 18104752926 | d31aaa41ae394346bf3328581f0fca31 | | 11 | 1f4a79344c3e1f58 | 3 | 夏侯钵 | 626356198004105252 | 夏侯钵 | 15774977688 | 0ee3e9c54e9943fe88cf48e8600971bb | | 12 | 1f4a79344c3e1f58 | 2 | 阎匠斟 | 218366198903101173 | 阎匠斟 | 15670645999 | dcdbc07c5962464d8d3e66a5cb36853a | | 13 | 1f4a79344c3e1f58 | 1 | 夏爱盯 | 517376199903095831 | 夏爱盯 | 17661954044 | 878edf627fff45219f973bd277dfd1ec | | 14 | 1f4a79344c3e1f58 | 1 | 钟劳莆 | 370266197405032643 | 钟劳莆 | 13965310079 | a2fb7f9e1d0e4785bdba761d2b11aa74 | | 15 | 1f4a79344c3e1f58 | 1 | 薛赁 | 625795198704224661 | 薛赁 | 17776179267 | 5eb6c4856dc84c6d9adf43fe2bf72709 | | 16 | 1f4a79344c3e1f58 | 1 | 东方俞 | 630096198611058735 | 东方俞 | 15247730702 | f9d72d951b6347f88de23972dc9fe4f2 | | 17 | 1f4a79344c3e1f58 | 2 | 雷积初 | 64035819730624107X | 雷积初 | 18948370461 | bc94a95337a5450dbadd0c11136e2787 | | 18 | 1f4a79344c3e1f58 | 2 | 夏侯新 | 645251197503132117 | 夏侯新 | 15953170952 | 8395242ea1714b6eaa46410e8f7991b0 | | 19 | 1f4a79344c3e1f58 | 3 | 孙执弥 | 528685199304186374 | 孙执弥 | 17623924105 | c3dc26263bce439b95152e890ca8bf1a | | 20 | 1f4a79344c3e1f58 | 1 | 宋捎 | 811350198106256564 | 宋捎 | 13132818283 | 27bbf78bbd5a47c1aa671eed1ef4dcc9 | | 21 | 1f4a79344c3e1f58 | 2 | 独孤阀 | 211477199105053624 | 独孤阀 | 18156835394 | 62fb707e9b0f40bba83e98910a9141d9 | ##### 4.4.7 多对一懒加载查询测试 > cn.kgc.tangcco.dao.StudentDaoTest ```java @Test @Transactional(readOnly = true) public void test01() { Student student = studentDao.getOne(1); System.out.println(student.getFirstName()); System.out.println(student.getClazz().getName()); System.out.println("程序执行结束"); } ``` 根据log日志可以看出只有调用关联表数据的时候才会进行二次查询 ```sql Hibernate: select student0_.id as id1_4_0_, student0_.auth_text as auth_tex2_4_0_, student0_.cid as cid3_4_0_, student0_.first_name as first_na4_4_0_, student0_.id_card as id_card5_4_0_, student0_.last_name as last_nam6_4_0_, student0_.mobile as mobile7_4_0_, student0_.uuid as uuid8_4_0_ from student student0_ where student0_.id=? 金笑 Hibernate: select clazz0_.id as id1_0_0_, clazz0_.name as name2_0_0_ from clazz clazz0_ where clazz0_.id=? kjde1021 程序执行结束 ``` ##### 4.4.7 多对一删除数据 > cn.kgc.tangcco.dao.StudentDaoTest ```java @Test @Transactional(readOnly = true) public void test02() { studentDao.deleteById(1); System.out.println("程序执行结束"); } ``` 从log日志和数据库结果来看,删除多方先验证数据是否存在,如果数据存在再删除,不会影响关联表中的数据 ```sql Hibernate: select student0_.id as id1_4_0_, student0_.auth_text as auth_tex2_4_0_, student0_.cid as cid3_4_0_, student0_.first_name as first_na4_4_0_, student0_.id_card as id_card5_4_0_, student0_.last_name as last_nam6_4_0_, student0_.mobile as mobile7_4_0_, student0_.uuid as uuid8_4_0_ from student student0_ where student0_.id=? 程序执行结束 Hibernate: delete from student where id=? ``` ##### 4.4.8 一对多删除数据之孤儿删除 > cn.kgc.tangcco.dao.ClazzDaoTest > > 孤儿删除即删除主表中数据的同时删除从表中与之关联的数据 ```java @Test @Transactional() @Rollback(value = false) public void test04() { clazzDao.deleteById(1); System.out.println("程序执行结束"); } ``` 从log日志和数据结果看出删除主表先查出该条数据和与之关联的数据,先删除与之关联的数据后再删除自己 ```sql Hibernate: select clazz0_.id as id1_0_0_, clazz0_.name as name2_0_0_ from clazz clazz0_ where clazz0_.id=? Hibernate: select students0_.cid as cid3_4_0_, students0_.id as id1_4_0_, students0_.id as id1_4_1_, students0_.auth_text as auth_tex2_4_1_, students0_.cid as cid3_4_1_, students0_.first_name as first_na4_4_1_, students0_.id_card as id_card5_4_1_, students0_.last_name as last_nam6_4_1_, students0_.mobile as mobile7_4_1_, students0_.uuid as uuid8_4_1_ from student students0_ where students0_.cid=? 程序执行结束 Hibernate: delete from student where id=? Hibernate: delete from student where id=? Hibernate: delete from student where id=? Hibernate: delete from student where id=? Hibernate: delete from student where id=? Hibernate: delete from student where id=? Hibernate: delete from student where id=? Hibernate: delete from student where id=? Hibernate: delete from student where id=? Hibernate: delete from clazz where id=? ``` ##### 4.4.9 一对多删除数据但不影响从表数据 > 思路: 先解除从表中与主表中该条记录有关联的关联关系, > > 方式一: 将外键字段值置空 > > 方式二: 将关联数据与其他主表中的数据关联 > > 本示例代码采用的方式一 ###### 4.4.9.1 一对多删除数据但不影响从表数据(示例一) > cn.kgc.tangcco.dao.ClazzDaoTest > > 从表中查出一条就解除一条 ```java @Test @Transactional() @Rollback(value = false) public void test05() { Optional optional = clazzDao.findById(2); if (!optional.isEmpty()) { Clazz clazz = optional.get(); List students = studentDao.findAll((root, cq, cb) -> { return cb.equal(root.get("cid"), clazz.getId()); }); students.forEach(student -> { student.setCid(null); studentDao.saveAndFlush(student); }); clazzDao.deleteById(clazz.getId()); } System.out.println("程序执行结束"); } ``` 从log日志和查询结果可以看出, 查询到主表记录和与之关联的从表记录后先update了从表数据与主表解除关联关系 然后再删除主表中记录 ```sql Hibernate: select clazz0_.id as id1_0_0_, clazz0_.name as name2_0_0_ from clazz clazz0_ where clazz0_.id=? Hibernate: select student0_.id as id1_4_, student0_.auth_text as auth_tex2_4_, student0_.cid as cid3_4_, student0_.first_name as first_na4_4_, student0_.id_card as id_card5_4_, student0_.last_name as last_nam6_4_, student0_.mobile as mobile7_4_, student0_.uuid as uuid8_4_ from student student0_ where student0_.cid=2 Hibernate: update student set auth_text=?, cid=?, first_name=?, id_card=?, last_name=?, mobile=?, uuid=? where id=? Hibernate: update student set auth_text=?, cid=?, first_name=?, id_card=?, last_name=?, mobile=?, uuid=? where id=? Hibernate: update student set auth_text=?, cid=?, first_name=?, id_card=?, last_name=?, mobile=?, uuid=? where id=? Hibernate: update student set auth_text=?, cid=?, first_name=?, id_card=?, last_name=?, mobile=?, uuid=? where id=? Hibernate: update student set auth_text=?, cid=?, first_name=?, id_card=?, last_name=?, mobile=?, uuid=? where id=? Hibernate: update student set auth_text=?, cid=?, first_name=?, id_card=?, last_name=?, mobile=?, uuid=? where id=? Hibernate: select students0_.cid as cid3_4_0_, students0_.id as id1_4_0_, students0_.id as id1_4_1_, students0_.auth_text as auth_tex2_4_1_, students0_.cid as cid3_4_1_, students0_.first_name as first_na4_4_1_, students0_.id_card as id_card5_4_1_, students0_.last_name as last_nam6_4_1_, students0_.mobile as mobile7_4_1_, students0_.uuid as uuid8_4_1_ from student students0_ where students0_.cid=? 程序执行结束 Hibernate: delete from clazz where id=? ``` ![image-20210118124921289](/image-20210118124921289.png) ###### 4.4.9.2 一对多删除数据但不影响从表数据(示例二) > cn.kgc.tangcco.dao.ClazzDaoTest > > 从表中查出所有记录后再解除关系 ```java @Test @Transactional() @Rollback(value = false) public void test06() { Optional optional = clazzDao.findById(3); if (!optional.isEmpty()) { Clazz clazz = optional.get(); List students = studentDao.findAll((root, cq, cb) -> { return cb.equal(root.get("cid"), clazz.getId()); }); List studentList = students.stream().map(student -> student.setCid(null)).collect(Collectors.toList()); studentList = studentDao.saveAll(studentList); studentDao.flush(); clazzDao.deleteById(clazz.getId()); } System.out.println("程序执行结束"); } ``` 从log日志和查询结果可以看出,虽然是查询所有记录后一次性解除关系但程序执行仍然是一条一条执行的 ```sql Hibernate: select clazz0_.id as id1_0_0_, clazz0_.name as name2_0_0_ from clazz clazz0_ where clazz0_.id=? Hibernate: select student0_.id as id1_4_, student0_.auth_text as auth_tex2_4_, student0_.cid as cid3_4_, student0_.first_name as first_na4_4_, student0_.id_card as id_card5_4_, student0_.last_name as last_nam6_4_, student0_.mobile as mobile7_4_, student0_.uuid as uuid8_4_ from student student0_ where student0_.cid=3 Hibernate: update student set auth_text=?, cid=?, first_name=?, id_card=?, last_name=?, mobile=?, uuid=? where id=? Hibernate: update student set auth_text=?, cid=?, first_name=?, id_card=?, last_name=?, mobile=?, uuid=? where id=? Hibernate: update student set auth_text=?, cid=?, first_name=?, id_card=?, last_name=?, mobile=?, uuid=? where id=? Hibernate: update student set auth_text=?, cid=?, first_name=?, id_card=?, last_name=?, mobile=?, uuid=? where id=? Hibernate: update student set auth_text=?, cid=?, first_name=?, id_card=?, last_name=?, mobile=?, uuid=? where id=? Hibernate: select students0_.cid as cid3_4_0_, students0_.id as id1_4_0_, students0_.id as id1_4_1_, students0_.auth_text as auth_tex2_4_1_, students0_.cid as cid3_4_1_, students0_.first_name as first_na4_4_1_, students0_.id_card as id_card5_4_1_, students0_.last_name as last_nam6_4_1_, students0_.mobile as mobile7_4_1_, students0_.uuid as uuid8_4_1_ from student students0_ where students0_.cid=? 程序执行结束 Hibernate: delete from clazz where id=? ``` ![image-20210118130755773](/image-20210118130755773.png) #### 4.5 多对多 ##### 4.5.1 编写实体类和数据库表的映射配置 ```java package cn.kgc.tangcco.pojo; import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; import javax.persistence.*; import java.io.Serializable; import java.util.List; /** * @author 李昊哲 * @Description * @create 2021/1/16 17:10 */ @NoArgsConstructor @AllArgsConstructor @Getter @Setter @Accessors(chain = true) @Table(name = "course") @Entity @JsonIgnoreProperties(value={"hibernateLazyInitializer","handler","fieldHandler"}) public class Course implements Serializable { private static final long serialVersionUID = 6617915468914088579L; /** * 学科编号 */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Integer id; /** * 学科名称 */ @Column(name = "name") private String name; public Course(String name) { this.name = name; } public Course(String name, List stus) { this.name = name; this.stus = stus; } /** * 该学科内的学生列表 * joinColumns当前类的主键 * inverseJoinColumns关联对象的主键 * mappedBy不可与JoinTable和JoinColumn一同使用 */ // @JsonBackReference @ManyToMany(targetEntity = Stu.class,fetch = FetchType.LAZY) @JoinTable( name = "sc", joinColumns = {@JoinColumn(name = "cid",referencedColumnName = "id")}, inverseJoinColumns = @JoinColumn(name = "sid",referencedColumnName = "id")) private List stus; } ``` ```java package cn.kgc.tangcco.pojo; import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; import javax.persistence.*; import java.io.Serializable; import java.util.List; /** * @author 李昊哲 * @Description * @create 2021/1/16 17:09 */ @NoArgsConstructor @AllArgsConstructor @Getter @Setter @Accessors(chain = true) @Table(name = "stu") @Entity @JsonIgnoreProperties(value = {"hibernateLazyInitializer", "handler", "fieldHandler"}) public class Stu implements Serializable { private static final long serialVersionUID = -2873266564174314116L; /** * 学生编号 */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Integer id; /** * 学生姓名 */ @Column(name = "nickname") private String nickname; public Stu(String nickname) { this.nickname = nickname; } public Stu(String nickname, List courses) { this.nickname = nickname; this.courses = courses; } /** * 该学生所学专业列表 * joinColumns当前类的主键 * inverseJoinColumns关联对象的主键 * mappedBy不可与JoinTable和JoinColumn一同使用 */ @JsonBackReference @ManyToMany(targetEntity = Course.class, fetch = FetchType.LAZY) @JoinTable( name = "sc", joinColumns = {@JoinColumn(name = "sid", referencedColumnName = "id")}, inverseJoinColumns = @JoinColumn(name = "cid", referencedColumnName = "id")) private List courses; } ``` ##### 4.5.2 编写符合spring Data JPA规范的dao层接口 ```java package cn.kgc.tangcco.dao; import cn.kgc.tangcco.pojo.Course; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** * @author 李昊哲 * @Description * @create 2021/1/12 9:26 * JpaRepository T操作实体类泛型 ID实体类中主键属性的泛型 * JpaSpecificationExecutor T操作实体类泛型 */ public interface CourseDao extends JpaRepository, JpaSpecificationExecutor {} ``` ```java package cn.kgc.tangcco.dao; import cn.kgc.tangcco.pojo.Stu; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import java.util.List; /** * @author 李昊哲 * @Description * @create 2021/1/12 9:26 * JpaRepository T操作实体类泛型 ID实体类中主键属性的泛型 * JpaSpecificationExecutor T操作实体类泛型 */ public interface StuDao extends JpaRepository, JpaSpecificationExecutor {} ``` ##### 4.5.3 编写测试类测试接口 在src/test/java 目录下新建测试Class > cn.kgc.tangcco.dao.CourseDaoTest > > cn.kgc.tangcco.dao.StuDaoTest ```java package cn.kgc.tangcco.dao; import cn.kgc.tangcco.pojo.Course; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; import java.util.List; /** * @author 李昊哲 * @Description * @create 2021/1/17 13:43 */ @RunWith(SpringRunner.class) @ContextConfiguration(locations = "classpath:spring/applicationContext.xml") public class CourseDaoTest { @Autowired private CourseDao courseDao; @Test public void test01() { List courses = courseDao.findAll(); ObjectMapper objectMapper = new ObjectMapper(); courses.forEach(course -> { try { System.out.println(objectMapper.writeValueAsString(course)); } catch (JsonProcessingException e) { e.printStackTrace(); } }); System.out.println("程序执行结束"); } @Test @Transactional() @Rollback(value = false) public void test11() { courseDao.deleteById(2); System.out.println("程序执行结束"); } } ``` ```java package cn.kgc.tangcco.dao; import cn.binarywang.tools.generator.ChineseNameGenerator; import cn.kgc.tangcco.pojo.Course; import cn.kgc.tangcco.pojo.Stu; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; import java.util.*; import java.util.concurrent.ThreadLocalRandom; /** * @author 李昊哲 * @Description * @create 2021/1/16 18:50 */ @RunWith(SpringRunner.class) @ContextConfiguration(locations = "classpath:spring/applicationContext.xml") public class StuDaoTest { @Autowired private CourseDao courseDao; @Autowired private StuDao stuDao; @Test @Transactional() @Rollback(value = false) public void test01() { Course course = new Course("python"); course = courseDao.saveAndFlush(course); List courses = new ArrayList<>(); courses.add(course); Stu stu = new Stu(ChineseNameGenerator.getInstance().generate(), courses); stu = stuDao.saveAndFlush(stu); System.out.println("程序执行结束"); } @Test @Transactional() @Rollback(value = false) public void test02() { List courses = new ArrayList<>() {{ add(new Course("java")); add(new Course("scala")); }}; List courseList = courseDao.saveAll(courses); courseList = courseDao.findAll(); List stus = new ArrayList<>(); for (int i = 0; i < 10; i++) { Stu stu = new Stu(ChineseNameGenerator.getInstance().generate()); Set set = new HashSet<>(); for (int j = 0; j < ThreadLocalRandom.current().nextInt(1, courseList.size() + 1); j++) { set.add(courseList.get(ThreadLocalRandom.current().nextInt(courseList.size()))); } List stuCourses = new ArrayList<>(); set.forEach(course -> stuCourses.add(course)); stu.setCourses(stuCourses); stus.add(stu); } List stuList = stuDao.saveAll(stus); System.out.println("程序执行结束"); } @Test @Transactional() @Rollback(value = false) public void test03() { Optional stuOptional = stuDao.findById(3); if (!stuOptional.isEmpty()) { // 获取学生对象 Stu stu = stuOptional.get(); // 获取该学生学习学科列表 List stuCourses = stu.getCourses(); // 获取所有学科列表 List courses = courseDao.findAll(); // 从所有学习列表中移除已学习列表 即未学习列表 for (int i = 0; i < stuCourses.size(); i++) { for (int j = 0; j < courses.size(); j++) { if (stuCourses.get(i).getId().equals(courses.get(j).getId())) { courses.remove(j); } } } // 从未学习的学科列表中随机获取一个学科 Course course = courses.get(ThreadLocalRandom.current().nextInt(courses.size())); // 将随机获取的一门学科添加到原来的参加的学科列表 stuCourses.add(course); // 将该学生最新的学科列表存储到学生对象中 stu.setCourses(stuCourses); // 将最新的学习列表更新到数据库中 stuDao.saveAndFlush(stu); } System.out.println("程序执行结束"); } @Test @Transactional() @Rollback(value = false) public void test04() { Optional stuOptional = stuDao.findById(7); if (!stuOptional.isEmpty()) { // 获取学生对象 Stu stu = stuOptional.get(); // 获取该学生学习学科列表 List courses = stu.getCourses(); // 从学习的学科列表中随机移除一个学科 courses.remove(ThreadLocalRandom.current().nextInt(courses.size())); // 将该学生最新的学科列表存储到学生对象中 stu.setCourses(courses); // 将最新的学习列表更新到数据库中 stuDao.saveAndFlush(stu); } System.out.println("程序执行结束"); } @Test @Transactional() @Rollback(value = false) public void test05() { stuDao.deleteById(10); System.out.println("程序执行结束"); } } ``` ##### 4.5.4 多对多新增一条记录 > cn.kgc.tangcco.dao.StuDaoTest ```java @Test @Transactional() @Rollback(value = false) public void test01() { Course course = new Course("python"); course = courseDao.saveAndFlush(course); List courses = new ArrayList<>(); courses.add(course); Stu stu = new Stu(ChineseNameGenerator.getInstance().generate(), courses); stu = stuDao.saveAndFlush(stu); System.out.println("程序执行结束"); } ``` 截取部分log可以看到当数据库中表不存在程序帮我们自动建表并插入数据: ``` Hibernate: create table course (id integer not null auto_increment, name varchar(255), primary key (id)) engine=InnoDB Hibernate: create table sc (sid integer not null, cid integer not null) engine=InnoDB Hibernate: create table stu (id integer not null auto_increment, nickname varchar(255), primary key (id)) engine=InnoDB Hibernate: alter table sc add constraint FKd1pjmscl3id0ilxy3brbgthxd foreign key (cid) references course (id) Hibernate: alter table sc add constraint FK1eo6to1d5idnjba3jkecpao7c foreign key (sid) references stu (id) Hibernate: insert into course (name) values (?) Hibernate: insert into stu (nickname) values (?) Hibernate: insert into sc (sid, cid) values (?, ?) ``` 生成数据 学科表 | id | name | | ---- | ------ | | 1 | python | 关联表 | sid | cid | | ---- | ---- | | 1 | 1 | 学生表 | id | nickname | | ---- | -------- | | 1 | 司马尤笛 | ##### 4.5.5 多对多新增多条记录 > cn.kgc.tangcco.dao.StuDaoTest ```java @Test @Transactional() @Rollback(value = false) public void test02() { List courses = new ArrayList<>() {{ add(new Course("java")); add(new Course("scala")); }}; List courseList = courseDao.saveAll(courses); courseList = courseDao.findAll(); List stus = new ArrayList<>(); for (int i = 0; i < 10; i++) { Stu stu = new Stu(ChineseNameGenerator.getInstance().generate()); Set set = new HashSet<>(); for (int j = 0; j < ThreadLocalRandom.current().nextInt(1, courseList.size() + 1); j++) { set.add(courseList.get(ThreadLocalRandom.current().nextInt(courseList.size()))); } List stuCourses = new ArrayList<>(); set.forEach(course -> stuCourses.add(course)); stu.setCourses(stuCourses); stus.add(stu); } List stuList = stuDao.saveAll(stus); System.out.println("程序执行结束"); } ``` log日志 发现 saveAll方法只是多次调用insert ``` ibernate: insert into course (name) values (?) Hibernate: insert into course (name) values (?) Hibernate: select course0_.id as id1_1_, course0_.name as name2_1_ from course course0_ Hibernate: insert into stu (nickname) values (?) Hibernate: insert into stu (nickname) values (?) Hibernate: insert into stu (nickname) values (?) Hibernate: insert into stu (nickname) values (?) Hibernate: insert into stu (nickname) values (?) Hibernate: insert into stu (nickname) values (?) Hibernate: insert into stu (nickname) values (?) Hibernate: insert into stu (nickname) values (?) Hibernate: insert into stu (nickname) values (?) Hibernate: insert into stu (nickname) values (?) 程序执行结束 Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) ``` 生成数据 学科表 | id | name | | ---- | ------ | | 1 | python | | 2 | java | | 3 | scala | 关联表 | sid | cid | | ---- | ---- | | 1 | 1 | | 2 | 3 | | 2 | 1 | | 3 | 3 | | 4 | 3 | | 4 | 2 | | 5 | 1 | | 6 | 1 | | 7 | 3 | | 7 | 2 | | 7 | 1 | | 8 | 3 | | 9 | 3 | | 9 | 2 | | 9 | 1 | | 10 | 3 | | 11 | 3 | | 11 | 2 | 学生表 | id | nickname | | ---- | -------- | | 1 | 司马尤笛 | | 2 | 陆郁 | | 3 | 杨磨蔑 | | 4 | 方涧 | | 5 | 武蜡 | | 6 | 钱蒲球 | | 7 | 皇甫奸寡 | | 8 | 彭石 | | 9 | 公孙宾胯 | | 10 | 周颜 | | 11 | 叶恋怠 | ##### 4.5.6 学生随机报名一门未报名的学科 > cn.kgc.tangcco.dao.StuDaoTest ```java @Test @Transactional() @Rollback(value = false) public void test03() { Optional stuOptional = stuDao.findById(3); if (!stuOptional.isEmpty()) { // 获取学生对象 Stu stu = stuOptional.get(); // 获取该学生学习学科列表 List stuCourses = stu.getCourses(); // 获取所有学科列表 List courses = courseDao.findAll(); // 从所有学习列表中移除已学习列表 即未学习列表 for (int i = 0; i < stuCourses.size(); i++) { for (int j = 0; j < courses.size(); j++) { if (stuCourses.get(i).getId().equals(courses.get(j).getId())) { courses.remove(j); } } } // 从未学习的学科列表中随机获取一个学科 Course course = courses.get(ThreadLocalRandom.current().nextInt(courses.size())); // 将随机获取的一门学科添加到原来的参加的学科列表 stuCourses.add(course); // 将该学生最新的学科列表存储到学生对象中 stu.setCourses(stuCourses); // 将最新的学习列表更新到数据库中 stuDao.saveAndFlush(stu); } System.out.println("程序执行结束"); } ``` 从log日志看出并不是新增一条记录而是先删除原来的记录然后再将原记录和记录新增 ``` Hibernate: select stu0_.id as id1_3_0_, stu0_.nickname as nickname2_3_0_ from stu stu0_ where stu0_.id=? Hibernate: select course0_.id as id1_1_, course0_.name as name2_1_ from course course0_ Hibernate: select courses0_.sid as sid1_2_0_, courses0_.cid as cid2_2_0_, course1_.id as id1_1_1_, course1_.name as name2_1_1_ from sc courses0_ inner join course course1_ on courses0_.cid=course1_.id where courses0_.sid=? Hibernate: delete from sc where sid=? Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) ``` 新增前后数据对比 | 新增前记录 | 新增后记录 | | :----------------------------------------------------------: | :----------------------------------------------------------: | | ![image-20210118142430055](./img/image-20210118142430055.png) | ![image-20210118142921980](./img/image-20210118142921980.png) | ##### 4.5.7 学生从已报名学科中随机取消报名 > cn.kgc.tangcco.dao.StuDaoTest ```java @Test @Transactional() @Rollback(value = false) public void test04() { Optional stuOptional = stuDao.findById(7); if (!stuOptional.isEmpty()) { // 获取学生对象 Stu stu = stuOptional.get(); // 获取该学生学习学科列表 List courses = stu.getCourses(); // 从学习的学科列表中随机移除一个学科 courses.remove(ThreadLocalRandom.current().nextInt(courses.size())); // 将该学生最新的学科列表存储到学生对象中 stu.setCourses(courses); // 将最新的学习列表更新到数据库中 stuDao.saveAndFlush(stu); } System.out.println("程序执行结束"); } ``` 从log日志看出并不是删除一条记录而是先原来的所有记录然后再将原记录中删除后的新纪录新增 ``` Hibernate: select stu0_.id as id1_3_0_, stu0_.nickname as nickname2_3_0_ from stu stu0_ where stu0_.id=? Hibernate: select courses0_.sid as sid1_2_0_, courses0_.cid as cid2_2_0_, course1_.id as id1_1_1_, course1_.name as name2_1_1_ from sc courses0_ inner join course course1_ on courses0_.cid=course1_.id where courses0_.sid=? Hibernate: delete from sc where sid=? Hibernate: insert into sc (sid, cid) values (?, ?) Hibernate: insert into sc (sid, cid) values (?, ?) ``` 删除前后数据对比 | 新增前记录 | 新增后记录 | | :----------------------------------------------------------: | :----------------------------------------------------------: | | ![image-20210118143637853](./img/image-20210118143637853.png) | ![image-20210118144239077](./img/image-20210118144239077.png) | ##### 4.5.8 删除一名学生孤儿删除与之对应的预报名学科记录但该学科仍然存在 > cn.kgc.tangcco.dao.StuDaoTest ```java @Test @Transactional() @Rollback(value = false) public void test05() { stuDao.deleteById(10); System.out.println("程序执行结束"); } ``` 从log日志和数据记录可以看出,删除一名学生后,与之对应的预报名学科记录亦被删除但该学科仍然存在 ``` Hibernate: select stu0_.id as id1_3_0_, stu0_.nickname as nickname2_3_0_ from stu stu0_ where stu0_.id=? 程序执行结束 Hibernate: delete from sc where sid=? Hibernate: delete from stu where id=? ``` ##### 4.5.9 编写符合spring Data JPA规范的dao层接口 > cn.kgc.tangcco.dao.CourseDaoTest ```java @Test @Transactional() @Rollback(value = false) public void test11() { courseDao.deleteById(2); System.out.println("程序执行结束"); } ``` 从log日志和数据记录可以看出,删除一个学科后,与之对应的预报名该学科的学报名记录亦被删除但该学生仍然存在 ``` Hibernate: select course0_.id as id1_1_0_, course0_.name as name2_1_0_ from course course0_ where course0_.id=? 程序执行结束 Hibernate: delete from sc where cid=? Hibernate: delete from course where id=? ``` ### [05.springboot整合SpringDataJPA](demo05) ### [06.rest repositories](demo06) #### 6.1 新建springboot项目 #### 6.2 maven坐标 ```xml 4.0.0 org.springframework.boot spring-boot-starter-parent 2.4.2 cn.kgc.tangcco demo06 0.0.1-SNAPSHOT war demo06 rest repositories project for Spring Boot 15 1.9.6 0.9.5.5 3.11 1.2.4 5.5.7 1.1.2 5.4.27.Final org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-data-rest org.springframework.boot spring-boot-starter-validation org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-devtools runtime true mysql mysql-connector-java runtime org.springframework.boot spring-boot-configuration-processor true org.projectlombok lombok true org.springframework.boot spring-boot-starter-tomcat provided org.springframework.boot spring-boot-starter-test org.junit.vintage junit-vintage-engine test junit junit 4.13.1 test com.alibaba druid-spring-boot-starter ${druid.version} cn.hutool hutool-all ${hutool-all.version} com.github.binarywang java-testdata-generator ${java-testdata-generator.version} org.springframework.boot spring-boot-maven-plugin org.projectlombok lombok maven-surefire-plugin true ``` #### 6.3 springboot配置文件 ```yaml server: port: 80 servlet: encoding: force: true charset: UTF-8 spring: servlet: multipart: max-file-size: 50MB # 文件上传大小限制为500kb max-request-size: 200MB # 请求大小限制为500kb datasource: url: jdbc:mysql://47.94.130.233:3306/ssm?useUnicode=true&characterEncoding=UTF8&useSSL=true&serverTimeZone=Aisa/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource druid: # 下面为连接池的补充设置,应用到上面所有数据源中 # 初始化大小,最小,最大 initial-size: 5 min-idle: 5 max-active: 200 # 配置获取连接等待超时的时间 max-wait: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 time-between-eviction-runs-millis: 60000 # 配置一个连接在池中最小生存的时间,单位是毫秒 min-evictable-idle-time-millis: 300000 validation-query: SELECT 1 FROM DUAL test-while-idle: true test-on-borrow: false test-on-return: false # 打开PSCache,并且指定每个连接上PSCache的大小 pool-prepared-statements: true # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 max-pool-prepared-statement-per-connection-size: 20 filters: stat,wall,slf4j use-global-data-source-stat: true # 通过connectProperties属性来打开mergeSql功能;慢SQL记录 connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 stat-view-servlet: login-username: admin login-password: 123456 reset-enable: false url-pattern: /druid/* allow: 0.0.0.0 #deny: enabled: true web-stat-filter: url-pattern: /* exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" jpa: database: MySQL database-platform: org.hibernate.dialect.MySQL8Dialect show-sql: true hibernate: ddl-auto: update key: aes: lihaozhelihaozhe des: lihaozhelihaozhelihaozhelihaozhelihaozhelihaozhelihaozhelihaozhe cors: pathPattern: "/**" response: header: # 允许跨域访问资源的访问者的url 注意:若参数值为*时 accessControlAllowCredentials 参数必须为false accessControlAllowOrigin: "*" # 允许跨域的requestMethod accessControlAllowMethods: "POST, GET, PUT, OPTIONS, DELETE" # 预检请求间隔 # 0 表示每次异步请求都发起预检请求,也就是说,发送两次请求。 # 3600 表示隔60分钟才发起预检请求。也就是说,发送两次请求 accessControlAllowMaxAge: "3600" # 接受的 requestHeader包含 accessControlAllowHeaders: "Origin, X-Requested-With, Content-Type, Accept, Authentication, Token" # 客户端是否携带cookie # false 为不允许 # true 为运行 注意:若参数值true时 accessControlAllowOrigin的参数值不能为* 必须指定允许跨域访问资源的访问者的url accessControlAllowCredentials: "false" ``` #### 6.4 编写实体类和数据库表的映射配置 ```java package cn.kgc.tangcco.pojo; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; import javax.persistence.*; import java.io.Serializable; /** * @author 李昊哲 * @Description * @create 2020/10/30 9:23 */ @NoArgsConstructor @AllArgsConstructor @Getter @Setter @Accessors(chain = true) @Table(name = "person") @Entity @JsonIgnoreProperties(value = {"hibernateLazyInitializer", "handler", "fieldHandler"}) public class Person implements Serializable { private static final long serialVersionUID = 934700498843956271L; /** * 学生编号 */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Integer id; /** * 学生唯一标志 */ @Column(name = "uuid") private String uuid; /** * 学生曾用名 */ @Column(name = "first_name") private String firstName; /** * 学生现用名 */ @Column(name = "last_name") private String lastName; /** * 学生身份证 */ @Column(name = "id_card") private String idCard; /** * 学生手机号 */ @Column(name = "mobile") private String mobile; /** * 密码 */ @Column(name = "auth_text") private String authText; public Person(String uuid, String firstName, String lastName, String idCard, String mobile, String authText) { this.uuid = uuid; this.firstName = firstName; this.lastName = lastName; this.idCard = idCard; this.mobile = mobile; this.authText = authText; } } ``` #### 6.5 编写测试类用于数据测试 在src/test/java 目录下新建测试Class > cn.kgc.tangcco.pojo.PersonTest ```java package cn.kgc.tangcco.pojo; import cn.binarywang.tools.generator.ChineseIDCardNumberGenerator; import cn.binarywang.tools.generator.ChineseMobileNumberGenerator; import cn.binarywang.tools.generator.ChineseNameGenerator; import cn.hutool.core.util.IdUtil; import cn.kgc.tangcco.utils.cryptography.BaseCryptographyUtils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; /** * @author 李昊哲 * @Description * @create 2021/1/21 9:38 */ @RunWith(SpringRunner.class) @SpringBootTest public class PersonTest { private final static String key = "lihaozhelihaozhelihaozhelihaozhelihaozhelihaozhelihaozhelihaozhe"; @Test public void mock() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); for (int i = 0; i < 50; i++) { String uuid = IdUtil.fastSimpleUUID(); String firstName = ChineseNameGenerator.getInstance().generate(); String lastName = firstName; String idCard = ChineseIDCardNumberGenerator.getInstance().generate(); String mobile = ChineseMobileNumberGenerator.getInstance().generate(); String authText = BaseCryptographyUtils.desEncodeHexString("123456", key); Person person = new Person(uuid, firstName, lastName, idCard, mobile, authText); System.out.println(objectMapper.writeValueAsString(person)); } } } ``` #### 6.6 编写测试类模拟测试 在src/test/java 目录下新建测试Class > cn.kgc.tangcco.dao.PersonDaoTest ```java package cn.kgc.tangcco.dao; import cn.binarywang.tools.generator.ChineseIDCardNumberGenerator; import cn.binarywang.tools.generator.ChineseMobileNumberGenerator; import cn.binarywang.tools.generator.ChineseNameGenerator; import cn.hutool.core.util.IdUtil; import cn.kgc.tangcco.pojo.Person; import cn.kgc.tangcco.utils.cryptography.BaseCryptographyUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; import java.util.stream.Stream; /** * @author 李昊哲 * @Description * @create 2021/1/21 9:27 */ @RunWith(SpringRunner.class) @SpringBootTest public class PersonDaoTest { private final static String key = "lihaozhelihaozhelihaozhelihaozhelihaozhelihaozhelihaozhelihaozhe"; @Autowired private PersonDao personDao; @Test @Transactional @Rollback(value = false) public void mock() { for (int i = 0; i < 5; i++) { String uuid = IdUtil.fastSimpleUUID(); String firstName = ChineseNameGenerator.getInstance().generate(); String lastName = firstName; String idCard = ChineseIDCardNumberGenerator.getInstance().generate(); String mobile = ChineseMobileNumberGenerator.getInstance().generate(); String authText = BaseCryptographyUtils.desEncodeHexString("123456", key); Person person = new Person(uuid, firstName, lastName, idCard, mobile, authText); personDao.save(person); } } } ``` ![模拟数据](./img/image-20210121103533108.png) #### 6.7 查询所有 PonsMan测试 ​ 请求地址:http://localhost/person ​ 请求方式:GET ​ 请求参数:无 返回结果 ```json { "_embedded": { "person": [ { "uuid": "c010c4caa7234ffd9a95ffab574b784e", "firstName": "胡脊", "lastName": "胡脊", "idCard": "370286199407137769", "mobile": "18705106356", "authText": "1f4a79344c3e1f58", "_links": { "self": { "href": "http://localhost/person/1" }, "person": { "href": "http://localhost/person/1" } } }, { "uuid": "494fb4658ff9445cb91411ff9d93535b", "firstName": "叶坑侍", "lastName": "叶坑侍", "idCard": "214494198207186975", "mobile": "17797067602", "authText": "1f4a79344c3e1f58", "_links": { "self": { "href": "http://localhost/person/2" }, "person": { "href": "http://localhost/person/2" } } }, { "uuid": "7f3f259dbf2e4a8496df7314c2b867de", "firstName": "邹泣", "lastName": "邹泣", "idCard": "635882197401176173", "mobile": "15689785773", "authText": "1f4a79344c3e1f58", "_links": { "self": { "href": "http://localhost/person/3" }, "person": { "href": "http://localhost/person/3" } } }, { "uuid": "b50b145314564bc0abe5fc7db274b8ab", "firstName": "彭瘦", "lastName": "彭瘦", "idCard": "501629198301317476", "mobile": "13120128810", "authText": "1f4a79344c3e1f58", "_links": { "self": { "href": "http://localhost/person/4" }, "person": { "href": "http://localhost/person/4" } } }, { "uuid": "2fb54b0a70814aefb3cf4a8c979865ef", "firstName": "赵窍保", "lastName": "赵窍保", "idCard": "237550197808026695", "mobile": "15981404261", "authText": "1f4a79344c3e1f58", "_links": { "self": { "href": "http://localhost/person/5" }, "person": { "href": "http://localhost/person/5" } } } ] }, "_links": { "self": { "href": "http://localhost/person" }, "profile": { "href": "http://localhost/profile/person" } }, "page": { "size": 20, "totalElements": 5, "totalPages": 1, "number": 0 } } ``` #### 6.8 分页查询 > 注意:page 的值从0开始,即当前页面号要减一 > > page 查询页码索引 PonsMan测试 ​ 请求地址:http://localhost/person ​ 请求方式:GET ​ 请求参数: | key | value | | ---- | ------- | | page | 0 | | size | 2 | | sort | id,desc | 返回结果 ```json { "_embedded": { "person": [ { "uuid": "2fb54b0a70814aefb3cf4a8c979865ef", "firstName": "赵窍保", "lastName": "赵窍保", "idCard": "237550197808026695", "mobile": "15981404261", "authText": "1f4a79344c3e1f58", "_links": { "self": { "href": "http://localhost/person/5" }, "person": { "href": "http://localhost/person/5" } } }, { "uuid": "b50b145314564bc0abe5fc7db274b8ab", "firstName": "彭瘦", "lastName": "彭瘦", "idCard": "501629198301317476", "mobile": "13120128810", "authText": "1f4a79344c3e1f58", "_links": { "self": { "href": "http://localhost/person/4" }, "person": { "href": "http://localhost/person/4" } } } ] }, "_links": { "first": { "href": "http://localhost/person?page=0&size=2&sort=id,desc" }, "self": { "href": "http://localhost/person?page=0&size=2&sort=id,desc" }, "next": { "href": "http://localhost/person?page=1&size=2&sort=id,desc" }, "last": { "href": "http://localhost/person?page=2&size=2&sort=id,desc" }, "profile": { "href": "http://localhost/profile/person" } }, "page": { "size": 2, "totalElements": 5, "totalPages": 3, "number": 0 } } ``` #### 6.9 根据ID查询 PonsMan测试 ​ 请求地址:http://localhost/person/1 ​ 请求方式:GET ​ 请求参数:无 返回结果 ```json { "uuid": "c010c4caa7234ffd9a95ffab574b784e", "firstName": "胡脊", "lastName": "胡脊", "idCard": "370286199407137769", "mobile": "18705106356", "authText": "1f4a79344c3e1f58", "_links": { "self": { "href": "http://localhost/person/1" }, "person": { "href": "http://localhost/person/1" } } } ``` #### 6.10 根据ID删除 PonsMan测试 ​ 请求地址:http://localhost/person/1 ​ 请求方式:DELETE ​ 请求参数:无 无返回结果 #### 6.11 根据ID修改 PonsMan测试 ​ 请求地址:http://localhost/person/1 ​ 请求方式:PUT ​ 请求参数: ```json { "uuid": "156fbadaebd64e38a1030d1ba0d78032", "firstName": "丁漾", "lastName": "丁漾", "idCard": "716409198202095318", "mobile": "13263850202", "authText": "1f4a79344c3e1f58" } ``` 返回结果 ```json { "uuid": "156fbadaebd64e38a1030d1ba0d78032", "firstName": "丁漾", "lastName": "丁漾", "idCard": "716409198202095318", "mobile": "13263850202", "authText": "1f4a79344c3e1f58", "_links": { "self": { "href": "http://localhost/person/1" }, "person": { "href": "http://localhost/person/1" } } } ``` #### 6.12 新增 PonsMan测试 ​ 请求地址:http://localhost/person ​ 请求方式:PUT ​ 请求参数: | me | value | | --------- | -------------------------------- | | uuid | 333a5cd5f6204913a7fe20c1f8c9983c | | firstName | 西门见佬 | | lastName | 西门见佬 | | idCard | 116328198202130285 | | mobile | 15294780377 | | authText | 1f4a79344c3e1f58 | ``` { "uuid": "333a5cd5f6204913a7fe20c1f8c9983c", "firstName": "西门见佬", "lastName": "西门见佬", "idCard": "116328198202130285", "mobile": "15294780377", "authText": "1f4a79344c3e1f58" } ```