欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring Data JPA中的Specification動(dòng)態(tài)查詢?cè)斀?/h1>
 更新時(shí)間:2023年07月17日 11:07:28   作者:記錄菌  
Specification是一個(gè)設(shè)計(jì)模式,用于企業(yè)級(jí)應(yīng)用開(kāi)發(fā)中,其主要目的是將業(yè)務(wù)規(guī)則從業(yè)務(wù)邏輯中分離出來(lái),在數(shù)據(jù)查詢方面,Specification可以定義復(fù)雜的查詢,使其更易于重用和測(cè)試,這篇文章主要介紹了Spring Data JPA中的Specification動(dòng)態(tài)查詢?cè)斀?需要的朋友可以參考下

寫(xiě)在前面:刷完Spring Data JPA的課后,發(fā)現(xiàn)Specification動(dòng)態(tài)查詢還挺有意思的,還應(yīng)用到了規(guī)約設(shè)計(jì)模式,在此記錄下學(xué)習(xí)過(guò)程和見(jiàn)解。

一、應(yīng)用場(chǎng)景

1. 簡(jiǎn)介

        有時(shí)我們?cè)诓樵兡硞€(gè)實(shí)體的時(shí)候,給定的條件是不固定的,這時(shí)就需要?jiǎng)討B(tài)構(gòu)建相應(yīng)的查詢語(yǔ)句,在Spring Data JPA中可以通過(guò)JpaSpecificationExecutor接口查詢。相比JPQL,其優(yōu)勢(shì)是類型安全,更加的面向?qū)ο蟆?/p>

        Specification是一個(gè)設(shè)計(jì)模式,常常用于企業(yè)級(jí)應(yīng)用開(kāi)發(fā)中,其主要目的是將業(yè)務(wù)規(guī)則從業(yè)務(wù)邏輯中分離出來(lái)。在數(shù)據(jù)查詢方面,Specification可以定義復(fù)雜的查詢,使其更易于重用和測(cè)試。

2. 優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

  • 動(dòng)態(tài)查詢:Specification允許你在運(yùn)行時(shí)構(gòu)建查詢。你可以基于用戶的輸入或程序的狀態(tài)動(dòng)態(tài)地選擇查詢的條件、排序或分組。
  • 復(fù)用:你可以定義一組基本的Specification,然后在不同的查詢中重用它們。這使得你的代碼更加簡(jiǎn)潔,也更易于測(cè)試和維護(hù)。
  • 組合:你可以通過(guò)邏輯運(yùn)算符(如AND和OR)來(lái)組合Specification。這使得你可以輕松地構(gòu)建復(fù)雜的查詢,而無(wú)需編寫(xiě)復(fù)雜的SQL語(yǔ)句。

缺點(diǎn):

  • 學(xué)習(xí)曲線:對(duì)于新手來(lái)說(shuō),理解和使用Specification可能有一定的難度。你需要對(duì)JPA Criteria API有一定的了解,而這個(gè)API本身也相當(dāng)復(fù)雜。
  • 性能:Specification是基于JPA Criteria API的,而這個(gè)API生成的SQL語(yǔ)句可能并不是最優(yōu)的。如果你需要執(zhí)行一些特別復(fù)雜或需要高性能的查詢,直接編寫(xiě)SQL可能會(huì)更好。
  • 靈活性:雖然Specification提供了大量的功能,但它仍然有一些限制。例如,它不支持JOIN ON子句,也不支持某些數(shù)據(jù)庫(kù)特有的功能。

3.mybatis或者mybatisPlus和Specification動(dòng)態(tài)查詢比較(對(duì)標(biāo))

        MyBatis或MyBatis Plus并沒(méi)有直接對(duì)應(yīng)于Spring Data JPA中的Specification動(dòng)態(tài)查詢的功能,但是,通過(guò)其強(qiáng)大的動(dòng)態(tài)SQL功能,可以實(shí)現(xiàn)類似的效果。在MyBatis中,可以使用 <if> 標(biāo)簽來(lái)動(dòng)態(tài)的構(gòu)建SQL查詢。這允許你根據(jù)傳入?yún)?shù)的值動(dòng)態(tài)地添加或移除查詢的一部分。

