源码
关于SpringDataJPA中@Column里面字段报红的处理方法
一、概述 1.1 概念 Spring Data JPA是Spring基于ORM框架、JPA规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码是实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展,使用Spring Data JPA可以极大地提高开发效率。
Spring Data JPA让我们解脱了dao层的操作,基本上所有的CRUD都可以依赖它来实现,在实际的工作中,使用Spring Data JPA+ORM(如Hibernate)完成操作,这样在切换不同的ORM时,提供了极大的方便,同时也使数据库层操作更加简单、方便解耦。
1.2 关系 JPA是一套规范,内部是由接口和抽象类组成的。
Hibernate是一套成熟的ORM框架,而且Hibernate实现了JPA规范,所以也称Hibernate为JPA的一种实现方式,我们使用JPA的api进行编程,就意味着,要站在更高的角度上看问题,即面向接口编程。
Spring Data JPA是Spring提供的一套对JPA操作更加高级的封装,是在JPA规范下的专门的用来进行数据持久化的解决方案
二、入门操作 2.1 搭建环境 步骤
创建工程导入坐标 配置spring的配置文件,添加log4j日志配置文件 编写实体类Customer,使用jpa注解配置映射关系 编写dao层接口,不需要实现类 创建工程导入坐标 pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 <?xml version="1.0" encoding="UTF-8"?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > top.meethigher</groupId > <artifactId > SpringDataJPA</artifactId > <version > 1.0</version > <properties > <spring.version > 5.3.4</spring.version > <mysql.version > 8.0.12</mysql.version > <hibernate.version > 5.4.30.Final</hibernate.version > <log4j.version > 1.2.17</log4j.version > <c3p0.version > 0.9.1.2</c3p0.version > <slf4j.version > 1.7.25</slf4j.version > </properties > <dependencies > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13</version > <scope > test</scope > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.9.6</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-aop</artifactId > <version > ${spring.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > ${spring.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context-support</artifactId > <version > ${spring.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-orm</artifactId > <version > ${spring.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-beans</artifactId > <version > ${spring.version}</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-core</artifactId > <version > ${spring.version}</version > </dependency > <dependency > <groupId > org.hibernate</groupId > <artifactId > hibernate-core</artifactId > <version > ${hibernate.version}</version > </dependency > <dependency > <groupId > org.hibernate</groupId > <artifactId > hibernate-entitymanager</artifactId > <version > ${hibernate.version}</version > </dependency > <dependency > <groupId > org.hibernate</groupId > <artifactId > hibernate-validator</artifactId > <version > 6.2.0.Final</version > </dependency > <dependency > <groupId > c3p0</groupId > <artifactId > c3p0</artifactId > <version > ${c3p0.version}</version > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > ${log4j.version}</version > </dependency > <dependency > <groupId > org.slf4j</groupId > <artifactId > slf4j-api</artifactId > <version > ${slf4j.version}</version > </dependency > <dependency > <groupId > org.slf4j</groupId > <artifactId > slf4j-log4j12</artifactId > <version > ${slf4j.version}</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > ${mysql.version}</version > </dependency > <dependency > <groupId > org.springframework.data</groupId > <artifactId > spring-data-jpa</artifactId > <version > 2.4.7</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-test</artifactId > <version > ${spring.version}</version > </dependency > <dependency > <groupId > javax.el</groupId > <artifactId > javax.el-api</artifactId > <version > 2.2.4</version > </dependency > <dependency > <groupId > org.glassfish.web</groupId > <artifactId > javax.el</artifactId > <version > 2.2.4</version > </dependency > </dependencies > </project >
配置spring的配置文件 spring.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop ="http://www.springframework.org/schema/aop" xmlns:context ="http://www.springframework.org/schema/context" xmlns:jdbc ="http://www.springframework.org/schema/jdbc" xmlns:tx ="http://www.springframework.org/schema/tx" xmlns:jpa ="http://www.springframework.org/schema/data/jpa" xmlns:task ="http://www.springframework.org/schema/task" xsi:schemaLocation =" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd" > <bean id ="entityManagerFactory" class ="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" > <property name ="dataSource" ref ="ds" /> <property name ="packagesToScan" value ="top.meethigher.demo01.domain" /> <property name ="persistenceProvider" > <bean class ="org.hibernate.jpa.HibernatePersistenceProvider" /> </property > <property name ="jpaVendorAdapter" > <bean class ="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" > <property name ="generateDdl" value ="false" /> <property name ="database" value ="MYSQL" /> <property name ="databasePlatform" value ="org.hibernate.dialect.MySQLDialect" /> <property name ="showSql" value ="true" /> </bean > </property > <property name ="jpaDialect" > <bean class ="org.springframework.orm.jpa.vendor.HibernateJpaDialect" /> </property > </bean > <bean id ="ds" class ="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name ="user" value ="root" /> <property name ="password" value ="root" /> <property name ="jdbcUrl" value ="jdbc:mysql://localhost:3306/springdatajpa?useSSL=false& useUnicode=true& characterEncoding=UTF-8& serverTimezone=Asia/Shanghai" /> <property name ="driverClass" value ="com.mysql.cj.jdbc.Driver" /> </bean > <jpa:repositories base-package ="top.meethigher.demo01.dao" transaction-manager-ref ="transactionManager" entity-manager-factory-ref ="entityManagerFactory" /> <bean id ="transactionManager" class ="org.springframework.orm.jpa.JpaTransactionManager" > <property name ="entityManagerFactory" ref ="entityManagerFactory" /> </bean > <context:component-scan base-package ="top.meethigher.demo01" /> </beans >
添加Log4j配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 log4j.rootLogger =ERROR,CONSOLE,logfile log4j.appender.CONSOLE =org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout =org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern =[frame] %d{yyyy-MM-dd HH:mm:ss,SSS} - %-4r %-5p [%t] %C:%L %x - %m%n log4j.appender.logfile =org.apache.log4j.RollingFileAppender log4j.appender.logfile.Encoding =UTF-8 log4j.appender.logfile.File =logs/root.log log4j.appender.logfile.MaxFileSize =10MB log4j.appender.logfile.MaxBackupIndex =3 log4j.appender.logfile.layout =org.apache.log4j.PatternLayout log4j.appender.logfile.layout.ConversionPattern =%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n log4j.logger.club.bagedate =DEBUG,bagedate log4j.additivity.club.bagedate =false log4j.appender.bagedate =org.apache.log4j.RollingFileAppender log4j.appender.bagedate.Encoding =UTF-8 log4j.appender.bagedate.File =logs/bagedate.log log4j.appender.bagedate.MaxFileSize =10MB log4j.appender.bagedate.MaxBackupIndex =3 log4j.appender.bagedate.layout =org.apache.log4j.PatternLayout log4j.appender.bagedate.layout.ConversionPattern =%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n
编写实体类配置映射关系 客户实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 @Entity @Table (name="customer" )public class Customer { @Id @GeneratedValue (strategy = GenerationType.IDENTITY) @Column (name = "cust_id" ) private Long custId; @Column (name = "cust_name" ) private String custName; @Column (name = "cust_source" ) private String custSource; @Column (name = "cust_industry" ) private String custIndustry; @Column (name = "cust_level" ) private String custLevel; @Column (name = "cust_address" ) private String custAddress; @Column (name = "cust_phone" ) private String custPhone; public Long getCustId () { return custId; } public void setCustId (Long custId) { this .custId = custId; } public String getCustName () { return custName; } public void setCustName (String custName) { this .custName = custName; } public String getCustSource () { return custSource; } public void setCustSource (String custSource) { this .custSource = custSource; } public String getCustIndustry () { return custIndustry; } public void setCustIndustry (String custIndustry) { this .custIndustry = custIndustry; } public String getCustLevel () { return custLevel; } public void setCustLevel (String custLevel) { this .custLevel = custLevel; } public String getCustAddress () { return custAddress; } public void setCustAddress (String custAddress) { this .custAddress = custAddress; } public String getCustPhone () { return custPhone; } public void setCustPhone (String custPhone) { this .custPhone = custPhone; } @Override public String toString () { return "Customer{" + "custId=" + custId + ", custName='" + custName + '\'' + ", custSource='" + custSource + '\'' + ", custIndustry='" + custIndustry + '\'' + ", custLevel='" + custLevel + '\'' + ", custAddress='" + custAddress + '\'' + ", custPhone='" + custPhone + '\'' + '}' ; } }
编写dao层接口 dao层接口 规范
需要继承两个接口JpaRepository<操作的实体类类型,实体类中主键属性的类型>封装了基本CRUD操作findById:通过id查询,获取到的对象需要通过.get()再获取 save:保存或者更新。如果没有id,或者有id但是数据库里没有,都是执行不带id的插入操作。如果数据库里有了id就会进行更新 deleteById:通过id删除 findAll:查找所有 JpaSpecificationExecutor<操作的实体类类型> 提供相应的泛型 注意是接口继承了两个接口
1 2 public interface CustomerDao extends JpaRepository <Customer ,Long >, JpaSpecificationExecutor <Customer > {}
2.2 CRUD基本操作 测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 @RunWith (SpringJUnit4ClassRunner.class )//声明spring 提供的单元测试环境 @ContextConfiguration (locations = "classpath:spring.xml" )public class CustomerDaoTest { @Autowired private CustomerDao customerDao; @Test public void testFindById () { Optional<Customer> byId = customerDao.findById(3L ); if (byId.isPresent()){ System.out.println(byId.get()); } } @Test public void testSave () { Customer customer = new Customer(); customer.setCustId(1L ); customer.setCustName("雅妃" ); customerDao.save(customer); } @Test public void testDeleteById () { customerDao.deleteById(1L ); } @Test public void testFindAll () { List<Customer> list = customerDao.findAll(); list.forEach(System.out::println); } }
三、运行过程和原理剖析 3.1 分析 CustomerDao只是一个接口。
真正发挥作用的应该是接口的实现类,在执行过程中自动动态生成接口的实现类对象。
如何实现的?动态代理!
可以通过debug的模式来看
3.2 执行流程 当程序执行的时候,会通过JdkDynamicAopProxy的invoke方法,对customerDao对象生成动态代理对象。根据对Spring Data JPA介绍而知,要想进行findOne查询方法,最终还是会出现JPA规范的API完成操作,那么这些底层代码存在于何处呢?
答案很简单,都隐藏在通过JdkDynamicAopProxy生成的动态代理对象当中,而这个动态代理对象就是SimpleJpaRepository
通过SimpleJpaRepository的源码分析,定位到了findOne方法,在此方法中,返回em.find()的返回结果,那么em又是什么呢?
我们发现em就是EntityManager对象,而他是JPA原生的实现方式,所以我们得到结论Spring Data JPA只是对标准JPA操作进行了进一步封装,简化了Dao层代码的开发
总结
通过JdkDynamicAopProxy的invoke创建一个动态代理对象 SimpleJpaRepository当中封装了JPA的操作(借助JPA的Api完成数据库的CRUD) 通过Hibernate完成数据库操作(封装了JDBC) 四、复杂查询 4.1 Count、Exist、GetOne 注意:因为GetOne底层实现了getReference,是懒加载。所以需要加注解@Transactional保证正常运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Test public void testCount () { long count = customerDao.count(); System.out.println(count); } @Test public void testExist () { boolean b = customerDao.existsById(4L ); System.out.println(b); } @Test @Transactional public void testGetOne () { Customer one = customerDao.getOne(4L ); System.out.println(one); }
4.2 JPQL查询 JPQL:JPA Query Language,jpa查询语言
特点:语法或者关键字和SQL语句类似,但是查询的是类和类中的属性
完成JPQL查询,步骤分两步
在接口上添加对应方法 在方法上使用注解的形式配置JPQL语句 @Query:代表当前的操作是个查询操作 对于多个占位符的
默认情况,占位符位置需要和方法参数的位置保持一致 指定占位符参数的位置 占位符从?1
开始 1 2 @Query (value = "from Customer where custName=?1 and custId=?2" )Customer findCustomerNameAndId (String name,Long id) ;
更新操作
@Modifying:代表当前的操作是更新操作 @Transactional:添加事务的支持,默认会执行结束之后,回滚事务 @Rollback:设置是否自动回滚,true或false CustomerDao
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public interface CustomerDao extends JpaRepository <Customer ,Long >, JpaSpecificationExecutor <Customer > { @Query (value = "from Customer where custName =?1" ) Customer findJpql (String custName) ; @Query (value = "from Customer where custName=?2 and custId=?1" ) Customer findCustomerNameAndId (Long id,String name) ; @Modifying @Query ("update Customer set custName=?2 where custId=?1" ) void updateNameById (Long id,String name) ; }
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @RunWith (SpringJUnit4ClassRunner.class )//声明spring 提供的单元测试环境 @ContextConfiguration (locations = "classpath:spring.xml" )public class JpqlTest { @Autowired private CustomerDao customerDao; @Test public void testFindJpql () { Customer customer = customerDao.findJpql("美杜莎" ); System.out.println(customer); } @Test public void testFindCustomerNameAndId () { Customer customer = customerDao.findCustomerNameAndId(4L ,"美杜莎" ); System.out.println(customer); } @Test @Transactional @Rollback (value=false ) public void testUpdateNameById () { customerDao.updateNameById(4L , "小舞" ); Customer customer = customerDao.findById(4L ).get(); System.out.println(customer); } }
4.3 SQL语句的查询 步骤跟JPQL差不多
完成这种SQL语句的查询,步骤分两步
在接口上添加对应方法 在方法上使用注解的形式配置JPQL语句 @Query:代表当前的操作是个查询操作value:可以是JPQL语句,也可以是SQL语句 nativeQuerytrue:使用本地查询,sql查询 false:默认值,使用jpql查询 SQL的占位符赋值方式跟JPQL是一样的,从?1
开始 CustomerDao
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public interface CustomerDao extends JpaRepository <Customer ,Long >, JpaSpecificationExecutor <Customer > { @Query (value="select * from customer" ,nativeQuery = true ) List<Object[]> findSql(); @Query (value="select * from customer where cust_name like ?1" ,nativeQuery = true ) List<Object[]> findSqlByName(String name); }
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @RunWith (SpringJUnit4ClassRunner.class )//声明spring 提供的单元测试环境 @ContextConfiguration (locations = "classpath:spring.xml" )public class JpqlTest { @Autowired private CustomerDao customerDao; @Test public void testFindSql () { List<Object[]> sql = customerDao.findSql(); for (Object[] obj : sql) { System.out.println(Arrays.toString(obj)); } } @Test public void testFindSqlByName () { List<Object[]> sqlByName = customerDao.findSqlByName("%美%" ); for (Object[] obj : sqlByName) { System.out.println(Arrays.toString(obj)); } } }
4.4 方法名称规则查询 方法名称规则查询是对jpql查询,更加深入的一层封装,我们只需要按照SpringDataJPA提供的方法名称规则定义方法,不需要再去配置JPQL语句完成查询。
方法名的约定
findBy:查询
findBy+属性名称(首字母要大写):精准查询 如findByCustName,是根据客户名称查询。在SpringDataJPA的运行阶段,会根据方法名称进行解析成from 实体类 where custName=?1
findBy+属性名称+查询方式(Like、IsNull、Null、NotNull、IsNotNull) findBy+属性名称+查询方式+多条件连接符(And、Or)+属性名称+查询方式 CustomerDao
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package top.meethigher.demo01.dao;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 org.springframework.transaction.annotation.Transactional;import top.meethigher.demo01.domain.Customer;import java.util.List;public interface CustomerDao extends JpaRepository <Customer ,Long >, JpaSpecificationExecutor <Customer > { Customer findByCustName (String name) ; List<Customer> findByCustNameLike (String name) ; List<Customer> findByCustNameLikeAndCustAddress (String name,String address) ; }
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @RunWith (SpringJUnit4ClassRunner.class )//声明spring 提供的单元测试环境 @ContextConfiguration (locations = "classpath:spring.xml" )public class JpqlTest { @Autowired private CustomerDao customerDao; @Test public void testFindByCustName () { Customer customer = customerDao.findByCustName("小舞" ); System.out.println(customer); } @Test public void testFindByCustNameLike () { List<Customer> 小 = customerDao.findByCustNameLike("%小%" ); System.out.println(小 ); } @Test public void testFindByCustNameLikeAndCustAddress () { List<Customer> 斗破苍穹 = customerDao.findByCustNameLikeAndCustAddress("%小%" , "斗破苍穹" ); System.out.println(斗破苍穹); } }
4.5 Specification动态查询 基本使用 方法列表
查询单个对象:OptionalfindOne(@Nullable Specificationvar1) 查询列表:ListfindAll(@Nullable Specificationvar1); 通过分页查询全部:PagefindAll(@Nullable Specificationvar1, Pageable var2);Pageable:分页参数PageRequest是Pageable的实现类,PageRequest.of(int,int)构造函数第一个参数:当前的页数(从0开始) 构造函数第二个参数:每页的查询数量 Page:分页pageBean 通过排序的方式查询全部:ListfindAll(@Nullable Specificationvar1, Sort var2); 统计查询:long count(@Nullable Specificationvar1); Specification是一个接口,里面有一个方法比较重要,也就是下面这个。
1 2 3 4 5 6 7 8 9 10 11 new Specification<要查询的实体的对象>() { @Override public Predicate toPredicate (Root<Customer> root, CriteriaQuery<?> cq, CriteriaBuilder cb) { return null ; } };
自定义查询条件的步骤
实现Specification接口,提供的泛型是查询对象的类型 实现toPredicate方法,构造查询条件 需要借助方法参数中的两个参数Root:获取需要查询的对象属性 CriteriaQuery:顶层查询对象,自定义查询方式(一般不用) CriteriaBuilder:构造查询条件的,内部封装了很多的查询条件(模糊匹配,精准匹配)and:以and的形式拼接多个查询条件 or:以or的形式拼接多个查询条件 gt:大于,greaterThan lt:小于,lessThan ge:大于等于,greaterThanOrEqualTo le:小于等于,lessThanOrEqualTo 如果以上在传参时,无法比较,可将参数.as(比较的类型的字节码)
即可。比如cb.like(root.get("name").as(String.class),"%1%")
CustomerDao
1 2 public interface CustomerDao extends JpaRepository <Customer ,Long >, JpaSpecificationExecutor <Customer > {}
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 @RunWith (SpringJUnit4ClassRunner.class ) @ContextConfiguration (locations = "classpath:spring.xml" )public class SpecTest { @Autowired private CustomerDao customerDao; @Test public void testFindOne () { Specification<Customer> specification = new Specification<Customer>() { @Override public Predicate toPredicate (Root<Customer> root, CriteriaQuery<?> cq, CriteriaBuilder cb) { Path<Object> custId = root.get("custId" ); Predicate predicate = cb.equal(custId, 4L ); return predicate; } }; Optional<Customer> one = customerDao.findOne(specification); if (one.isPresent()) { System.out.println(one.get()); } } @Test public void testFindByCustNameAndCustIndustry () { List<Customer> all = customerDao.findAll(new Specification<Customer>() { @Override public Predicate toPredicate (Root<Customer> root, CriteriaQuery<?> cq, CriteriaBuilder cb) { Predicate equal = cb.equal(root.get("custName" ), "小舞" ); Predicate equal1 = cb.equal(root.get("custIndustry" ), "蛇人族" ); Predicate and = cb.and(equal, equal1); return and; } }); System.out.println(all); } @Test public void testFindByAddressAndNameOrId () { List<Customer> all = customerDao.findAll(new Specification<Customer>() { @Override public Predicate toPredicate (Root<Customer> root, CriteriaQuery<?> cq, CriteriaBuilder cb) { Predicate custId = cb.equal(root.get("custId" ), 4L ); Predicate custName = cb.equal(root.get("custName" ), "小舞" ); Predicate custAddress = cb.equal(root.get("custAddress" ), "斗破苍穹" ); Predicate or = cb.or(custId, custName); return cb.and(custAddress, or); } }); System.out.println(all); } @Test public void testFindLike () { List<Customer> all = customerDao.findAll(new Specification<Customer>() { @Override public Predicate toPredicate (Root<Customer> root, CriteriaQuery<?> cq, CriteriaBuilder cb) { Path<Object> objectPath = root.get("custName" ); Predicate custName = cb.like(objectPath.as(String.class), "%小%"); return custName; } }); System.out.println(all); } @Test public void testSort () { Sort orders = Sort.by(Sort.Direction.DESC, "custId" ); List<Customer> all = customerDao.findAll(orders); all.forEach(System.out::println); } @Test public void testPage () { Specification<Customer> specification = new Specification<Customer>() { @Override public Predicate toPredicate (Root<Customer> root, CriteriaQuery<?> cq, CriteriaBuilder cb) { return null ; } }; Sort custId = Sort.by("custId" ).descending(); Pageable page = PageRequest.of(0 , 2 , custId); Page<Customer> all = customerDao.findAll(specification, page); System.out.println(all.getTotalElements()); System.out.println(all.getTotalPages()); System.out.println(all.getContent()); } }
扩展查询 简单记录一个最平常不过的写法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 private Specification<FenceInfo> queryByPageSpecification (QueryFencePageRequest request) { String keywords = request.getKeywords(); String status = request.getStatus(); return new Specification<FenceInfo>() { @Override public Predicate toPredicate (Root<FenceInfo> root, CriteriaQuery<?> cq, CriteriaBuilder cb) { List<Predicate> list=new LinkedList<>(); if (!ObjectUtils.isEmpty(status)) { Predicate statusEqual = cb.equal(root.get("status" ), status); list.add(statusEqual); } if (!ObjectUtils.isEmpty(keywords)) { Predicate nameLike = cb.like(root.get("name" ), "%" + keywords + "%" ); Predicate codeEqual = cb.equal(root.get("code" ), keywords); Predicate or = cb.or(nameLike, codeEqual); list.add(or); } cq.orderBy(cb.asc(root.get("type" )),cb.asc(root.get("createTime" ))); Predicate[] predicates = new Predicate[list.size()]; return cb.and(list.toArray(predicates)); } }; }
五、多表操作 5.1 多表关系 表关系
一对一 一对多一的一方:主表 多的一方:从表 外键:需要在从表上选择一列作为外键,他的取值来源于主表的主键 多对多中间表:中间表中最少由两个字段组成,这两个字段作为外键指向两张表的主键,又组成了联合主键 实体类中的关系
包含关系:可以通过实体类中的包含关系描述表关系 继承关系 分析步骤
明确表关系 确定表关系,也就是描述出来,通过外键描述一对多,通过中间表描述多对多 编写实体类,并且在实体类中,描述表关系(包含关系) 配置映射关系 5.2 一对多或多对一 一对多案例,比如单位和员工。
一个单位有多个员工,一个员工从属于一个单位
单位:包含一个员工的集合
员工:包含一个单位的对象
使用JPA注解配置一对多映射关系
使用注解配置一对多
注意因为mysql5.5之前,是MyISAM引擎,不支持事务,后来MySQ5.5后引入了InnoDB。我一开始MySQL的方言引入的是MySQLDialect,创建表有问题。后来又引入了MySQL8Dialect,注意要与自己数据库相对应,否则会有问题。
配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop ="http://www.springframework.org/schema/aop" xmlns:context ="http://www.springframework.org/schema/context" xmlns:jdbc ="http://www.springframework.org/schema/jdbc" xmlns:tx ="http://www.springframework.org/schema/tx" xmlns:jpa ="http://www.springframework.org/schema/data/jpa" xmlns:task ="http://www.springframework.org/schema/task" xsi:schemaLocation =" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd" > <bean id ="entityManagerFactory" class ="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" > <property name ="dataSource" ref ="ds" /> <property name ="packagesToScan" value ="top.meethigher.demo03.domain" /> <property name ="persistenceProvider" > <bean class ="org.hibernate.jpa.HibernatePersistenceProvider" /> </property > <property name ="jpaVendorAdapter" > <bean class ="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" > <property name ="generateDdl" value ="true" /> <property name ="database" value ="MYSQL" /> <property name ="databasePlatform" value ="org.hibernate.dialect.MySQL8Dialect" /> <property name ="showSql" value ="true" /> </bean > </property > <property name ="jpaDialect" > <bean class ="org.springframework.orm.jpa.vendor.HibernateJpaDialect" /> </property > <property name ="jpaProperties" > <props > <prop key ="hibernate.hbm2ddl.auto" > create</prop > </props > </property > </bean > <bean id ="ds" class ="com.mchange.v2.c3p0.ComboPooledDataSource" > <property name ="user" value ="root" /> <property name ="password" value ="root" /> <property name ="jdbcUrl" value ="jdbc:mysql://localhost:3306/springdatajpa?useSSL=false& useUnicode=true& characterEncoding=UTF-8& serverTimezone=Asia/Shanghai" /> <property name ="driverClass" value ="com.mysql.cj.jdbc.Driver" /> </bean > <jpa:repositories base-package ="top.meethigher.demo03.dao" transaction-manager-ref ="transactionManager" entity-manager-factory-ref ="entityManagerFactory" /> <bean id ="transactionManager" class ="org.springframework.orm.jpa.JpaTransactionManager" > <property name ="entityManagerFactory" ref ="entityManagerFactory" /> </bean > <context:component-scan base-package ="top.meethigher.demo03" /> </beans >
实体类Unit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 @Entity @Table (name="unit" )public class Unit { @Id @GeneratedValue (strategy = GenerationType.IDENTITY) @Column (name = "unit_id" ) private Integer unitId; @Column (name = "unit_name" ) private String unitName; @Column (name="unit_address" ) private String unitAddress; @OneToMany (mappedBy = "unit" ) private Set<Person> persons=new HashSet<>(); public Integer getUnitId () { return unitId; } public void setUnitId (Integer unitId) { this .unitId = unitId; } public String getUnitName () { return unitName; } public void setUnitName (String unitName) { this .unitName = unitName; } public String getUnitAddress () { return unitAddress; } public void setUnitAddress (String unitAddress) { this .unitAddress = unitAddress; } public Set<Person> getPersons () { return persons; } public void setPersons (Set<Person> persons) { this .persons = persons; } }
实体类Person
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 @Entity @Table (name = "person" )public class Person { @Id @Column (name="p_id" ) @GeneratedValue (strategy = GenerationType.IDENTITY) private Integer pId; @Column (name="person_name" ) private String personName; @Column (name="person_address" ) private String personAddress; @ManyToOne (targetEntity = Unit.class ,cascade = CascadeType.ALL) @JoinColumn (name = "p_u_id" ,referencedColumnName = "unit_Id" ) private Unit unit; public Integer getpId () { return pId; } public void setpId (Integer pId) { this .pId = pId; } public String getPersonName () { return personName; } public void setPersonName (String personName) { this .personName = personName; } public String getPersonAddress () { return personAddress; } public void setPersonAddress (String personAddress) { this .personAddress = personAddress; } public Unit getUnit () { return unit; } public void setUnit (Unit unit) { this .unit = unit; } }
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 @RunWith (SpringJUnit4ClassRunner.class ) @ContextConfiguration (locations = "classpath:spring.xml" )public class OneToManyTest { @Autowired private PersonDao personDao; @Autowired private UnitDao unitDao; @Test @Transactional @Rollback (false ) public void testAdd1 () { Unit unit = new Unit(); unit.setUnitName("小米科技" ); unit.setUnitAddress("北京" ); Person person = new Person(); person.setPersonName("雷军" ); person.setPersonAddress("北京" ); unit.getPersons().add(person); unitDao.save(unit); personDao.save(person); } @Test @Transactional @Rollback (false ) public void testAdd2 () { Unit unit = new Unit(); unit.setUnitName("腾讯科技" ); unit.setUnitAddress("北京" ); Person person = new Person(); person.setPersonName("马化腾" ); person.setPersonAddress("深圳" ); person.setUnit(unit); unitDao.save(unit); personDao.save(person); } @Test @Transactional @Rollback (false ) public void testAdd3 () { Unit unit = new Unit(); unit.setUnitName("腾讯科技" ); unit.setUnitAddress("北京" ); Person person = new Person(); person.setPersonName("马化腾" ); person.setPersonAddress("深圳" ); person.setUnit(unit); unit.getPersons().add(person); unitDao.save(unit); personDao.save(person); } @Test @Transactional @Rollback (false ) public void testCascadeAdd () { Unit unit = new Unit(); unit.setUnitName("腾讯科技" ); unit.setUnitAddress("北京" ); Person person = new Person(); person.setPersonName("马化腾" ); person.setPersonAddress("深圳" ); person.setUnit(unit); unit.getPersons().add(person); personDao.save(person); } @Test @Transactional @Rollback (false ) public void testCascadeRemove () { Optional<Person> byId = personDao.findById(2 ); byId.ifPresent(person -> personDao.delete(byId.get())); } }
5.3 多对多 多对多案例,比如用户和角色。
一个用户可以有多个角色。
一个角色可以有多个用户。
多对多配置
实体类User
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 @Entity @Table (name ="t_user" )public class User { @Id @GeneratedValue (strategy = GenerationType.IDENTITY) @Column (name="user_id" ) private Integer userId; @Column (name="user_name" ) private String userName; @Column (name="user_age" ) private Integer userAge; @ManyToMany (targetEntity = Role.class ,cascade = CascadeType.ALL) @JoinTable (name="user_role" , joinColumns = {@JoinColumn (name ="user_id" ,referencedColumnName = "user_id" )}, inverseJoinColumns = {@JoinColumn (name="role_id" ,referencedColumnName = "role_id" )}) private Set<Role> roles=new HashSet<>(); public Integer getUserId () { return userId; } public void setUserId (Integer userId) { this .userId = userId; } public String getUserName () { return userName; } public void setUserName (String userName) { this .userName = userName; } public Integer getUserAge () { return userAge; } public void setUserAge (Integer userAge) { this .userAge = userAge; } public Set<Role> getRoles () { return roles; } public void setRoles (Set<Role> roles) { this .roles = roles; } }
实体类Role
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @Entity @Table (name = "t_role" )public class Role { @Id @GeneratedValue (strategy = GenerationType.IDENTITY) @Column (name="role_id" ) private Integer roleId; @Column (name="role_name" ) private String roleName; @ManyToMany (mappedBy = "roles" ) private Set<User> users=new HashSet<>(); public Integer getRoleId () { return roleId; } public void setRoleId (Integer roleId) { this .roleId = roleId; } public String getRoleName () { return roleName; } public void setRoleName (String roleName) { this .roleName = roleName; } public Set<User> getUsers () { return users; } public void setUsers (Set<User> users) { this .users = users; } }
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 @RunWith (SpringJUnit4ClassRunner.class ) @ContextConfiguration (locations = "classpath:spring.xml" )public class ManyToManyTest { @Autowired private UserDao userDao; @Autowired private RoleDao roleDao; @Test @Transactional @Rollback (false ) public void testAdd () { User user = new User(); Role role = new Role(); user.setUserName("雅妃" ); role.setRoleName("监察长老" ); role.getUsers().add(user); user.getRoles().add(role); roleDao.save(role); userDao.save(user); } @Test @Transactional @Rollback (false ) public void testCascadeAdd () { User user = new User(); Role role = new Role(); user.setUserName("雅妃" ); role.setRoleName("监察长老" ); user.getRoles().add(role); userDao.save(user); } @Test @Transactional @Rollback (false ) public void testCascadeRemove () { Optional<User> byId = userDao.findById(1 ); byId.ifPresent(user -> userDao.delete(user)); } }
六、对象导航查询 6.1 一查多 对象导航查询:查询一个对象的同时,通过此对象查询他的关联对象。
直接进行查询,如findById(底层是find,立即加载)、getOne(底层是getReference,延迟加载)。这种的是可选的。
但如果是对象导航查询查多的一方时,也就是级联查询查多的一方时,其实默认都是懒加载的 。
如果我们需要单位查个人时立即加载,那我们可以通过修改单位中个人属性的加载机制,如下。
如果出现懒加载no session,可以通过添加@Transaction注解来解决。
no session是因为执行完一次查询后,session已经关闭了,而懒加载需要再次获取session。通过事务,可以让他们共用一个查询。
单位查人
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 @Entity @Table (name="unit" )public class Unit { @Id @GeneratedValue (strategy = GenerationType.IDENTITY) @Column (name = "unit_id" ) private Integer unitId; @Column (name = "unit_name" ) private String unitName; @Column (name="unit_address" ) private String unitAddress; @OneToMany (mappedBy = "unit" ,fetch = FetchType.LAZY) private Set<Person> persons=new HashSet<>(); public Integer getUnitId () { return unitId; } public void setUnitId (Integer unitId) { this .unitId = unitId; } public String getUnitName () { return unitName; } public void setUnitName (String unitName) { this .unitName = unitName; } public String getUnitAddress () { return unitAddress; } public void setUnitAddress (String unitAddress) { this .unitAddress = unitAddress; } public Set<Person> getPersons () { return persons; } public void setPersons (Set<Person> persons) { this .persons = persons; } }
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @RunWith (SpringJUnit4ClassRunner.class ) @ContextConfiguration (locations = "classpath:spring.xml" )public class Test05 { @Autowired private PersonDao personDao; @Autowired private UnitDao unitDao; @Test @Transactional public void testQueryOneToMany () { Unit unit = unitDao.getOne(3 ); Set<Person> persons = unit.getPersons(); System.out.println("测试懒加载" ); System.out.println(unit); System.out.println(persons.size()); } }
6.2 多查一 如果是对象导航查询查一的一方时,也就是级联查询查一的一方时,其实默认都是立即加载的 。
人查单位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 @Entity @Table (name = "person" )public class Person { @Id @Column (name="p_id" ) @GeneratedValue (strategy = GenerationType.IDENTITY) private Integer pId; @Column (name="person_name" ) private String personName; @Column (name="person_address" ) private String personAddress; @ManyToOne (targetEntity = Unit.class ,cascade = CascadeType.ALL,fetch = FetchType.LAZY) @JoinColumn (name = "unit_id" ,referencedColumnName = "unit_Id" ) private Unit unit; public Integer getpId () { return pId; } public void setpId (Integer pId) { this .pId = pId; } public String getPersonName () { return personName; } public void setPersonName (String personName) { this .personName = personName; } public String getPersonAddress () { return personAddress; } public void setPersonAddress (String personAddress) { this .personAddress = personAddress; } public Unit getUnit () { return unit; } public void setUnit (Unit unit) { this .unit = unit; } @Override public String toString () { return "Person{" + "pId=" + pId + ", personName='" + personName + '\'' + ", personAddress='" + personAddress + '\'' + '}' ; } }
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @RunWith (SpringJUnit4ClassRunner.class ) @ContextConfiguration (locations = "classpath:spring.xml" )public class Test05 { @Autowired private PersonDao personDao; @Autowired private UnitDao unitDao; @Test @Transactional public void testQueryManyToOne () { Person one = personDao.getOne(3 ); Unit unit = one.getUnit(); System.out.println("测试懒加载" ); System.out.println(unit); } }
七、实际案例 7.1 筛选查询 以上这种的算是太入门级别的查询了,工作中用到的查询,瞧一瞧!先上原型
这要涉及到开始时间、结束时间、申请状态、项目编号,以及输入框中接口名称和接口名字的模糊查询。
SQL语句应该先判断各项是否为空,为空就是全查,如果不为空,就要用条件来查了。
如果全部为空的情况下,不考虑外连接、内连接的情况下,应该是这样。
1 select * from table where startTime<? and endTime>? and projectCode=? and applyState=? and (code=? or name =?);
而实际中,applyState又是一个靠自己计算出来的值,如果申请人是当前登录用户,那么就是已申请;如果申请人不等于当前用户或者为null那么就不是。这样又多出来一个or。最后的实现时这样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public Specification<InterfaceInfo> queryApplyByPageSpecification (InterfaceApplyQueryRequest request) { return (Specification<InterfaceInfo>) (root, cq, cb) -> { ArrayList<Predicate> predicatesAnd = new ArrayList<>(); predicatesAnd.add(cb.equal(root.get("interfaceState" ), DataStates.USING.code)); Join<InterfaceInfo, Project> join = root.join("projectComm" , JoinType.INNER); if (!ObjectUtils.isEmpty(request.getApplyState())) { if (request.getApplyState() == InterfaceApplyType.NOT_APPLIED.code) { Join<InterfaceInfo, InterfaceApplyInfo> leftJoin = root.join("interfaceApplyInfos" , JoinType.LEFT); Predicate applyUserIdNotEqual = cb.notEqual(leftJoin.get("applyUserId" ), UserUtils.getLogInfo().getId()); Predicate applyUserIdIsNull = cb.isNull(leftJoin.get("applyUserId" )); Predicate notEqualOrIsNull = cb.or(applyUserIdNotEqual, applyUserIdIsNull); predicatesAnd.add(notEqualOrIsNull); Subquery<String> subQuery = cq.subquery(String.class ) ; Root<InterfaceInfo> subRoot = subQuery.from(InterfaceInfo.class ) ; Join<InterfaceInfo, InterfaceApplyInfo> subLeftJoin = subRoot.join("interfaceApplyInfos" , JoinType.INNER); subQuery.select(subRoot.get("interfaceCode" )).where(cb .equal(subLeftJoin.get("applyUserId" ), UserUtils.getLogInfo().getId()), cb.notEqual(subLeftJoin.get("interfaceApplyStatus" ), DataStates.APPROVAL_NO_PASS.code)); predicatesAnd.add(cb.not(cb.in(root.get("interfaceCode" )).value(subQuery))); } if (request.getApplyState() == InterfaceApplyType.APPLIED.code) { Join<InterfaceInfo, InterfaceApplyInfo> leftJoin = root.join("interfaceApplyInfos" , JoinType.LEFT); predicatesAnd.add(cb.equal(leftJoin.get("applyUserId" ), UserUtils.getLogInfo().getId())); predicatesAnd.add(cb.equal(leftJoin.get("interfaceApplyStatus" ), DataStates.USING.code)); } if (request.getApplyState() == InterfaceApplyType.APPLYING.code) { Join<InterfaceInfo, InterfaceApplyInfo> leftJoin = root.join("interfaceApplyInfos" , JoinType.LEFT); predicatesAnd.add(cb.equal(leftJoin.get("applyUserId" ), UserUtils.getLogInfo().getId())); predicatesAnd.add(cb.notEqual(leftJoin.get("interfaceApplyStatus" ), DataStates.USING.code)); predicatesAnd.add(cb.notEqual(leftJoin.get("interfaceApplyStatus" ), DataStates.APPROVAL_NO_PASS.code)); } } if (!ObjectUtils.isEmpty(request.getKeywords())) { Predicate or = cb.or(cb.like(root.get("interfaceName" ), '%' + request.getKeywords() + '%' ), cb.like(root.get("interfaceCode" ), '%' + request.getKeywords() + '%' )); predicatesAnd.add(or); } if (!ObjectUtils.isEmpty(request.getProjectCode())) { Predicate projectName = cb.like(join.get("projectCode" ), '%' + request.getProjectCode() + '%' ); predicatesAnd.add(projectName); } if (!ObjectUtils.isEmpty(request.getStartTime())) { Predicate createTime = cb.greaterThanOrEqualTo(root.get("createTime" ), request.getStartTime()); predicatesAnd.add(createTime); } if (!ObjectUtils.isEmpty(request.getEndTime())) { Predicate createTime = cb.lessThanOrEqualTo(root.get("createTime" ), request.getEndTime()); predicatesAnd.add(createTime); } Predicate[] arrayAnd = new Predicate[predicatesAnd.size()]; Predicate and = cb.and(predicatesAnd.toArray(arrayAnd)); cq.distinct(true ); return cq.where(and).getRestriction(); }; }
其实这个代码还可以优化,里面有很多重复代码
7.2 查询成新的Pojo 新的pojo
1 2 3 4 5 6 public Class InterfaceLogCount{ private String code; private Long count; getter and setter... }
查询接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public interface InterfaceLogRepository extends JpaRepository <InterfaceLog , String >, JpaSpecificationExecutor <InterfaceLog > { List<InterfaceLog> findAllByInterfaceCodeAndInterfaceType (String code, Integer type, Pageable page) ; @Query ("select new com.shenlan.dataserver.interfacelog.interfaces.rest.dto.InterfaceLogCount(il.interfaceCode,count(il.interfaceCode)) " + "from InterfaceLog as il where il.interfaceCallType=2 group by il.interfaceCode order by count(il.interfaceCode) desc" ) List<InterfaceLogCount> findCountGroupByCode (Pageable page) ; }
八、联合主键 直接放上源码