Jpa?Specification如何實(shí)現(xiàn)and和or同時(shí)使用查詢
同時(shí)使用and和or的查詢
UserServiceImpl 類,service實(shí)現(xiàn)類
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
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;
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private RongUserRepository rongUserRepository;
//FriendNumResult 自定的返回類型
//FriendNumParam 自定義的封裝參數(shù)的類型
//RongUser 實(shí)體類型
@Override
public FriendNumResult friendNum(FriendNumParam friendNumParam) {
FriendNumResult friendNumResult=new FriendNumResult();
Specification<RongUser> specification = new Specification<RongUser>(){
@Override
public Predicate toPredicate(Root<RongUser> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
//封裝and語(yǔ)句
List<Predicate> listAnd = new ArrayList<Predicate>();
//這里是hql,所以root.get(),方法里面必須是對(duì)應(yīng)的實(shí)體屬性
listAnd.add(criteriaBuilder.equal(root.get("perLevel").as(Integer.class), friendNumParam.getPerLevel()));
Predicate[] array_and=new Predicate[listAnd.size()];
Predicate Pre_And = criteriaBuilder.and(listAnd.toArray(array_and));
//封裝or語(yǔ)句
List<Predicate> listOr = new ArrayList<Predicate>();
listOr.add(criteriaBuilder.equal(root.get("fId").as(Integer.class), friendNumParam.getUid()));
listOr.add(criteriaBuilder.equal(root.get("fId2").as(Integer.class), friendNumParam.getUid()));
Predicate[] arrayOr = new Predicate[listOr.size()];
Predicate Pre_Or = criteriaBuilder.or(listOr.toArray(arrayOr));
return criteriaQuery.where(Pre_And,Pre_Or).getRestriction();
//單獨(dú)使用 and 或者 or 時(shí) 返回
//return criteriaBuilder.and(list.toArray());
}
};
long num=this.rongUserRepository.count(specification);
friendNumResult.setFriendNum(Integer.valueOf((int)num));
return friendNumResult;
}
}
RongUserRepository接口
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
//RongUser 自己的實(shí)體類型
public interface RongUserRepository extends JpaRepository<RongUser,Integer> , JpaSpecificationExecutor<RongUser> {
}
注意:使用Specification之前,RongUserRepository接口必須實(shí)現(xiàn)JpaSpecificationExecutor<RongUser>,RongUser對(duì)應(yīng)表的實(shí)體類
JPA 動(dòng)態(tài)查詢之AND、OR結(jié)合使用
現(xiàn)在,我負(fù)責(zé)開發(fā)的項(xiàng)目中,使用JPA作為ORM框架。有了JPA,一行SQL都沒(méi)寫過(guò)。在昨天,有一個(gè)新的需求,需要進(jìn)行動(dòng)態(tài)查詢,這個(gè)簡(jiǎn)單。但是有一個(gè)地方需要AND、OR結(jié)合使用,這里,我將記錄下我的理解與寫法,希望能幫助到大家。
問(wèn)題描述
需要根據(jù)條件進(jìn)行動(dòng)態(tài)查詢,實(shí)現(xiàn)一條類似下文的語(yǔ)句:
SELECT *
FROM table
WHERE 1 = 1
if (a == 1)
AND table.column1 = a
if (b != null)
AND table.column2 = b
if (cList != null && cList.size() > 0)
AND table.column3 IN cList
if (d == 2 || dd == 2)
AND (table.column4 = d OR table.column5 = dd)
上面是幾行偽代碼。意思是,幾個(gè)條件之間是AND連接,但是其中的部分條件,是使用OR連接的。
在我們的實(shí)際項(xiàng)目中,這個(gè)場(chǎng)景也是很常見的。這里,我將分享下具體的寫法。以我們項(xiàng)目中的例子為例。
代碼示例
JPA的動(dòng)態(tài)查詢,這里我們使用的方式是:實(shí)現(xiàn) Specification 接口,自定義動(dòng)態(tài)查詢邏輯。這也是我個(gè)人比較推薦的方式。JPA的使用、Specification 接口基礎(chǔ)知識(shí)這里我就不講了。有興趣的朋友可以查閱官方文檔學(xué)習(xí)。
下面,我們首先定義好我們的數(shù)據(jù)庫(kù)實(shí)體:
@Data
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
/**
* 用戶名
*/
private String username;
/**
* 年齡
*/
private Integer age;
/**
* 生日
*/
private Date birthDay;
/**
* 刪除標(biāo)識(shí); 0 - 未刪除,1 - 已刪除
*/
private Integer deleteFlag;
}
然后定義好DAO層接口:
@Repository
public interface UserDAO extends JpaRepository<User, Long> {
/**
* 其實(shí),這個(gè)功能一般用作 list 接口使用,一般結(jié)合分頁(yè)查詢使用。這里,我不做介紹,看情況要不要后期加上教程
*/
List<User> findAll(Specification<User> querySpec);
}
下面是前端傳過(guò)來(lái)的動(dòng)態(tài)查詢的參數(shù)對(duì)象:
@Data
public class UserDTO {
/**
* 用戶名,用于模糊搜索
*/
private String username;
/**
* 用戶ID,用于 In 查詢
*/
private List<String> userIdList;
/**
* 用戶年齡,用于 OR In 查詢
*/
private List<Integer> ageList;
/**
* 生日,開始
*/
@JsonFormat(pattern = "yyyy-MM-dd", locale = "zh", timezone = "GMT+8")
private Date birthDayBegin;
/**
* 生日,結(jié)束
*/
@JsonFormat(pattern = "yyyy-MM-dd", locale = "zh", timezone = "GMT+8")
private Date birthDayEnd;
}
然后,重要的地方來(lái)了,我們實(shí)現(xiàn) Specification 接口,定義查詢邏輯:
在實(shí)際代碼操作中,我會(huì)將這部分邏輯抽離為一個(gè)單獨(dú)的方法,使用lambda表達(dá)式完成,其實(shí)也就是匿名內(nèi)部類。
private Specification<User> getListSpec(UserDTO userDTO) {
return (root, criteriaQuery, criteriaBuilder) -> {
List<Predicate> predicateList = new ArrayList<>();
// 未刪除標(biāo)識(shí),只查詢未刪除的數(shù)據(jù)
predicateList.add(criteriaBuilder.equal(root.get("deleteFlag"), 0));
// 根據(jù) 用戶名 或 年齡List 查詢
List<Predicate> usernameOrAgePredicate = new ArrayList<>();
String username = userDTO.getUsername();
if (!StringUtils.isEmpty(username)) {
// 用戶名這里,用模糊匹配
usernameOrAgePredicate.add(criteriaBuilder.like(root.get("username"), "%" + username + "%"));
}
List<Integer> ageList = userDTO.getAgeList();
if (!CollectionUtils.isEmpty(ageList)) {
// 下面是一個(gè) IN查詢
CriteriaBuilder.In<Integer> in = criteriaBuilder.in(root.get("age"));
ageList.forEach(in::value);
usernameOrAgePredicate.add(in);
}
/* 下面這一行代碼很重要。
* criteriaBuilder.or(Predicate... restrictions) 接收多個(gè)Predicate,可變參數(shù);
* 這多個(gè) Predicate條件之間,是使用OR連接的;該方法最終返回 一個(gè)Predicate對(duì)象;
*/
predicateList.add(criteriaBuilder.or(usernameOrAgePredicate.toArray(new Predicate[0])));
// 用戶ID List,IN 查詢
List<Integer> userIdList = reqDTO.getUserIdList();
if (!CollectionUtils.isEmpty(userIdList)) {
CriteriaBuilder.In<Integer> in = criteriaBuilder.in(root.get("id"));
userIdList.forEach(in::value);
predicateList.add(in);
}
// 生日時(shí)間段查詢
Date birthDayBegin = reqDTO.getBirthDayBegin();
Date birthDayEnd = reqDTO.getBirthDayEnd();
if (birthDayBegin != null && birthDayEnd != null) {
// DateUtils 是我自定義的一個(gè)工具類
Date begin = DateUtils.startOfDay(birthDayBegin);
Date end = DateUtils.endOfDay(birthDayEnd);
predicateList.add(criteriaBuilder.greaterThanOrEqualTo(root.get("birthDay"), begin));
predicateList.add(criteriaBuilder.lessThanOrEqualTo(root.get("birthDay"), end));
}
// 最終,使用AND 連接 多個(gè) Predicate 查詢條件
return criteriaBuilder.and(predicateList.toArray(new Predicate[0]));
};
}
這樣,我們的動(dòng)態(tài)查詢部分就構(gòu)建完畢了。具體怎么使用呢?如下:
Specification<User> querySpec = this.getListSpec(userDTO); List<User> userList = userDAO.findAll(querySpec);
就這樣,我們就執(zhí)行了一次動(dòng)態(tài)查詢,并獲取到了結(jié)果。
上面的動(dòng)態(tài)查詢,實(shí)際上等價(jià)于下面的偽代碼:
SELECT *
FROM user
WHERE user.deleteFlag = 0
AND ( user.username like '%{username}%' OR user.age IN ageList )
AND user.id IN userIdList
AND user.birthDay > birthDayBegin AND user.birthDay < birthDayEnd ;
當(dāng)前,需要對(duì)應(yīng)值不為空,才會(huì)拼接相應(yīng)的AND條件。
至此,JPA 動(dòng)態(tài)查詢之 AND OR結(jié)合使用,教程到這里就結(jié)束了。以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
javaweb中Filter(過(guò)濾器)的常見應(yīng)用
這篇文章主要介紹了javaweb中Filter的常見應(yīng)用,過(guò)濾器的使用方法,感興趣的小伙伴們可以參考一下2015-12-12
Java使用Apache Commons高效處理CSV文件的操作指南
在 Java 開發(fā)中,CSV(Comma-Separated Values,逗號(hào)分隔值)是一種常見的數(shù)據(jù)存儲(chǔ)格式,廣泛用于數(shù)據(jù)交換和簡(jiǎn)單的存儲(chǔ)任務(wù),本文將介紹Java使用Apache Commons高效處理CSV文件的操作指南,需要的朋友可以參考下2025-03-03
java.nio.file.WatchService?實(shí)時(shí)監(jiān)控文件變化的示例代碼
在?Java?語(yǔ)言中,從?JDK7?開始,新增了java.nio.file.WatchService類,用來(lái)實(shí)時(shí)監(jiān)控文件的變化,這篇文章主要介紹了java.nio.file.WatchService?實(shí)時(shí)監(jiān)控文件變化,需要的朋友可以參考下2022-05-05