二、源碼解析

        Specification接口是Spring Data JPA庫(kù)中的一部分。這個(gè)接口定義了一個(gè)toPredicate方法,該方法接收一個(gè)RootCriteriaQueryCriteriaBuilder,并返回一個(gè)Predicate。Predicate是JPA Criteria API中的一個(gè)接口,用于定義查詢的條件。

  • root:查詢的根對(duì)象(查詢的任何屬性都可以從根對(duì)象中獲?。?/li>
  • CriteriaQuery:頂層查詢對(duì)象,自定義查詢方式(了解:一般不用)
  • CriteriaBuilder:查詢的構(gòu)造器,封裝了很多的查詢條件

        在Spring Data JPA中,這個(gè)接口主要被用于JpaSpecificationExecutor接口,這個(gè)接口定義了一些用于執(zhí)行Specification查詢的方法,如findAll(Specification<T> spec)。

   JpaSpecificationExecutor接口的實(shí)現(xiàn)在SimpleJpaRepository類中。例如,findAll(Specification<T> spec)方法的實(shí)現(xiàn)如下:

@Override
public List<T> findAll(@Nullable Specification<T> spec) {
	TypedQuery<T> query = getQuery(spec, Sort.unsorted());
	return query.getResultList();
}

在這個(gè)方法中,首先調(diào)用了getQuery方法來(lái)創(chuàng)建一個(gè)TypedQuery,然后執(zhí)行這個(gè)查詢并返回結(jié)果。

getQuery方法的實(shí)現(xiàn)如下:

protected TypedQuery<T> getQuery(@Nullable Specification<T> spec, Sort sort) {
	CriteriaBuilder builder = entityManager.getCriteriaBuilder();
	CriteriaQuery<T> query = builder.createQuery(getDomainClass());
	Root<T> root = applySpecificationToCriteria(spec, query);
	query.select(root);
	if (sort.isSorted()) {
		query.orderBy(toOrders(sort, root, builder));
	}
	return applyRepositoryMethodMetadata(entityManager.createQuery(query));
}

在這個(gè)方法中,首先創(chuàng)建了一個(gè)CriteriaBuilderCriteriaQuery,然后調(diào)用了applySpecificationToCriteria方法來(lái)應(yīng)用Specification到CriteriaQuery上,然后選擇查詢的結(jié)果,如果有排序的需求,就添加排序條件,最后創(chuàng)建并返回TypedQuery。

applySpecificationToCriteria方法的實(shí)現(xiàn)如下:

private Root<T> applySpecificationToCriteria(@Nullable Specification<T> spec, CriteriaQuery<?> query) {
	Root<T> root = query.from(getDomainClass());
	if (spec == null) {
		return root;
	}
	CriteriaBuilder builder = entityManager.getCriteriaBuilder();
	Predicate predicate = spec.toPredicate(root, query, builder);
	if (predicate != null) {
		query.where(predicate);
	}
	return root;
}

        在這個(gè)方法中,首先創(chuàng)建了一個(gè)Root,然后如果有Specification的話,就調(diào)用toPredicate方法來(lái)創(chuàng)建一個(gè)Predicate,然后添加這個(gè)PredicateCriteriaQuery的where條件中。

        這就是Spring Data JPA中Specification動(dòng)態(tài)查詢的基本實(shí)現(xiàn)。在實(shí)際的應(yīng)用中,我們只需要實(shí)現(xiàn)Specification接口,并提供一個(gè)toPredicate方法來(lái)定義我們的查詢規(guī)則,Spring Data JPA就會(huì)自動(dòng)地為我們執(zhí)行查詢。

三、規(guī)約模式

        規(guī)約模式(Specification Pattern)是一種特殊的設(shè)計(jì)模式,最早由Eric Evans在他的《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》一書(shū)中提出。規(guī)約模式的主要思想是將業(yè)務(wù)規(guī)則從業(yè)務(wù)對(duì)象中分離出來(lái),這樣就可以將這些規(guī)則獨(dú)立地重用和組合。一個(gè)規(guī)約(Specification)是一個(gè)獨(dú)立的業(yè)務(wù)規(guī)則,它通常會(huì)實(shí)現(xiàn)一個(gè)方法(在Java中通常是isSatisfiedBy),該方法接收一個(gè)業(yè)務(wù)對(duì)象,然后檢查這個(gè)對(duì)象是否滿足規(guī)約的條件。

        例如,假設(shè)我們有一個(gè)Customer類,我們需要檢查一個(gè)客戶是否滿足“是VIP客戶”和“注冊(cè)超過(guò)一年”的規(guī)則。首先,我們可以定義一個(gè)IsVip規(guī)約和一個(gè)IsRegisteredForMoreThanOneYear規(guī)約:

public class IsVip implements Specification<Customer> {
    @Override
    public boolean isSatisfiedBy(Customer customer) {
        return customer.isVip();
    }
}
public class IsRegisteredForMoreThanOneYear implements Specification<Customer> {
    @Override
    public boolean isSatisfiedBy(Customer customer) {
        return customer.getRegisteredDate().isBefore(LocalDate.now().minusYears(1));
    }
}

然后,我們可以將這兩個(gè)規(guī)約組合起來(lái),檢查一個(gè)客戶是否滿足這兩個(gè)條件:

Specification<Customer> spec = new IsVip().and(new IsRegisteredForMoreThanOneYear());
boolean isSatisfied = spec.isSatisfiedBy(customer);

我們還可以使用or方法來(lái)組合規(guī)約,檢查一個(gè)客戶是否滿足這兩個(gè)條件中的任意一個(gè):

Specification<Customer> spec = new IsVip().or(new IsRegisteredForMoreThanOneYear());
boolean isSatisfied = spec.isSatisfiedBy(customer);

        在這個(gè)例子中,我們將每個(gè)業(yè)務(wù)規(guī)則封裝為一個(gè)單獨(dú)的規(guī)約,然后使用andor方法將這些規(guī)約組合起來(lái)。這樣做的好處是我們可以將復(fù)雜的業(yè)務(wù)規(guī)則分解為多個(gè)簡(jiǎn)單的規(guī)約,這些規(guī)約可以獨(dú)立地重用和測(cè)試。同時(shí),我們也可以在運(yùn)行時(shí)動(dòng)態(tài)地組合規(guī)約,從而實(shí)現(xiàn)動(dòng)態(tài)的業(yè)務(wù)規(guī)則。

四、實(shí)際應(yīng)用

單個(gè)條件查詢

    /**
     * 根據(jù)條件,查詢單個(gè)對(duì)象
     *
     */
    @Test
    public void testSpec() {
        //匿名內(nèi)部類
        /**
         * 自定義查詢條件
         *      1.實(shí)現(xiàn)Specification接口(提供泛型:查詢的對(duì)象類型)
         *      2.實(shí)現(xiàn)toPredicate方法(構(gòu)造查詢條件)
         *      3.需要借助方法參數(shù)中的兩個(gè)參數(shù)(
         *          root:獲取需要查詢的對(duì)象屬性
         *          CriteriaBuilder:構(gòu)造查詢條件的,內(nèi)部封裝了很多的查詢條件(模糊匹配,精準(zhǔn)匹配)
         *       )
         *  案例:根據(jù)客戶名稱查詢,查詢客戶名為傳智播客的客戶
         *          查詢條件
         *              1.查詢方式
         *                  cb對(duì)象
         *              2.比較的屬性名稱
         *                  root對(duì)象
         *
         */
        Specification<Customer> spec = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                //1.獲取比較的屬性
                Path<Object> custName = root.get("custId");
                //2.構(gòu)造查詢條件  :    select * from cst_customer where cust_name = '傳智播客'
                /**
                 * 第一個(gè)參數(shù):需要比較的屬性(path對(duì)象)
                 * 第二個(gè)參數(shù):當(dāng)前需要比較的取值
                 */
                Predicate predicate = cb.equal(custName, "傳智播客");//進(jìn)行精準(zhǔn)的匹配  (比較的屬性,比較的屬性的取值)
                return predicate;
            }
        };
        Customer customer = customerDao.findOne(spec);
        System.out.println(customer);
    }

