本文阅读时长5分钟。由作者三汪首发于简书。
前言
发这篇的目的是为了提供一篇涵盖Specification各种写法的备忘。
Specification算是JPA中比较灵活的查询方式了,
也少不了Criteria类型安全和面向对象的优点。
之前都是点到即止的简单使用,刚好最近系统地使用了一下,遂有本文。
示例
(先贴用法,再贴环境配置。有不明白的地方欢迎留言讨论。)
1. 用法
@Service
public class StudentService implements IStudentService {
@Autowired
private IStudentRepository repository;
//无关代码略
@Override
public List<Student> getStudent(String studentNumber,String name ,String nickName,
Date birthday,String courseName,float chineseScore,float mathScore,
float englishScore,float performancePoints) {
Specification<Student> specification = new Specification<Student>(){
@Override
public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//用于暂时存放查询条件的集合
List<Predicate> predicatesList = new ArrayList<>();
//--------------------------------------------
//查询条件示例
//equal示例
if (!StringUtils.isEmpty(name)){
Predicate namePredicate = cb.equal(root.get("name"), name);
predicatesList.add(namePredicate);
}
//like示例
if (!StringUtils.isEmpty(nickName)){
Predicate nickNamePredicate = cb.like(root.get("nickName"), '%'+nickName+'%');
predicatesList.add(nickNamePredicate);
}
//between示例
if (birthday != null) {
Predicate birthdayPredicate = cb.between(root.get("birthday"), birthday, new Date());
predicatesList.add(birthdayPredicate);
}
//关联表查询示例
if (!StringUtils.isEmpty(courseName)) {
Join<Student,Teacher> joinTeacher = root.join("teachers",JoinType.LEFT);
Predicate coursePredicate = cb.equal(joinTeacher.get("courseName"), courseName);
predicatesList.add(coursePredicate);
}
//复杂条件组合示例
if (chineseScore!=0 && mathScore!=0 && englishScore!=0 && performancePoints!=0) {
Join<Student,Examination> joinExam = root.join("exams",JoinType.LEFT);
Predicate predicateExamChinese = cb.ge(joinExam.get("chineseScore"),chineseScore);
Predicate predicateExamMath = cb.ge(joinExam.get("mathScore"),mathScore);
Predicate predicateExamEnglish = cb.ge(joinExam.get("englishScore"),englishScore);
Predicate predicateExamPerformance = cb.ge(joinExam.get("performancePoints"),performancePoints);
//组合
Predicate predicateExam = cb.or(predicateExamChinese,predicateExamEnglish,predicateExamMath);
Predicate predicateExamAll = cb.and(predicateExamPerformance,predicateExam);
predicatesList.add(predicateExamAll);
}
//--------------------------------------------
//排序示例(先根据学号排序,后根据姓名排序)
query.orderBy(cb.asc(root.get("studentNumber")),cb.asc(root.get("name")));
//--------------------------------------------
//最终将查询条件拼好然后return
Predicate[] predicates = new Predicate[predicatesList.size()];
return cb.and(predicatesList.toArray(predicates));
}
};
return repository.findAll(specification);
}
}
2. 环境配置
pom文件引入依赖(对此尚不了解的请自行补充maven相关知识)
<!-- jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Dao层文件继承JpaSpecificationExecutor。这边多继承了一个JpaRepository,不需要的可以自行去除。
@Repository
public interface IStudentRepository extends JpaRepository<Student, String>,JpaSpecificationExecutor<Student>{
}
3. 一个第三方库
如果你觉得每次实现接口重写toPredicate太麻烦了,也可以使用这个第三方库:jpa-spec
,能够减少一些代码量。
public Page<Person> findAll(SearchRequest request) {
Specification<Person> specification = Specifications.<Person>and()
.eq(StringUtils.isNotBlank(request.getName()), "name", request.getName())
.gt(Objects.nonNull(request.getAge()), "age", 18)
.between("birthday", new Range<>(new Date(), new Date()))
.like("nickName", "%og%", "%me")
.build();
return personRepository.findAll(specification, new PageRequest(0, 15));
}
maven依赖
<dependency>
<groupId>com.github.wenhao</groupId>
<artifactId>jpa-spec</artifactId>
<version>3.1.1</version>
</dependency>
4. 推荐阅读
5. 扩展阅读
以上。
希望我的文章对你能有所帮助。
我不能保证文中所有说法的百分百正确,但我能保证它们都是我的理解和感悟以及拒绝复制黏贴。
有什么意见、见解或疑惑,欢迎留言讨论。