您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“Jpa Specification怎么實現and和or同時使用查詢”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“Jpa Specification怎么實現and和or同時使用查詢”這篇文章吧。
UserServiceImpl 類,service實現類
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 自定義的封裝參數的類型 //RongUser 實體類型 @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(),方法里面必須是對應的實體屬性 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(); //單獨使用 and 或者 or 時 返回 //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 自己的實體類型 public interface RongUserRepository extends JpaRepository<RongUser,Integer> , JpaSpecificationExecutor<RongUser> { }
注意:使用Specification之前,RongUserRepository接口必須實現JpaSpecificationExecutor<RongUser>,RongUser對應表的實體類
現在,我負責開發的項目中,使用JPA作為ORM框架。有了JPA,一行SQL都沒寫過。在昨天,有一個新的需求,需要進行動態查詢,這個簡單。但是有一個地方需要AND、OR結合使用,這里,我將記錄下我的理解與寫法,希望能幫助到大家。
需要根據條件進行動態查詢,實現一條類似下文的語句:
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)
上面是幾行偽代碼。意思是,幾個條件之間是AND連接,但是其中的部分條件,是使用OR連接的。
在我們的實際項目中,這個場景也是很常見的。這里,我將分享下具體的寫法。以我們項目中的例子為例。
JPA的動態查詢,這里我們使用的方式是:實現 Specification 接口,自定義動態查詢邏輯。這也是我個人比較推薦的方式。JPA的使用、Specification 接口基礎知識這里我就不講了。有興趣的朋友可以查閱官方文檔學習。
下面,我們首先定義好我們的數據庫實體:
@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; /** * 刪除標識; 0 - 未刪除,1 - 已刪除 */ private Integer deleteFlag; }
然后定義好DAO層接口:
@Repository public interface UserDAO extends JpaRepository<User, Long> { /** * 其實,這個功能一般用作 list 接口使用,一般結合分頁查詢使用。這里,我不做介紹,看情況要不要后期加上教程 */ List<User> findAll(Specification<User> querySpec); }
下面是前端傳過來的動態查詢的參數對象:
@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; /** * 生日,結束 */ @JsonFormat(pattern = "yyyy-MM-dd", locale = "zh", timezone = "GMT+8") private Date birthDayEnd; }
然后,重要的地方來了,我們實現 Specification 接口,定義查詢邏輯:
在實際代碼操作中,我會將這部分邏輯抽離為一個單獨的方法,使用lambda表達式完成,其實也就是匿名內部類。
private Specification<User> getListSpec(UserDTO userDTO) { return (root, criteriaQuery, criteriaBuilder) -> { List<Predicate> predicateList = new ArrayList<>(); // 未刪除標識,只查詢未刪除的數據 predicateList.add(criteriaBuilder.equal(root.get("deleteFlag"), 0)); // 根據 用戶名 或 年齡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)) { // 下面是一個 IN查詢 CriteriaBuilder.In<Integer> in = criteriaBuilder.in(root.get("age")); ageList.forEach(in::value); usernameOrAgePredicate.add(in); } /* 下面這一行代碼很重要。 * criteriaBuilder.or(Predicate... restrictions) 接收多個Predicate,可變參數; * 這多個 Predicate條件之間,是使用OR連接的;該方法最終返回 一個Predicate對象; */ 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); } // 生日時間段查詢 Date birthDayBegin = reqDTO.getBirthDayBegin(); Date birthDayEnd = reqDTO.getBirthDayEnd(); if (birthDayBegin != null && birthDayEnd != null) { // DateUtils 是我自定義的一個工具類 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 連接 多個 Predicate 查詢條件 return criteriaBuilder.and(predicateList.toArray(new Predicate[0])); }; }
這樣,我們的動態查詢部分就構建完畢了。具體怎么使用呢?如下:
Specification<User> querySpec = this.getListSpec(userDTO); List<User> userList = userDAO.findAll(querySpec);
就這樣,我們就執行了一次動態查詢,并獲取到了結果。
上面的動態查詢,實際上等價于下面的偽代碼:
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 ;
當前,需要對應值不為空,才會拼接相應的AND條件。
以上是“Jpa Specification怎么實現and和or同時使用查詢”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。