多條件查詢

    /**
     * 多條件查詢
     *      案例:根據(jù)客戶名(傳智播客)和客戶所屬行業(yè)查詢(it教育)
     *
     */
    @Test
    public void testSpec1() {
        /**
         *  root:獲取屬性
         *      客戶名
         *      所屬行業(yè)
         *  cb:構(gòu)造查詢
         *      1.構(gòu)造客戶名的精準(zhǔn)匹配查詢
         *      2.構(gòu)造所屬行業(yè)的精準(zhǔn)匹配查詢
         *      3.將以上兩個(gè)查詢聯(lián)系起來(lái)
         */
        Specification<Customer> spec = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Path<Object> custName = root.get("custName");//客戶名
                Path<Object> custIndustry = root.get("custIndustry");//所屬行業(yè)
                //構(gòu)造查詢
                //1.構(gòu)造客戶名的精準(zhǔn)匹配查詢
                Predicate p1 = cb.equal(custName, "傳智播客");//第一個(gè)參數(shù),path(屬性),第二個(gè)參數(shù),屬性的取值
                //2..構(gòu)造所屬行業(yè)的精準(zhǔn)匹配查詢
                Predicate p2 = cb.equal(custIndustry, "it教育");
                //3.將多個(gè)查詢條件組合到一起:組合(滿足條件一并且滿足條件二:與關(guān)系,滿足條件一或滿足條件二即可:或關(guān)系)
                Predicate and = cb.and(p1, p2);//以與的形式拼接多個(gè)查詢條件
                // cb.or();//以或的形式拼接多個(gè)查詢條件
                return and;
            }
        };
        Customer customer = customerDao.findOne(spec);
        System.out.println(customer);
    }

