溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Jpa?Specification怎么實(shí)現(xiàn)and和or同時(shí)使用查詢

發(fā)布時(shí)間:2021-11-23 11:07:30 來源:億速云 閱讀:165 作者:小新 欄目:開發(fā)技術(shù)

這篇文章主要為大家展示了“Jpa Specification怎么實(shí)現(xiàn)and和or同時(shí)使用查詢”,內(nèi)容簡(jiǎn)而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“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è)簡(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è)資訊頻道!

向AI問一下細(xì)節(jié)

免責(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)容。

AI