一、概述
是 spring data 项目下的一个模块。提供了一套基于 JPA 标准操作数据库的简化方案。底层默认的是依赖 Hibernate JPA 来实现的。
Spring Data JPA 的技术特点:我们只需要定义接口并集成 Spring Data JPA 中所提供的接 口就可以了。不需要编写接口实现类。
1.1 重要api
entityManagerFactory
EntityManagerFactory接口主要用来创建EntityManager实例 EntityManagerFactory是一个线程安全的对象,并且其创建极其浪费资源,所以编程的时候要保持它是单例的。 在JPA规范中,EntityManager是操作数据库的重要API,他是线程不安全的,需要保持线程独有。 重要方法: getTransaction: 获取事务对象 persist:保存操作 merge:更新操作 remove:删除操作 find/getReference:根据id查询
1.2 实现过程
jpa的dao层对象本质上是通过JdkDynamicAopProxy生成的一个代理对象 当进行操作时,会通过JdkDynamicAopProxy的invoke方法,对操作dao层对象生成动态代理对象SimpleJpaRepository。代理对象中包含Jpa规范的API操作 Spring Data JPA只是对标准JPA操作进行了进一步封装,底层是JPA原生实现方式。还是使用的EntityManager。
二、使用示例
2.1 入门Demo
依赖
<!-- Mysql and MariaDB -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>配置
<?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:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置读取 properties 文件的工具类 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置 c3p0 数据库连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="driverClass" value="${jdbc.driver.class}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- Spring 整合 JPA 配置 EntityManagerFactory-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/> <property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!-- hibernate 相关的属性的注入 -->
<!-- 配置数据库类型 -->
<property name="database" value="MYSQL"/>
<!-- 正向工程 自动创建表 -->
<property name="generateDdl" value="true"/>
<!-- 显示执行的 SQL -->
<property name="showSql" value="true"/>
</bean>
</property>
<!-- 扫描实体的包 -->
<property name="packagesToScan">
<list>
<value>com.bjsxt.pojo</value>
</list>
</property>
</bean>
<!-- 配置 Hibernate 的事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!-- 配置开启注解事务处理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 配置 springIOC 的注解扫描 -->
<context:component-scan base-package="com.bjsxt"/>
<!-- Spring Data JPA 的配置 -->
<!-- base-package:扫描 dao 接口所在的包 -->
<jpa:repositories base-package="com.bjsxt.dao"/>
</beans>实体类
@Entity//表示这是一个实体类
@Table(name = "article") //建立实体类和表的映射关系
public class Article implements Serializable {
@Id//声明当前私有属性为主键
@GeneratedValue(strategy = GenerationType.IDENTITY) //配置主键的生成策略
private Integer aid;
//声明类的属性跟数据表字段的对应关系,如果属性名称和字段名称一致,可省略
@Column(name = "title")
private String title;
private String author;
private Date createTime;
}dao层接口
/**
* JpaRepository<实体类类型,主键类型>:用来完成基本 CRUD 操作
* JpaSpecificationExecutor<实体类类型>:用于复杂查询(分页等查询操作)
*/
public interface UsersDao extends JpaRepository<Users, Integer> {
}使用方式
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class UsersDaoImplTest {
@Autowired
private UsersDao usersDao;
/**
* 添加用户
*/
@Test
@Transactional// 在测试类对于事务提交方式默认的是回滚。
@Rollback(false)//取消自动回滚
public void testInsertUsers(){
Users users = new Users();
users.setUserage(24);
users.setUsername("张三");
this.usersDao.save(users);
}
}三、核心知识
3.1 运行原理
@PersistenceContext(name="entityManagerFactory")
private EntityManager em;
@Test
public void test1(){
//org.springframework.data.jpa.repository.support.SimpleJpaRepositor y@fba8bf
//System.out.println(this.usersDao);
//class com.sun.proxy.$Proxy29 代理对象 是基于 JDK 的动态代理方式 创建的
//System.out.println(this.usersDao.getClass());
JpaRepositoryFactory factory = new JpaRepositoryFactory(em);
//getRepository(UsersDao.class);可以帮助我们为接口生成实现类。而 这个实现类是 SimpleJpaRepository 的对象
//要求:该接口必须要是继承 Repository 接口
UsersDao ud = factory.getRepository(UsersDao.class);
System.out.println(ud); System.out.println(ud.getClass());
}3.2 Repository 接口
Repository 接口
Repository 接口是 Spring Data JPA 中为我我们提供的所有接口中的顶层接口
提供了两种查询方式
- 基于方法名称命名规则查询
- 基于@Query 注解查询 规则:findBy(关键字)+属性名称(属性名称的首字母大写)+查询条件(首字母大写) 如:findByNameAndPwd
使用示例
public interface UsersDao extends Repository<Users, Integer> {
/**
按照Spring Data JPA 定义的规则,查询方法以findBy开头,涉及条件查询
时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写。框
架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对剩下部分进
行解析。
* 判断相等的条件,有三种表示方式
* 1,什么都不写,默认的就是做相等判断
* 2,Is
* 3,Equal
*/
List<Users> findByUsernameIs(String string);
List<Users> findByUsernameLike(String string);
List<Users> findByUsernameAndUserageGreaterThanEqual(String name,Integer age);
// Query注解查询
//JPQL:通过 Hibernate 的 HQL 演变过来的。他和 HQL 语法及其相似。
@Query(value="from Users where username = ?")
List<Users> queryUserByNameUseJPQL(String name);
@Query("update Users set userage = ? where userid = ?")
@Modifying //@Modifying 当前语句是一个更新语句
void updateUserAgeById(Integer age,Integer id);
// 通过SQL语句查询
//nativeQuery:默认的是 false.表示不开启 sql 查询。是否对 value 中的语句 做转义。
@Query(value="select * from t_users where username = ?",nativeQuery=true)
List<Users> queryUserByNameUseSQL(String name);
}CrudRepository 接口
基本增删改查
// 保存 save
//批量保存 save
//根据id查询 findOne
//查询全部 findAll
//根据id删除 delete
/**
* 更新数据 方式一
*/
public void test(){
Users user = this.usersDao.findOne(12);
user.setUsername("王一");
this.usersDao.save(user);
}
/**
* 更新数据 方式二
*/
@Transactional
@Rollback(false)
public void test(){
Users user = this.usersDao.findOne(12);
//持久化状态的
user.setUsername("王二");
}PagingAndSortingRepository 接口
分页查询
int page = 2; //page:当前页的索引。注意索引都是从 0 开始的。
int size = 3;// size:每页显示 3 条数据
Pageable pageable= new PageRequest(page, size);
Page<Users> p = this.usersDao.findAll(pageable);
//Sort:该对象封装了排序规则以及指定的排序字段(对象的属性来表示)
//direction:排序规则
//properties:指定做排序的属性
Sort sort = new Sort(Direction.DESC,"userid");
//多列的排序处理
Order order1 = new Order(Direction.DESC,"userage");
Order order2 = new Order(Direction.ASC,"username");
Sort sort = new Sort(order1,order2);
List<Users> list = (List<Users>)this.usersDao.findAll(sort)JpaRepository 接口
返回值适配处理
// JpaRepository 接口是开发时使用的最多的接口。其特点是可以将其他接口的方法的返回值做适配处理。可以在开发时更方便的使用这些方法。JpaSpecificationExecutor 接口
多条件查询
// 单条件查询
Specification<Users> spec = new Specification<Users>() {
/**
* @return Predicate:定义了查询条件
* @param Root<Users> root:根对象。封装了查询条件的对象
* @param CriteriaQuery<?> query:定义了一个基本的查询.一般不 使用
* @param CriteriaBuilder cb:创建一个查询条件
*/
@Override
public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Predicate pre = cb.equal(root.get("username"), "王五");
return pre;
}
};
List<Users> list = this.usersDao.findAll(spec);
// 多条件1
Specification<Users> spec = new Specification<Users>() {
@Override
public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> list = new ArrayList<>();
list.add(cb.equal(root.get("username"),"王五"));
list.add(cb.equal(root.get("userage"),24));
//此时条件之间是没有任何关系的。
Predicate[] arr = new Predicate[list.size()];
return cb.and(list.toArray(arr));
}
};
List<Users> list = this.usersDao.findAll(spec);
// 多条件2
Specification<Users> spec = new Specification<Users>() {
@Override
public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return cb.or(cb.equal(root.get("username"),"王五 "),cb.equal(root.get("userage"), 25));
}
};
List<Users> list = this.usersDao.findAll(spec);
// 分页与排序
//排序等定义
Sort sort = new Sort(Direction.DESC,"userid");
// List<Users> list = this.usersDao.findAll(spec, sort);
//分页的定义
Pageable pageable = new PageRequest(2,2, sort);
Page<Users> page = this.usersDao.findAll(spec, pageable);3.3 关联映射操作
一对一的关联关系
//user类属性
@OneToOne
//@JoinColumn:就是维护一个外键
@JoinColumn(name="roles_id", referencedColumnName = "aid", unique = true)
private Roles roles;
//role类属性
@OneToOne(mappedBy="roles",cascade = CascadeType.ALL))
private Users users;
//关系操作
//创建角色
Roles roles = new Roles();
roles.setRolename("管理员");
//创建用户
Users users = new Users();
users.setUserage(30);
users.setUsername("赵小刚");
//建立关系
users.setRoles(roles);
roles.setUsers(users);
//保存数据
this.usersDao.save(users);一对多关联关系
//一个角色对应多个用户
//用户实体
@ManyToOne()
@JoinColumn(name="roles_id",referencedColumnName = "aid")
private Roles roles;
//角色实体
@OneToMany(mappedBy="roles")
private Set<Users> users = new HashSet<>();
//关系操作
//创建角色
Roles roles = new Roles();
roles.setRolename("管理员");
//创建用户
Users users =new Users();
users.setUserage(30);
users.setUsername("小王");
//建立关系
roles.getUsers().add(users);
users.setRoles(roles);
//保存数据
this.usersDao.save(users);
/**
*@OneToMany:
作用:建立一对多的关系映射
属性:
targetEntityClass:指定多的多方的类的字节码
mappedBy:指定从表实体类中引用主表对象的名称。
cascade:指定要使用的级联操作
fetch:指定是否采用延迟加载
orphanRemoval:是否使用孤儿删除
@ManyToOne
作用:建立多对一的关系
属性:
targetEntityClass:指定一的一方实体类字节码
cascade:指定要使用的级联操作
fetch:指定是否采用延迟加载
optional:关联是否可选。如果设置为false,则必须始终存在非空关系。
@JoinColumn
作用:用于定义主键字段和外键字段的对应关系。
属性:
name:指定外键字段的名称
referencedColumnName:指定引用主表的主键字段名称
unique:是否唯一。默认值不唯一
nullable:是否允许为空。默认值允许。
insertable:是否允许插入。默认值允许。
updatable:是否允许更新。默认值允许。
columnDefinition:列的定义信息
*/多对多关联关系
//一个角色可以拥有多个菜单,一个菜单可以分配多个角色。多对多的关联关系
//角色实体
@ManyToMany
//@JoinTable:配置中间表信息
//joinColumns:建立当前表在中间表中的外键字段
//中间表的外键字段关联对方类所对应表的主键字段
@JoinTable(name="t_roles_menus",joinColumns=@JoinColumn(name="ro le_id"),inverseJoinColumns=@JoinColumn(name="menu_id"))
private Set<Menus> menus = new HashSet<>();
//菜单实体
@ManyToMany(mappedBy="menus")
private Set<Roles> roles = new HashSet<>();
//关联操作
//创建角色对象
Roles roles = new Roles();
roles.setRolename("超级管理员");
//创建菜单对象 XXX 管理平台 --->用户管理
Menus menus = new Menus();
menus.setMenusname("XXX 管理平台");
menus.setFatherid(-1);
menus.setMenusurl(null);
//用户管理菜单
Menus menus1 = new Menus();
menus1.setMenusname("用户管理");
menus1.setFatherid(1);
menus1.setMenusurl(null);
//建立关系
roles.getMenus().add(menus);
roles.getMenus().add(menus1);
menus.getRoles().add(roles);
menus1.getRoles().add(roles);
//保存数据
this.rolesDao.save(roles);
/**
@ManyToMany
作用:用于映射多对多关系
属性:
cascade:配置级联操作。
fetch:配置是否采用延迟加载。
targetEntity:配置目标的实体类。映射多对多的时候不用写。
@JoinTable
作用:针对中间表的配置
属性:
nam:配置中间表的名称
joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段
inverseJoinColumn:中间表的外键字段关联对方表的主键字段
@JoinColumn
作用:用于定义主键字段和外键字段的对应关系。
属性:
name:指定外键字段的名称
referencedColumnName:指定引用主表的主键字段名称
unique:是否唯一。默认值不唯一
nullable:是否允许为空。默认值允许。
insertable:是否允许插入。默认值允许。
updatable:是否允许更新。默认值允许。
columnDefinition:列的定义信息。
*/3.4 实体类与数据库表映射
/**
* * 所有的注解都是使用JPA的规范提供的注解,
* * 所以在导入注解包的时候,一定要导入javax.persistence下的
*/
@Entity //声明实体类
@Table(name="cst_customer") //建立实体类和表的映射关系
public class Customer {
@Id//声明当前私有属性为主键
@GeneratedValue(strategy=GenerationType.IDENTITY) //配置主键的生成策略
@Column(name="cust_id") //指定和表中cust_id字段的映射关系
private Long custId;
@Column(name="cust_name") //指定和表中cust_name字段的映射关系
private String custName;
}
/**
*@Entity
作用:指定当前类是实体类。
@Table
作用:指定实体类和表之间的对应关系。
属性:
name:指定数据库表的名称
@Id
作用:指定当前字段是主键。
@GeneratedValue
作用:指定主键的生成方式。。
属性:
strategy :指定主键生成策略。
IDENTITY:主键由数据库自动生成(主要是自动增长型)
SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。
AUTO:主键由程序控制
TABLE:使用一个特定的数据库表格来保存主键
@Column
作用:指定实体类属性和数据库表之间的对应关系
属性:
name:指定数据库表的列名称。
unique:是否唯一
nullable:是否可以为空
inserttable:是否可以插入
updateable:是否可以更新
columnDefinition: 定义建表时创建此列的DDL
secondaryTable: 从表名。如果此列不建在主表上(默认建在主表),该属性定义该列所在从表的名字搭建开发环境[重点]
*/3.5 查询方式
父接口方法查询
自定义的Dao接口可以使用它的父接口提供的方法。
方法命名规则查询
方法命名规则查询就是根据方法的名字,就能创建查询。只需要按照SpringData JPA提供的方法命名规则定义方法的名称,就可以完成查询工作。 SpringData JPA在程序执行的时候会根据方法名称进行解析,并自动生成查询语句进行查询. 按照SpringData JPA定义的规则,查询方法以findBy开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉, 然后对剩下部分进行解析。
public interface ArticleDao extends JpaRepository<Article, Integer>, JpaSpecificationExecutor<Article> {
//根据标题查询
List<Article> findByTitle(String title);
//根据标题模糊查询
List<Article> findByTitleLike(String title);
//根据标题和作者查询
List<Article> findByTitleAndAuthor(String title, String author);
//根据ID范围查询
List<Article> findByAidBetween(Integer starAid, Integer endAid);
List<Article> findByAidLessThan(Integer endAid);
List<Article> findByAidIn(List<Integer> aids);
//根据创建时间之后查询
List<Article> findByCreateTimeAfter(Date createTime);
}命名规则示例:
| 关键字 | 例子 | 对应的JPQL语句 |
|---|---|---|
| And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ? 2 |
| Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
| Is,Equals | findByFirstnameIs, findByFirstnameEquals | … where x.firstname = ?1 |
| Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
| LessThan | findByAgeLessThan | … where x.age < ?1 |
| LessThanEqual | findByAgeLessThanEqual | … where x.age ⇐ ?1 |
| GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
| GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
| After | findByStartDateAfter | … where x.startDate > ?1 |
| Before | findByStartDateBefore | … where x.startDate < ?1 |
| IsNull | findByAgeIsNull | … where x.age is null |
| IsNotNull,NotNull | findByAge(Is)NotNull | … where x.age not null |
| Like | findByFirstnameLike | … where x.firstname like ?1 |
| NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
| StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (parameter bound with appended %) |
| EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended %) |
| Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in %) |
| OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
| Not | findByLastnameNot | … where x.lastname <> ?1 |
| In | findByAgeIn(Collection ages) | … where x.age in ?1 |
| NotIn | findByAgeNotIn(Collection age) | … where x.age not in ?1 |
| TRUE | findByActiveTrue() | … where x.active = true |
| FALSE | findByActiveFalse() | … where x.active = false |
| IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstame) = U |
JPQL查询
使用SpringData JPA提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还 需要灵活的构造查询条件,这时就可以使用@Query注解,结合JPQL的语句方式完成查询。 JPQL,全称是Java Persistence Query Language。JPQL语句是JPA中定义的一种查询语言,此种 语言的用意是让开发者忽略数据库表和表中的字段,而关注实体类及实体类中的属性。 它的写法十分类似于SQL语句的写法,但是要把查询的表名换成实体类名称,把表中的字段名换成实体类 的属性名称。
public interface ArticleDao extends JpaRepository<Article, Integer>, JpaSpecificationExecutor<Article> {
//展示位置参数绑定
@Query("from Article a where a.author=?1 and a.title=?2")
List<Article> findByCondition1(String author, String title);
//展示名字参数绑定
@Query("from Article a where a.author=:author and a.title=:title")
List<Article> findByCondition2(@Param("author") String author, @Param("title") String title);
//展示like模糊查询
@Query("from Article a where a.title like %:title%")
List<Article> findByCondition3(@Param("title") String title);
//展示排序查询
@Query("from Article a where a.title like %:title% order by aid desc")
List<Article> findByCondition4(@Param("title") String title);
//展示分页查询
@Query("from Article a where a.title like %:title%")
Page<Article> findByCondition5(Pageable pageable, @Param("title") String title);
//展示传入集合参数查询
@Query("from Article a where a.aid in :aids")
List<Article> findByCondition6(@Param("aids") Collection<String> aids);
//展示传入Bean进行查询(SPEL表达式查询)
@Query("from Article a where a.author=:#{#article.author} and a.title=:# {#article.title}")
Article findByCondition67(@Param("article") Article article);
}SQL查询
//nativeQuery=true表示使用本地SQL查询
//基本不会使用,除非是出现非常复杂的业务情况导致SQL非常复杂,JPQL搞不定的时候
@Query("select * from article where title like ?1 and author = ? 2",nativeQuery=true)
List<User> findAllByTitleAndAuthor(String title,String author);Specifications动态查询
有时在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在 Spring Data JPA 中可以通过 JpaSpecificationExecutor 接口查询。相比 JPQL,其优势是类型安全,更加的面向对象,缺点是书写比较麻烦。
package com.itheima.test;
import com.itheima.dao.ArticleDao;
import com.itheima.domain.Article;
import org.junit.Test;
import org.junit.runner.RunWith;
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.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.StringUtils;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-jpa.xml")
public class Query4Test {
@Autowired
private ArticleDao articleDao;
//按照标题和作者进行查询,以不为空的属性作为查询条件
@Test
public void testFindAll() {
//就模拟从从外边传入的变量
String title = "黑马程序员3";
String author = "黑马3";
List<Article> articles = articleDao.findAll(new Specification<Article>() {
/**
* @param root 代表实体对象,我们可以通过它获取属性值
* @param cq 用于生成SQL语句
* @param cb 用于拼接查询条件
* @return
*/
@Override
public Predicate toPredicate(Root<Article> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
List<Predicate> list = new ArrayList<>();
if (!StringUtils.isEmpty(title)) {
//拼接作为查询条件
Predicate predicate = cb.equal(root.get("title").as(String.class), title);
list.add(predicate);
}
if (!StringUtils.isEmpty(author)) {
//拼接作为查询条件
Predicate predicate = cb.equal(root.get("author").as(String.class), author);
list.add(predicate);
}
return cb.and(list.toArray(new Predicate[]{}));
}
});
for (Article article : articles) {
System.out.println(article);
}
}
@Test
public void testFindAllWithPage() {
//就模拟从从外边传入的变量
String title = "";
String author = "";
//分页
Pageable pageable = PageRequest.of(0, 3);
Page<Article> page = articleDao.findAll(new Specification<Article>() {
/**
* @param root 代表实体对象,我们可以通过它获取属性值
* @param cq 用于生成SQL语句
* @param cb 用于拼接查询条件
* @return
*/
@Override
public Predicate toPredicate(Root<Article> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
List<Predicate> list = new ArrayList<>();
if (!StringUtils.isEmpty(title)) {
//拼接作为查询条件
Predicate predicate = cb.equal(root.get("title").as(String.class), title);
list.add(predicate);
}
if (!StringUtils.isEmpty(author)) {
//拼接作为查询条件
Predicate predicate = cb.equal(root.get("author").as(String.class), author);
list.add(predicate);
}
return cb.and(list.toArray(new Predicate[]{}));
}
}, pageable);
for (Article article : page.getContent()) {
System.out.println(article);
}
}
@Test
public void testFindAllWithPageAndSort() {
//就模拟从从外边传入的变量
String title = "";
String author = "";
//分页
Pageable pageable = PageRequest.of(0, 3, Sort.by(Sort.Order.desc("aid")));
Page<Article> page = articleDao.findAll(new Specification<Article>() {
/**
* @param root 代表实体对象,我们可以通过它获取属性值
* @param cq 用于生成SQL语句
* @param cb 用于拼接查询条件
* @return
*/
@Override
public Predicate toPredicate(Root<Article> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
List<Predicate> list = new ArrayList<>();
if (!StringUtils.isEmpty(title)) {
//拼接作为查询条件
Predicate predicate = cb.equal(root.get("title").as(String.class), title);
list.add(predicate);
}
if (!StringUtils.isEmpty(author)) {
//拼接作为查询条件
Predicate predicate = cb.equal(root.get("author").as(String.class), author);
list.add(predicate);
}
return cb.and(list.toArray(new Predicate[]{}));
}
}, pageable);
for (Article article : page.getContent()) {
System.out.println(article);
}
}
}