模糊查詢

/**
     * 案例:完成根據(jù)客戶名稱的模糊匹配,返回客戶列表
     *      客戶名稱以 '傳智播客‘ 開(kāi)頭
     *
     * equal :直接的到path對(duì)象(屬性),然后進(jìn)行比較即可
     * gt,lt,ge,le,like : 得到path對(duì)象,根據(jù)path指定比較的參數(shù)類型,再去進(jìn)行比較
     *      指定參數(shù)類型:path.as(類型的字節(jié)碼對(duì)象)
     */
    @Test
    public void testSpec3() {
        //構(gòu)造查詢條件
        Specification<Customer> spec = new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                //查詢屬性:客戶名
                Path<Object> custName = root.get("custName");
                //查詢方式:模糊匹配
                Predicate like = cb.like(custName.as(String.class), "傳智播客%");
                return like;
            }
        };
//        List<Customer> list = customerDao.findAll(spec);
//        for (Customer customer : list) {
//            System.out.println(customer);
//        }
        //添加排序
        //創(chuàng)建排序?qū)ο?需要調(diào)用構(gòu)造方法實(shí)例化sort對(duì)象
        //第一個(gè)參數(shù):排序的順序(倒序,正序)
        //   Sort.Direction.DESC:倒序
        //   Sort.Direction.ASC : 升序
        //第二個(gè)參數(shù):排序的屬性名稱
        Sort sort = new Sort(Sort.Direction.DESC,"custId");
        List<Customer> list = customerDao.findAll(spec, sort);
        for (Customer customer : list) {
            System.out.println(customer);
        }
    }
 

分頁(yè)查詢

 /**
     * 分頁(yè)查詢
     *      Specification: 查詢條件
     *      Pageable:分頁(yè)參數(shù)
     *          分頁(yè)參數(shù):查詢的頁(yè)碼,每頁(yè)查詢的條數(shù)
     *          findAll(Specification,Pageable):帶有條件的分頁(yè)
     *          findAll(Pageable):沒(méi)有條件的分頁(yè)
     *  返回:Page(springDataJpa為我們封裝好的pageBean對(duì)象,數(shù)據(jù)列表,共條數(shù))
     */
    @Test
    public void testSpec4() {
        Specification spec = null;
        //PageRequest對(duì)象是Pageable接口的實(shí)現(xiàn)類
        /**
         * 創(chuàng)建PageRequest的過(guò)程中,需要調(diào)用他的構(gòu)造方法傳入兩個(gè)參數(shù)
         *      第一個(gè)參數(shù):當(dāng)前查詢的頁(yè)數(shù)(從0開(kāi)始)
         *      第二個(gè)參數(shù):每頁(yè)查詢的數(shù)量
         */
        Pageable pageable = new PageRequest(0,2);
        //分頁(yè)查詢
        Page<Customer> page = customerDao.findAll(null, pageable);
        System.out.println(page.getContent()); //得到數(shù)據(jù)集合列表
        System.out.println(page.getTotalElements());//得到總條數(shù)
        System.out.println(page.getTotalPages());//得到總頁(yè)數(shù)
    }

