您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“Jpa Specification怎么實(shí)現(xiàn)and和or同時(shí)使用查詢”,內(nèi)容簡(jiǎn)而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Jpa Specification怎么實(shí)現(xiàn)and和or同時(shí)使用查詢”這篇文章吧。
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í)體類
現(xiàn)在,我負(fù)責(zé)開發(fā)的項(xiàng)目中,使用JPA作為ORM框架。有了JPA,一行SQL都沒寫過。在昨天,有一個(gè)新的需求,需要進(jìn)行動(dòng)態(tài)查詢,這個(gè)簡(jiǎn)單。但是有一個(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è)場(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ù)庫實(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 Specification怎么實(shí)現(xiàn)and和or同時(shí)使用查詢”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。