前言
发这篇的目的是为了提供一篇涵盖Specification各种写法的备忘。
Specification算是JPA中比较灵活的查询方式了,
也少不了Criteria类型安全和面向对象的优点。
之前都是点到即止的简单使用,刚好最近系统地使用了一下,遂有本文。
示例
(先贴用法,再贴环境配置。有不明白的地方欢迎留言讨论。)
-
用法
@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); } }
-
环境配置
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>{
}
- 一个第三方库
如果你觉得每次实现接口重写toPredicate太麻烦了,也可以使用这个第三方库:jpa-spec
,能够减少一些代码量。
示例(更多示例请移步该库github查看)
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>
4. 推荐阅读 SpringDataJpa的Specification查询 jpa-spec教程 5. 扩展阅读 SpringBoot环境下QueryDSL-JPA的入门及进阶 以上。 希望我的文章对你能有所帮助。 我不能保证文中所有说法的百分百正确,但我能保证它们都是我的理解和感悟以及拒绝复制黏贴。 有什么意见、见解或疑惑,欢迎留言讨论。