本文閱讀時長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. 擴展閱讀
以上。
希望我的文章對你能有所幫助。
我不能保證文中所有說法的百分百正確,但我能保證它們都是我的理解和感悟以及拒絕復制黏貼。
有什么意見、見解或疑惑,歡迎留言討論。