1.创建项目
- 创建项目的第一步,搭建好框架
- 导包
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<org.springframework.version>4.2.5.RELEASE</org.springframework.version>
<org.hibernate.version>4.3.8.Final</org.hibernate.version>
<spring-data-jpa.version>1.9.0.RELEASE</spring-data-jpa.version>
<com.fasterxml.jackson.version>2.5.0</com.fasterxml.jackson.version>
<org.slf4j.version>1.6.1</org.slf4j.version>
</properties>
<dependencies>
<!-- Spring的支持包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<!-- 引入web前端的支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!-- SpringMCV上传需要用到io包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 文件上传用到的包 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
<!-- SpringMVC的json支持包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${com.fasterxml.jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${com.fasterxml.jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${com.fasterxml.jackson.version}</version>
</dependency>
<!-- hibernate的支持包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${org.hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${org.hibernate.version}</version>
</dependency>
<!-- SpringData的支持包 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>${spring-data-jpa.version}</version>
</dependency>
<!-- SpringData的擴展包 -->
<dependency>
<groupId>com.github.wenhao</groupId>
<artifactId>jpa-spec</artifactId>
<version>3.1.1</version>
<!-- 把所有的依賴都去掉 -->
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<!-- 測試包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<!-- 这个scope 只能作用在编译和测试时,同时没有传递性。表示在运行的时候不添加此jar文件 -->
<scope>provided</scope>
</dependency>
<!-- 日志文件 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<!-- 代码生成器模版技术 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.6</version>
</dependency>
<!-- shiro的支持包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.4.0</version>
<type>pom</type>
</dependency>
<!-- shiro与Spring的集成包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!-- poi支持的jar包 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.11</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.11</version>
</dependency>
<!-- 图片压缩功能 -->
<!-- 缩略图 -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.6</version>
</dependency>
<!-- 定时调度 -->
<dependency>
<groupId>quartz</groupId>
<artifactId>quartz</artifactId>
<version>1.5.2</version>
</dependency>
<!-- 邮件支持 -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.1</version>
</dependency>
</dependencies>
<build>
<finalName>yxb</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>8.1.15.v20140411</version>
<configuration>
<stopPort>9966</stopPort>
<stopKey>foo</stopKey>
<webAppConfig>
<contextPath>/</contextPath>
</webAppConfig>
</configuration>
</plugin>
</plugins>
</build>
- 配置好Spring的xml(applicationContext.xml)和数据库配置文件(jdbc.properties)。
. applicationContext.xml的配置如下所示:
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.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
">
<!--service的扫描-->
<context:component-scan base-package="com.xpc.service" />
<!--读取jdbc.properties
这里必需写classpath:,不然web环境下就读取不到这个文件
-->
<context:property-placeholder location="classpath:jdbc.properties" />
<!--配置datasource-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!--maxActive: 最大连接数量 -->
<property name="maxActive" value="150" />
<!--minIdle: 最小空闲连接 -->
<property name="minIdle" value="5" />
<!--maxIdle: 最大空闲连接 -->
<property name="maxIdle" value="20" />
<!--initialSize: 初始化连接 -->
<property name="initialSize" value="30" />
<!-- 用来配置数据库断开后自动连接的 -->
<!-- 连接被泄露时是否打印 -->
<property name="logAbandoned" value="true" />
<!--removeAbandoned: 是否自动回收超时连接 -->
<property name="removeAbandoned" value="true" />
<!--removeAbandonedTimeout: 超时时间(以秒数为单位) -->
<property name="removeAbandonedTimeout" value="10" />
<!--maxWait: 超时等待时间以毫秒为单位 1000等于60秒 -->
<property name="maxWait" value="1000" />
<!-- 在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位. -->
<property name="timeBetweenEvictionRunsMillis" value="10000" />
<!-- 在每次空闲连接回收器线程(如果有)运行时检查的连接数量 -->
<property name="numTestsPerEvictionRun" value="10" />
<!-- 1000 * 60 * 30 连接在池中保持空闲而不被空闲连接回收器线程 -->
<property name="minEvictableIdleTimeMillis" value="10000" />
<property name="validationQuery" value="SELECT NOW() FROM DUAL" />
</bean>
<!--alt+ins配置: EntityManagerFactory-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!--连接数据库的基本配置-->
<property name="dataSource" ref="dataSource"/>
<!-- packagesToScan:扫描相应的包(jpa的扫描) -->
<property name="packagesToScan" value="com.xpc.domain" />
<!--
jpaVendorAdapter:JPA的适配器[确定这个jpa是哪种框架来实现的]
JPA:ORM规范 -> 有多种实现(hibernate,openjpa,...)
-->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--方言-->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
<!--是否显示sql-->
<property name="showSql" value="true" />
<!--
建表策略 DDL:建库建表建约束
true:相当于update
false:相当于什么都不做
-->
<property name="generateDdl" value="false" />
</bean>
</property>
</bean>
<!--配置相应的事务对象-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!--
配置事务的支持(支持使用注解加事务)
它默认会去找一个transactionManager的bean
-->
<tx:annotation-driven transaction-manager="transactionManager" />
<!--
配置SpringDataJpa -> 让咱们的某一层的代码支持SpringDataJpa
只要发现咱们的接口继承了JpaRepository,它就会自动去完成相应的CRUD
-->
<jpa:repositories base-package="com.xpc.repository"
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager" />
</beans>
- jdbc.properties的配置如下所示
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///xsw
jdbc.username=root
jdbc.password=334455
配置好这些之后先测试一下是否配置成功,是否有错
-
先在domain里面创建一个Employee的实体类,对应数据库的实体表
-
然后再把id这一个字段抽取出来成为一个公共的父类叫BaseDomain,以后的所有实体表都来继承它
//抽取父类是为了提高扩展性、减少代码量、定义一种规范
//MappedSuperclass 在JPA里面就表示是父类,不持久化到表
@MappedSuperclass
public class BaseDomain {
@Id
@GeneratedValue
protected Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
- 再到repository包下面创建一个EmployeeRepository的接口,它来它来继承JpaRepository<Employee,Long>这个接口
/*
* 继承了JpaRepository之后,就会自动完成CRUD功能(增删改查)
* Employee:代表你要操作的是哪一个domain对象
* Long:代表的是主键的数据类型
* */
public interface EmployeeRepository extends JpaRepository<Employee,Long>{
}
- 最后就可以在test里面写代码测试了
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class MyTest {
@Autowired
private EmployeeRepository employeeRepository;
@Test
public void testFindAll(){
List<Employee> all = employeeRepository.findAll();
all.forEach(e -> System.out.println(e));
}
在学习JpaRepository和JpaSpecificationExecutor之前可以先了解一下SpringDataJpa
JpaRepository和JpaSpecificationExecutor是都 SpringDataJpa的子接口
Spring Data JPA的七个Repository接口:
Repository(org.springframework.data.repository.Repository)
CrudRepository(org.springframework.data.repository.CrudRepository)
PagingAndSortingRepository(org.springframework.data.repository.PagingAndSortingRepository)
JpaRepository (org.springframework.data.jpa.repository.JpaRepository)
QueryByExampleExecutor(org.springframework.data.repository.query.QueryByExampleExecutor)
JpaSpecificationExecutor (org.springframework.data.jpa.repository.JpaSpecificationExecutor)
QueryDslPredicateExecutor (org.springframework.data.querydsl.QueryDslPredicateExecutor)
两大Repository实现类:
SimpleJpaRepository(org.springframework.data.jpa.repository.support.SimpleJpaRepository)
QueryDslJpaRepository(org.springframework.data.jpa.repository.support.QueryDslJpaRepository)
结构图
2.JpaRepository的基本功能
①. 普通的CRUD
@Test
public void testFindAll(){ //查询所有
List<Employee> all = employeeRepository.findAll();
all.forEach(e -> System.out.println(e));
}
@Test
public void testFindOne(){ //根据ID查询1条数据
Employee one = employeeRepository.findOne(1L);
System.out.println(one);
}
/*
添加和修改都是用save方法来进行
会自动的判断(有无id)是执行添加还是修改
*/
@Test
public void testSave(){
Employee employee = new Employee();
employee.setId(274l);
employee.setAge(25);
employee.setEmail("258@qq.com");
employee.setUsername("小黑");
employee.setPassword("8694");
employeeRepository.save(employee);
}
@Test
public void testDel(){ //删除数据
employeeRepository.delete(274L);
}
@Test
public void testCount(){ //得到总条数
System.out.println(employeeRepository.count());
}
②. 分页排序功能
//排序
@Test
public void testSort(){
//ASC 升序 DESC 降序
Sort sort = new Sort(Sort.Direction.DESC,"age");
List<Employee> list = employeeRepository.findAll(sort);
list.forEach(e-> System.out.println(e));
}
//分页
@Test
public void testPage(){
//0:起始页 10:每页的条数
Pageable pageable = new PageRequest(0,10);
Page<Employee> page = employeeRepository.findAll(pageable);
page.forEach(e-> System.out.println(e));
System.out.println(page.getTotalElements()); //总条数
System.out.println(page.getTotalPages()); //总页
System.out.println(page.getNumber()); //1 当前页
System.out.println(page.getNumberOfElements()); //当前页的数量
System.out.println(page.getSize()); //每页条数
System.out.println(page.getContent()); //当前页的数据
}
//分页+排序
@Test
public void testPageSort(){
Sort sort = new Sort(Sort.Direction.DESC,"age");
Pageable pageable = new PageRequest(0,10,sort);
Page<Employee> page = employeeRepository.findAll(pageable);
page.forEach(e-> System.out.println(e));
}
③ 根据条件进行查询
EmployeeRepository中写方法
Employee findByUsername(String username); //根据名字查找
List<Employee> findByUsernameLike(String name); //根据名字模糊查询
List<Employee> findByUsernameLikeAndEmailLike(String name,String email); //根据名字和邮箱模糊查询
test里面直接用接口对象调用方法就可以完成功能了
@Test
public void testFind(){ //根据名字模糊查询
Employee employee = employeeRepository.findByUsername("admin2");
System.out.println(employee);
}
@Test
public void testLike(){ //根据名字模糊查询
List<Employee> list = employeeRepository.findByUsernameLike("%2%");
list.forEach(e-> System.out.println(e));
}
@Test
public void testLikes(){ //根据名字和邮箱模糊查询
List<Employee> list = employeeRepository.findByUsernameLikeAndEmailLike("%1%", "%2%");
list.forEach(e-> System.out.println(e));
}
④ @Query注解查询
EmployeeRepository
/*JPQL
* 问号后面必须加占位符
* */
//根据名字查找
@Query("SELECT o FROM Employee o WHERE o.username=?1")
Employee query01(String username);
//根据名字模糊查询
@Query("SELECT o FROM Employee o WHERE o.username LIKE ?1")
List<Employee> query02(String username);
//根据名字和邮箱模糊查询
//方式一
//@Query("SELECT o FROM Employee o WHERE o.username LIKE ?1 AND o.email LIKE ?2")
//List<Employee> query03(String username,String email);
//方式二
@Query("SELECT o FROM Employee o WHERE o.username LIKE :username AND o.email LIKE :email")
List<Employee> query03(@Param("username") String username,@Param("email") String email);
test
//根据名字查找
@Test
public void testquery01(){
Employee query01 = employeeRepository.query01("admin1");
System.out.println(query01);
}
//根据名字模糊查询
@Test
public void testquery02(){
List<Employee> list = employeeRepository.query02("%1%");
list.forEach(e-> System.out.println(e));
}
//根据名字和邮箱模糊查询
@Test
public void testquery03(){
List<Employee> list = employeeRepository.query03("%1%","%2%");
list.forEach(e-> System.out.println(e));
}
如果想要写原生的SQL
//执行原生SQL
@Query(nativeQuery = true,value = "SELECT * FROM employee")
List<Employee> query04();
3.JpaSpecificationExecutor的认识
**JpaSpecificationExecutor(JPA规则执行者)**是JPA2.0提供的Criteria API的使用封装,可以用于动态生成Query来满足我们业务中的各种复杂场景。
Spring Data JPA为我们提供了JpaSpecificationExecutor接口,只要简单实现toPredicate方法就可以实现复杂的查询。
我们要使用这个接口中的方法,首先让我们的接口也去继承这个接口
public interface EmployeeRepository extends JpaRepository<Employee,Long> ,JpaSpecificationExecutor<Employee>{
- 单个查询
@Test
public void testJpaSpecificationExecutor01(){
/*
*官方解释:
* Root<T> root:代表了可以查询和操作的实体对象的根,
* 可以通过它的 Path<Y> get(String attributeName); 这个方法拿到我们要操作的字段
* 注意:只可以拿到对应的T的字段(Employee)
* CriteriaQuery<?> query:代表一个specific的顶层查询对象
* 包含查询的各个部分,比如select,from,where,group by ,order by 等
* 简单理解 就是它提供 了查询ROOT的方法(where,select,having)
* CriteriaBuilder cb:用来构建CriteriaQuery的构建器对象(相当于条件或者说条件组合)
* 构造好后以Predicate的形式返回
*
*
* 非官方理解:
* 查询的时候就需要给一个标准(规范)
* -》 根据规范(这个规范我们可以先简单理解为查询的条件)进行查询
*
* Root:查询哪个表(定位到表和字段-> 用于拿到表中的字段)
* 可以查询和操作的实体的根
* Root接口:代表Criteria查询的根对象,Criteria查询的查询根定义了实体类型,能为将来导航获得想要的结果,它与SQL查询中的FROM子句类似
* Root<Employee> 相当于 from Employee
* Root<Product> 相当于 from Product
* CriteriaQuery:查询哪些字段,排序是什么(主要是把多个查询的条件连系起来)
* CriteriaBuilder:字段之间是什么关系,如何生成一个查询条件,每一个查询条件都是什么方式
* 主要判断关系(和这个字段是相等,大于,小于like等)
* Predicate(Expression):单独每一条查询条件的详细描述 整个 where xxx=xx and yyy=yy ...
*/
List<Employee> list = employeeRepository.findAll(new Specification<Employee>() {
@Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
Path path = root.get("username");//拿到要做查询的字段
Predicate predicate = cb.like(path, "admin3");//like代表做模糊查询,后面就是它的条件值
return predicate;
}
});
list.forEach(e-> System.out.println(e));
}
- 多个条件查询
//JpaSpecificationExecutor 多个条件查询
@Test
public void testJpaSpecificationExecutor03(){
List<Employee> list = employeeRepository.findAll(new Specification<Employee>() {
@Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
//查询username里面带1的
Path username = root.get("username");
Predicate predicate = cb.like(username, "%1%");
//查询email里面带2的
Path email = root.get("email");
Predicate predicate1 = cb.like(email, "%2%");
//age>20岁
Path age = root.get("age");
Predicate predicate2 = cb.gt(age, 20);
//把3个条件连起来
Predicate predicate3 = cb.and(predicate, predicate1, predicate2);
return predicate3;
}
});
list.forEach(e-> System.out.println(e));
}
- 多个条件查询+分页+排序
@Test
public void testJpaSpecificationExecutor04(){
//排序
Sort sort = new Sort(Sort.Direction.DESC,"age");
//分页
Pageable pageable = new PageRequest(0, 10, sort);
Page<Employee> page = employeeRepository.findAll(new Specification<Employee>() {
@Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
Path username = root.get("username");
Predicate predicate = cb.like(username, "%1%");
return predicate;
}
},pageable);
page.forEach(e-> System.out.println(e));
}
4.jpa-spec插件
这是一个对于咱们刚才的动态生成Query功能的一个封装版,如果我们使用这个插件,在完成查询与分页的时候功能会简单不少。
基于Spring Data Jpa的动态查询库 https://github.com/wenhao/jpa-spec
想要使用首先要导包Maven包
pom.xml(咱们项目中已经引入,不需要再次引入了):
<!-- jpa的SpecificationSpecification功能封装 -->
<dependency>
<groupId>com.github.wenhao</groupId>
<artifactId>jpa-spec</artifactId>
<version>3.1.1</version>
<!-- 把所有依赖都过滤 -->
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
- 单个条件查询
//JpaSpec 查询一个条件的数据
@Test
public void testJpaSpec01(){
Specification<Employee> specification = Specifications.<Employee>and()
.like("username", "%1%")
.build();
List<Employee> list = employeeRepository.findAll(specification);
list.forEach(e-> System.out.println(e));
}
- 多个条件查询
//Jpa-Spec 多条件的数据查询
@Test
public void testJpaSpec02(){
Specification<Employee> specification = Specifications.<Employee>and()
.like("username", "%1%")
.like("email", "%2%")
.gt("age",20) //大于20
.build();
List<Employee> list = employeeRepository.findAll(specification);
list.forEach(e-> System.out.println(e));
}
- 排序+分页+模糊查询
//Jpa-Spec 排序+分页+模糊查询
@Test
public void testJpaSpec03(){
Sort sort = new Sort(Sort.Direction.valueOf("DESC"), "age"); //排序
PageRequest request = new PageRequest(0, 10, sort); //分页
Specification<Employee> specification = Specifications.<Employee>and()
.like("username", "%1%")
.like("email", "%2%")
.build();
Page<Employee> page = employeeRepository.findAll(specification, request); //三合一
page.forEach(e-> System.out.println(e));
}
Jpa-spec的中文文档:
https://github.com/wenhao/jpa-spec/blob/master/docs/3.1.0_cn.md
https://www.w3cschool.cn/jpaspec/
5.Query查询条件
Query查询条件也就是——简单的模拟真实的客户在前台页面输入相应的数据进行查询
首先 建立一个query包里面写两个类EmployeeQuery和BaseQuery(抽象类)
以后这个包里面的的每个方法都继承BaseQuery这个类
BaseQuery里面的代码
*
*抽取父类是为了提高扩展性、减少代码量、定义一种规范
*/
public abstract class BaseQuery {
private int currentPage = 1; //当前页
private int pageSize = 10; //每条条数
private String orderName; //排序字段名(如果前台没有传字段名,代表不需要做排序)
private String orderType = "ASC"; //排序的规则(默认升序)
//写个抽象方法去规范子类获取Specification对象的名称必需叫:createSpec
public abstract Specification createSpec();
//创建排序对象
public Sort createSort(){
//import org.apache.commons.lang3.StringUtils; 导这个包
if(StringUtils.isNotBlank(orderName)){
return new Sort(Sort.Direction.valueOf(orderType.toUpperCase()),orderName);
}
return null;
}
public int getCurrentPage() {
return currentPage;
}
//加一个方法 第一页从0开始计算
public int getJpaCurrentPage() {
return currentPage-1;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
public String getOrderType() {
return orderType;
}
public void setOrderType(String orderType) {
this.orderType = orderType;
}
}
EmployeeQuery里面的代码
public class EmployeeQuery extends BaseQuery{
private String username;
private String email;
private Integer age;
@Override
public Specification createSpec() {
//这个地方要导 wenhao 包
/**
* like方法两个参数
* 1.查询的字段
* 2.这个字段条件对应的值
* like方法三个参数
* 1.boolean false,这个查询不执行 为true则执行
* 2.查询的字段
* 3.这个字段条件对应的值
*/
Specification<Employee> specification = Specifications.<Employee>and()
.like(StringUtils.isNotBlank(username),"username", "%"+username+"%")
.like(StringUtils.isNotBlank(email),"email", "%"+email+"%")
.gt(age != null,"age", age)
.build();
return specification;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
最后就可以在测试类里面进行测试了
@Test
public void testQuery(){
EmployeeQuery employeeQuery = new EmployeeQuery();
//模拟前台传参
employeeQuery.setUsername("1");
employeeQuery.setEmail("2");
employeeQuery.setAge(20);
Specification spec = employeeQuery.createSpec();
List<Employee> list = employeeRepository.findAll(spec);
list.forEach(e-> System.out.println(e));
}
//模糊查询+排序+分页
@Test
public void testQuery02(){
EmployeeQuery employeeQuery = new EmployeeQuery();
//模拟前台传参
employeeQuery.setUsername("1");
employeeQuery.setOrderName("age");//排序字段
employeeQuery.setOrderType("DESC");//降序排序
Specification spec = employeeQuery.createSpec();
//分页对象
Sort sort = employeeQuery.createSort();
//排序对象 getJpaCurrentPage():起始页数 getPageSize():每页条数
PageRequest request = new PageRequest(employeeQuery.getJpaCurrentPage(), employeeQuery.getPageSize(), sort);
Page<Employee> page = employeeRepository.findAll(spec, request);
page.forEach(e-> System.out.println(e));
}
项目结构图
总结:
今天学的主要类容就是SpringDataJpa和Query的抽取思想
JpaRepository和JpaSpecificationExecutor就是SpringDataJpa的子接口
继承JpaRepository这个接口就可以通过接口类完成基本的CRUD操作
再继承JpaSpecificationExecutor这个接口就可以完成一下复杂的业务查询
Query的抽取思想是为了规范类、少写代码、同时提高灵活性和拓展性
jpa-spec插件就是动态生成Query功能的一个封装版,不用SQL和APQL语句,用一种面向对象的思维来完成CRUD的一个插件
好了,今天的学习内容就是这些了,我们下次再见吧!!