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語句
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語句
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都沒寫過。在昨天,有一個(gè)新的需求,需要進(jìn)行動(dòng)態(tài)查詢,這個(gè)簡單。但是有一個(gè)地方需要AND、OR結(jié)合使用,這里,我將記錄下我的理解與寫法,希望能幫助到大家。
問題描述
需要根據(jù)條件進(jìn)行動(dòng)態(tài)查詢,實(shí)現(xiàn)一條類似下文的語句:
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è)場景也是很常見的。這里,我將分享下具體的寫法。以我們項(xiàng)目中的例子為例。
代碼示例
JPA的動(dòng)態(tài)查詢,這里我們使用的方式是:實(shí)現(xiàn) Specification 接口,自定義動(dòng)態(tài)查詢邏輯。這也是我個(gè)人比較推薦的方式。JPA的使用、Specification 接口基礎(chǔ)知識(shí)這里我就不講了。有興趣的朋友可以查閱官方文檔學(xué)習(xí)。
下面,我們首先定義好我們的數(shù)據(jù)庫實(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é)合分頁查詢使用。這里,我不做介紹,看情況要不要后期加上教程
*/
List<User> findAll(Specification<User> querySpec);
}
下面是前端傳過來的動(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;
}
然后,重要的地方來了,我們實(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)文章
Netty事件循環(huán)主邏輯NioEventLoop的run方法分析
這篇文章主要介紹了Netty事件循環(huán)主邏輯NioEventLoop的run方法分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03
javaSE基礎(chǔ)如何通俗的理解javaBean是什么
所謂的Java Bean,就是一個(gè)java類,編譯后成為了一個(gè)后綴名是 .class的文件。這就是Java Bean,很多初學(xué)者,包括當(dāng)年的我自己,總是被這些專有名詞搞的暈頭轉(zhuǎn)向2021-10-10
mybatis-plus實(shí)體類主鍵策略有3種(小結(jié))
這篇文章主要介紹了mybatis-plus實(shí)體類主鍵策略有3種(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
使用SpringAop動(dòng)態(tài)獲取mapper執(zhí)行的SQL,并保存SQL到Log表中
這篇文章主要介紹了使用SpringAop動(dòng)態(tài)獲取mapper執(zhí)行的SQL,并保存SQL到Log表中問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
Java異步調(diào)用轉(zhuǎn)同步方法實(shí)例詳解
這篇文章主要介紹了Java異步調(diào)用轉(zhuǎn)同步方法實(shí)例詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06

