项目中遇到问题:使用specification
理想的sql:where status=? and (enterprise_name=? or credit_code=? or (legal_person_name=? and legal_person_certificate_no=?))
specification拼接的sql:where status=? and (enterprise_name=? or credit_code=? or legal_person_name=? and legal_person_certificate_no=?)
List<Predicate> predicateAndList = new ArrayList<>();
predicateAndList.add(criteriaBuilder.equal(root.get("status"), SwindleReportEnterpriseStatusEnum.CONFIRM));
Predicate andStatus = criteriaBuilder.and(predicateAndList.toArray(new Predicate[predicateAndList.size()]));
// 组装4个条件的or
List<Predicate> predicateOrList = new ArrayList<>();
String enterpriseName = enterpriseBase.getEnterpriseName();
if (StringUtils.isNotBlank(enterpriseName)) {
predicateOrList.add(criteriaBuilder.equal(root.get("enterpriseName"), enterpriseName));
}
String creditCode = enterpriseBase.getCreditCode();
if (StringUtils.isNotBlank(creditCode)) {
predicateOrList.add(criteriaBuilder.equal(root.get("creditCode"), creditCode));
}
// 法人信息的过滤
List<Predicate> predicateLegalList = new ArrayList<>();
String legalPersonName = enterpriseBase.getLegalPersonName();
if (StringUtils.isNotBlank(legalPersonName)) {
predicateLegalList.add(criteriaBuilder.equal(root.get("legalPersonName"), legalPersonName));
}
String legalPersonCertificateNo = enterpriseBase.getLegalPersonCertificateNo();
if (StringUtils.isNotBlank(legalPersonCertificateNo)) {
predicateLegalList.add(criteriaBuilder.equal(root.get("legalPersonCertificateNo"), legalPersonCertificateNo));
}
// 法人名、证件号
Predicate andLegal = criteriaBuilder.and(predicateLegalList.toArray(new Predicate[predicateLegalList.size()]));
predicateOrList.add(andLegal);
// 企业名、统一信用代码
Predicate orEnterInfo = criteriaBuilder.or(predicateOrList.toArray(new Predicate[predicateOrList.size()]));
return criteriaQuery.where(andStatus, orEnterInfo).getRestriction();
从打印的sql看最后少了一个括号,这个问题折腾了许久未果,和同事讨论了一下,最终发现是对的,其实以上的两个sql是等价的。
因为sql里面的and的优先级高于or,所以打印的sql没有括号!!!
使用本地数据测试:
现需要查询出性别为女的1年级女学生,或者性别为女的2班级女学生。SQL语句如下:
-
select * from student where sex='女' and grade=1 or class=2
- 但是该sql查询出来的结果并不符合要求,执行结果如下:
执行结果
执行的结果中还查询出了班级为2的男学生,显然结果是不正确的。
4、修改下SQL语句,添加上括号。如下:
-
select * from student where sex='女' and (grade=1 or class=2)
该sql查询出来的结果符合要求,执行结果如下:
执行结果
分析
从上面的场景中,问题的关键就在于AND和OR的执行顺序问题。
查阅资料,关系型运算符优先级高到低为:NOT >AND >OR
如果where 后面有OR条件的话,则OR自动会把左右的查询条件分开。
就如上面场景中的第一条语句,他的查询条件分为两部分(或):
-
1、sex='女' and grade=1
-
2、 class=2
这也就是查询出1年级的女学生,2班级的学生。不符合最初查询的要求。
那么解决办法就是使用括号区分执行的顺序
就如上面场景的第二条语句,查询条件分为两部分(并):
-
1、 sex='女'
-
2、 (grade=1 or class=2)