到此這篇關(guān)于Spring Data JPA中的Specification動(dòng)態(tài)查詢?cè)斀獾奈恼戮徒榻B到這了,更多相關(guān)Spring Data JPA Specification動(dòng)態(tài)查詢內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot配置Jackson返回統(tǒng)一默認(rèn)值的實(shí)現(xiàn)示例

    springboot配置Jackson返回統(tǒng)一默認(rèn)值的實(shí)現(xiàn)示例

    在項(xiàng)目開(kāi)發(fā)中,我們返回的數(shù)據(jù)或者對(duì)象沒(méi)有的時(shí)候一般直接返回的null,那么如何返回統(tǒng)一默認(rèn)值,感興趣的可以了解一下
    2021-07-07
  • @Autowired 自動(dòng)注入接口失敗的原因及解決

    @Autowired 自動(dòng)注入接口失敗的原因及解決

    這篇文章主要介紹了@Autowired 自動(dòng)注入接口失敗的原因及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • SpringBoot實(shí)現(xiàn)配置文件加密的方案分享

    SpringBoot實(shí)現(xiàn)配置文件加密的方案分享

    項(xiàng)目的數(shù)據(jù)庫(kù)密碼、redis 密碼等明文展示在配置文件中會(huì)有潛在的風(fēng)險(xiǎn),因此采用合適的安全防護(hù)措施是有必要的,下面小編就為大家介紹一下SpringBoot中配置文件加密的方法,希望對(duì)大家有所幫助
    2023-11-11
  • Java框架搭建之Maven、Mybatis、Spring MVC整合搭建(圖文)

    Java框架搭建之Maven、Mybatis、Spring MVC整合搭建(圖文)

    這篇文章主要介紹了Java框架搭建之Maven、Mybatis、Spring MVC整合搭建(圖文),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • Java圖像之自定義角度旋轉(zhuǎn)(實(shí)例)

    Java圖像之自定義角度旋轉(zhuǎn)(實(shí)例)

    這篇文章主要介紹了Java圖像之自定義角度旋轉(zhuǎn)(實(shí)例),需要的朋友可以參考下
    2017-09-09
  • Java Spring使用hutool的HttpRequest發(fā)送請(qǐng)求的幾種方式

    Java Spring使用hutool的HttpRequest發(fā)送請(qǐng)求的幾種方式

    文章介紹了Hutool庫(kù)中用于發(fā)送HTTP請(qǐng)求的工具,包括添加依賴、發(fā)送GET和POST請(qǐng)求的方法,以及GET請(qǐng)求的不同參數(shù)傳遞方式,感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • Java自動(dòng)釋放鎖的三種實(shí)現(xiàn)方案

    Java自動(dòng)釋放鎖的三種實(shí)現(xiàn)方案

    在筆者面試過(guò)程時(shí),經(jīng)常會(huì)被問(wèn)到各種各樣的鎖,如樂(lè)觀鎖、讀寫(xiě)鎖等等,非常繁多,下面這篇文章主要給大家介紹了關(guān)于Java自動(dòng)釋放鎖的三種實(shí)現(xiàn)方案,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06
  • 在SpringBoot中集成H2數(shù)據(jù)庫(kù)的完整指南

    在SpringBoot中集成H2數(shù)據(jù)庫(kù)的完整指南

    Spring Boot是一個(gè)簡(jiǎn)化企業(yè)級(jí)Java應(yīng)用程序開(kāi)發(fā)的強(qiáng)大框架,H2數(shù)據(jù)庫(kù)是一個(gè)輕量級(jí)的、開(kāi)源的SQL數(shù)據(jù)庫(kù),非常適合用于開(kāi)發(fā)和測(cè)試,本文將指導(dǎo)您如何在Spring Boot應(yīng)用程序中集成H2數(shù)據(jù)庫(kù),并探索一些高級(jí)配置選項(xiàng),需要的朋友可以參考下
    2024-10-10
  • MyBatis?SQL映射文件的作用和結(jié)構(gòu)詳解

    MyBatis?SQL映射文件的作用和結(jié)構(gòu)詳解

    MyBatisSQL映射文件定義了SQL語(yǔ)句和參數(shù)映射規(guī)則,用于將Java代碼與數(shù)據(jù)庫(kù)操作解耦,實(shí)現(xiàn)SQL語(yǔ)句的靈活配置和動(dòng)態(tài)生成
    2025-03-03
  • Java中斷線程的方法

    Java中斷線程的方法

    這篇文章主要介紹了Java中斷線程的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-05-05

最新評(píng)論