最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java動(dòng)態(tài)構(gòu)建數(shù)據(jù)庫(kù)復(fù)雜查詢教程

 更新時(shí)間:2021年11月25日 14:10:30   作者:廈門(mén)在乎科技  
這篇文章主要介紹了java動(dòng)態(tài)構(gòu)建數(shù)據(jù)庫(kù)復(fù)雜查詢的實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

有的時(shí)候,你需要?jiǎng)討B(tài)構(gòu)建一個(gè)比較復(fù)雜的查詢條件,傳入數(shù)據(jù)庫(kù)中進(jìn)行查詢。而條件本身可能來(lái)自前端請(qǐng)求或者配置文件。那么這個(gè)時(shí)候,表達(dá)式樹(shù),就可以幫助到你。

Where當(dāng)中可以傳入固定的條件

以下是一個(gè)簡(jiǎn)單的單元測(cè)試用例。接下來(lái),我們將這個(gè)測(cè)試用例改的面目全非。

[Test]
public void Normal()
{
    var re = Enumerable.Range(0, 10).AsQueryable() // 0-9
        .Where(x => x >= 1 && x < 5).ToList(); // 1 2 3 4
    var expectation = Enumerable.Range(1, 4); // 1 2 3 4
    re.Should().BeEquivalentTo(expectation);
}

Queryable中的Where就是一種表達(dá)式樹(shù)

由于是 Queryable 的關(guān)系,所以Where當(dāng)中的其實(shí)是一個(gè)表達(dá)式,那么我們把它單獨(dú)定義出來(lái),順便水一下文章的長(zhǎng)度。

[Test]
public void Expression00()
{
    Expression<Func<int, bool>> filter = x => x >= 1 && x < 5;
    var re = Enumerable.Range(0, 10).AsQueryable()
        .Where(filter).ToList();
    var expectation = Enumerable.Range(1, 4);
    re.Should().BeEquivalentTo(expectation);
}

表達(dá)式可以通過(guò)Lambda隱式轉(zhuǎn)換

Expression 右側(cè)是一個(gè) Lambda ,所以可以捕獲上下文中的變量。
這樣你便可以把 minValue 和 maxValue 單獨(dú)定義出來(lái)。
于是乎你可以從其他地方來(lái)獲取 minValue 和 maxValue 來(lái)改變 filter。

[Test]
public void Expression01()
{
    var minValue = 1;
    var maxValue = 5;
    Expression<Func<int, bool>> filter = x => x >= minValue && x < maxValue;
    var re = Enumerable.Range(0, 10).AsQueryable()
        .Where(filter).ToList();
    var expectation = Enumerable.Range(1, 4);
    re.Should().BeEquivalentTo(expectation);
}

可以使用方法創(chuàng)建表達(dá)式

那既然這樣,我們也可以使用一個(gè)方法來(lái)創(chuàng)建 Expression。
這個(gè)方法,實(shí)際上就可以認(rèn)為是這個(gè) Expression 的工廠方法。

[Test]
public void Expression02()
{
    var filter = CreateFilter(1, 5);
    var re = Enumerable.Range(0, 10).AsQueryable()
        .Where(filter).ToList();
    var expectation = Enumerable.Range(1, 4);
    re.Should().BeEquivalentTo(expectation); 
    Expression<Func<int, bool>> CreateFilter(int minValue, int maxValue)
    {
        return x => x >= minValue && x < maxValue;
    }
}

通過(guò)Func可以更加靈活的組合條件

那可以使用 minValue 和 maxValue 作為參數(shù)來(lái)制作工廠方法,那么用委托當(dāng)然也可以。
于是,我們可以把左邊和右邊分別定義成兩個(gè) Func,從而由外部來(lái)決定左右具體的比較方式。

[Test]
public void Expression03()
{
    var filter = CreateFilter(x => x >= 1, x => x < 5);
    var re = Enumerable.Range(0, 10).AsQueryable()
        .Where(filter).ToList();
    var expectation = Enumerable.Range(1, 4);
    re.Should().BeEquivalentTo(expectation); 
    Expression<Func<int, bool>> CreateFilter(Func<int, bool> leftFunc, Func<int, bool> rightFunc)
    {
        return x => leftFunc.Invoke(x) && rightFunc.Invoke(x);
    }
}

也可以手動(dòng)構(gòu)建表達(dá)式

實(shí)際上,左右兩個(gè)不僅僅是兩個(gè)Func,其實(shí)也可以直接是兩個(gè)表達(dá)式。
不過(guò)稍微有點(diǎn)不同的是,表達(dá)式的合并需要用 Expression 類型中的相關(guān)方法創(chuàng)建。
我們可以發(fā)現(xiàn),調(diào)用的地方這次其實(shí)沒(méi)有任何改變,因?yàn)?Lambda 既可以隱式轉(zhuǎn)換為 Func 也可以隱式轉(zhuǎn)換為 Expression。
每個(gè)方法的意思可以從注釋中看出。

[Test]
public void Expression04()
{
    var filter = CreateFilter(x => x >= 1, x => x < 5);
    var re = Enumerable.Range(0, 10).AsQueryable()
        .Where(filter).ToList();
    var expectation = Enumerable.Range(1, 4);
    re.Should().BeEquivalentTo(expectation); 
    Expression<Func<int, bool>> CreateFilter(Expression<Func<int, bool>> leftFunc,
        Expression<Func<int, bool>> rightFunc)
    {
        // x
        var pExp = Expression.Parameter(typeof(int), "x");
        // (a => leftFunc(a))(x)
        var leftExp = Expression.Invoke(leftFunc, pExp);
        // (a => rightFunc(a))(x)
        var rightExp = Expression.Invoke(rightFunc, pExp);
        // (a => leftFunc(a))(x) && (a => rightFunc(a))(x)
        var bodyExp = Expression.AndAlso(leftExp, rightExp);
        // x => (a => leftFunc(a))(x) && (a => rightFunc(a))(x)
        var resultExp = Expression.Lambda<Func<int, bool>>(bodyExp, pExp);
        return resultExp;
    }
}

引入表達(dá)式的解構(gòu)

使其更加簡(jiǎn)單

但是,上面的方法,其實(shí)可以再優(yōu)化一下。避免對(duì)左右表達(dá)式的直接調(diào)用。
使用一個(gè)叫做 Unwrap 的方法,可以將 Lambda Expression 解構(gòu)成只包含 Body 部分的表達(dá)式。
這是一個(gè)自定義的擴(kuò)展方法,你可以通過(guò)?ObjectVisitor?來(lái)引入這個(gè)方法。

[Test]
public void Expression05()
{
    var filter = CreateFilter(x => x >= 1, x => x < 5);
    var re = Enumerable.Range(0, 10).AsQueryable()
        .Where(filter).ToList();
    var expectation = Enumerable.Range(1, 4);
    re.Should().BeEquivalentTo(expectation); 
    Expression<Func<int, bool>> CreateFilter(Expression<Func<int, bool>> leftFunc,
        Expression<Func<int, bool>> rightFunc)
    {
        // x
        var pExp = Expression.Parameter(typeof(int), "x");
        // leftFunc(x)
        var leftExp = leftFunc.Unwrap(pExp);
        // rightFunc(x)
        var rightExp = rightFunc.Unwrap(pExp);
        // leftFunc(x) && rightFunc(x)
        var bodyExp = Expression.AndAlso(leftExp, rightExp);
        // x => leftFunc(x) && rightFunc(x)
        var resultExp = Expression.Lambda<Func<int, bool>>(bodyExp, pExp);
        return resultExp;
    }
}

可以拼接更多的表達(dá)式

我們可以再優(yōu)化以下,把 CreateFilter 方法擴(kuò)展為支持多個(gè)子表達(dá)式和可自定義子表達(dá)式的連接方式。
于是,我們就可以得到一個(gè) JoinSubFilters 方法。

[Test]
public void Expression06()
{
    var filter = JoinSubFilters(Expression.AndAlso, x => x >= 1, x => x < 5);
    var re = Enumerable.Range(0, 10).AsQueryable()
        .Where(filter).ToList();
    var expectation = Enumerable.Range(1, 4);
    re.Should().BeEquivalentTo(expectation); 
    Expression<Func<int, bool>> JoinSubFilters(Func<Expression, Expression, Expression> expJoiner,
        params Expression<Func<int, bool>>[] subFilters)
    {
        // x
        var pExp = Expression.Parameter(typeof(int), "x");
        var result = subFilters[0];
        foreach (var sub in subFilters[1..])
        {
            var leftExp = result.Unwrap(pExp);
            var rightExp = sub.Unwrap(pExp);
            var bodyExp = expJoiner(leftExp, rightExp);
            result = Expression.Lambda<Func<int, bool>>(bodyExp, pExp);
        }
        return result;
    }
}

使用工廠方法來(lái)代替固定的子表達(dá)式

有了前面的經(jīng)驗(yàn),我們知道。其實(shí)x => x >= 1這個(gè)表達(dá)式可以通過(guò)一個(gè)工廠方法來(lái)建。
所以,我們使用一個(gè) CreateMinValueFilter 來(lái)創(chuàng)建這個(gè)表達(dá)式。

[Test]
public void Expression07()
{
    var filter = JoinSubFilters(Expression.AndAlso,
        CreateMinValueFilter(1),
        x => x < 5);
    var re = Enumerable.Range(0, 10).AsQueryable()
        .Where(filter).ToList();
    var expectation = Enumerable.Range(1, 4);
    re.Should().BeEquivalentTo(expectation);
 
    Expression<Func<int, bool>> CreateMinValueFilter(int minValue)
    {
        return x => x >= minValue;
    }
    Expression<Func<int, bool>> JoinSubFilters(Func<Expression, Expression, Expression> expJoiner,
        params Expression<Func<int, bool>>[] subFilters)
    {
        // x
        var pExp = Expression.Parameter(typeof(int), "x");
        var result = subFilters[0];
        foreach (var sub in subFilters[1..])
        {
            var leftExp = result.Unwrap(pExp);
            var rightExp = sub.Unwrap(pExp);
            var bodyExp = expJoiner(leftExp, rightExp);
 
            result = Expression.Lambda<Func<int, bool>>(bodyExp, pExp);
        } 
        return result;
    }
}

工廠方法內(nèi)部也可以使用Expression手動(dòng)創(chuàng)建

當(dāng)然,可以只使用 Expression 相關(guān)的方法來(lái)創(chuàng)建x => x >= 1。

[Test]
public void Expression08()
{
    var filter = JoinSubFilters(Expression.AndAlso,
        CreateMinValueFilter(1),
        x => x < 5);
    var re = Enumerable.Range(0, 10).AsQueryable()
        .Where(filter).ToList();
    var expectation = Enumerable.Range(1, 4);
    re.Should().BeEquivalentTo(expectation);
 
    Expression<Func<int, bool>> CreateMinValueFilter(int minValue)
    {
        // x
        var pExp = Expression.Parameter(typeof(int), "x");
        // minValue
        var rightExp = Expression.Constant(minValue);
        // x >= minValue
        var bodyExp = Expression.GreaterThanOrEqual(pExp, rightExp);
        var result = Expression.Lambda<Func<int, bool>>(bodyExp, pExp);
        return result;
    } 
    Expression<Func<int, bool>> JoinSubFilters(Func<Expression, Expression, Expression> expJoiner,
        params Expression<Func<int, bool>>[] subFilters)
    {
        // x
        var pExp = Expression.Parameter(typeof(int), "x");
        var result = subFilters[0];
        foreach (var sub in subFilters[1..])
        {
            var leftExp = result.Unwrap(pExp);
            var rightExp = sub.Unwrap(pExp);
            var bodyExp = expJoiner(leftExp, rightExp);
 
            result = Expression.Lambda<Func<int, bool>>(bodyExp, pExp);
        } 
        return result;
    }
}

同理,子表達(dá)式都可以如此創(chuàng)建

那既然都用了 Expression 來(lái)創(chuàng)建子表達(dá)式了,那就干脆再做一點(diǎn)點(diǎn)改進(jìn),把x => x < 5也做成從工廠方法獲取。

[Test]
public void Expression09()
{
    var filter = JoinSubFilters(Expression.AndAlso,
        CreateValueCompareFilter(Expression.GreaterThanOrEqual, 1),
        CreateValueCompareFilter(Expression.LessThan, 5));
    var re = Enumerable.Range(0, 10).AsQueryable()
        .Where(filter).ToList();
    var expectation = Enumerable.Range(1, 4);
    re.Should().BeEquivalentTo(expectation);
    Expression<Func<int, bool>> CreateValueCompareFilter(Func<Expression, Expression, Expression> comparerFunc,
        int rightValue)
    {
        var pExp = Expression.Parameter(typeof(int), "x");
        var rightExp = Expression.Constant(rightValue);
        var bodyExp = comparerFunc(pExp, rightExp);
        var result = Expression.Lambda<Func<int, bool>>(bodyExp, pExp);
        return result;
    } 
    Expression<Func<int, bool>> JoinSubFilters(Func<Expression, Expression, Expression> expJoiner,
        params Expression<Func<int, bool>>[] subFilters)
    {
        // x
        var pExp = Expression.Parameter(typeof(int), "x");
        var result = subFilters[0];
        foreach (var sub in subFilters[1..])
        {
            var leftExp = result.Unwrap(pExp);
            var rightExp = sub.Unwrap(pExp);
            var bodyExp = expJoiner(leftExp, rightExp);
 
            result = Expression.Lambda<Func<int, bool>>(bodyExp, pExp);
        } 
        return result;
    }
}

加入一點(diǎn)點(diǎn)配置,就完成了

最后,我們?cè)诎炎颖磉_(dá)式的創(chuàng)建通過(guò)一點(diǎn)點(diǎn)小技巧。通過(guò)外部參數(shù)來(lái)決定。就基本完成了一個(gè)多 And 的值比較查詢條件的動(dòng)態(tài)構(gòu)建。

[Test]
public void Expression10()
{
    var config = new Dictionary<string, int>
    {
        { ">=", 1 },
        { "<", 5 }
    };
    var subFilters = config.Select(x => CreateValueCompareFilter(MapConfig(x.Key), x.Value)).ToArray();
    var filter = JoinSubFilters(Expression.AndAlso, subFilters);
    var re = Enumerable.Range(0, 10).AsQueryable()
        .Where(filter).ToList();
    var expectation = Enumerable.Range(1, 4);
    re.Should().BeEquivalentTo(expectation); 
    Func<Expression, Expression, Expression> MapConfig(string op)
    {
        return op switch
        {
            ">=" => Expression.GreaterThanOrEqual,
            "<" => Expression.LessThan,
            _ => throw new ArgumentOutOfRangeException(nameof(op))
        };
    } 
    Expression<Func<int, bool>> CreateValueCompareFilter(Func<Expression, Expression, Expression> comparerFunc,
        int rightValue)
    {
        var pExp = Expression.Parameter(typeof(int), "x");
        var rightExp = Expression.Constant(rightValue);
        var bodyExp = comparerFunc(pExp, rightExp);
        var result = Expression.Lambda<Func<int, bool>>(bodyExp, pExp);
        return result;
    }
    Expression<Func<int, bool>> JoinSubFilters(Func<Expression, Expression, Expression> expJoiner,
        params Expression<Func<int, bool>>[] subFilters)
    {
        // x
        var pExp = Expression.Parameter(typeof(int), "x");
        var result = subFilters[0];
        foreach (var sub in subFilters[1..])
        {
            var leftExp = result.Unwrap(pExp);
            var rightExp = sub.Unwrap(pExp);
            var bodyExp = expJoiner(leftExp, rightExp);
 
            result = Expression.Lambda<Func<int, bool>>(bodyExp, pExp);
        } 
        return result;
    }
}

以上就是java動(dòng)態(tài)構(gòu)建數(shù)據(jù)庫(kù)復(fù)雜查詢實(shí)現(xiàn)示例的詳細(xì)內(nèi)容,更多關(guān)于動(dòng)態(tài)構(gòu)建數(shù)據(jù)庫(kù)復(fù)雜查詢的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解Java中的四種引用類型(強(qiáng)軟弱虛)

    詳解Java中的四種引用類型(強(qiáng)軟弱虛)

    Java中的引用類型主要分為四種,分別是強(qiáng)引用、軟引用、弱引用和虛引用,這篇文章主要為大家詳細(xì)介紹了四者的使用與區(qū)別,需要的小伙伴可以參考下
    2023-10-10
  • 詳解Spring?Security?捕獲?filter?層面異常返回我們自定義的內(nèi)容

    詳解Spring?Security?捕獲?filter?層面異常返回我們自定義的內(nèi)容

    Spring?的異常會(huì)轉(zhuǎn)發(fā)到?BasicErrorController?中進(jìn)行異常寫(xiě)入,然后才會(huì)返回客戶端。所以,我們可以在?BasicErrorController?對(duì)?filter異常進(jìn)行捕獲并處理,下面通過(guò)本文給大家介紹Spring?Security?捕獲?filter?層面異常,返回我們自定義的內(nèi)容,感興趣的朋友一起看看吧
    2022-05-05
  • Java大文本并行計(jì)算實(shí)現(xiàn)過(guò)程解析

    Java大文本并行計(jì)算實(shí)現(xiàn)過(guò)程解析

    這篇文章主要介紹了Java大文本并行計(jì)算如何實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • SpringBoot+微信小程序?qū)崿F(xiàn)文件上傳與下載功能詳解

    SpringBoot+微信小程序?qū)崿F(xiàn)文件上傳與下載功能詳解

    這篇文章主要為大家介紹了SpringBoot整合微信小程序?qū)崿F(xiàn)文件上傳與下載功能,文中的實(shí)現(xiàn)步驟講解詳細(xì),快跟隨小編一起學(xué)習(xí)一下吧
    2022-03-03
  • 一文教你使用Java?Calendar類進(jìn)行日期計(jì)算

    一文教你使用Java?Calendar類進(jìn)行日期計(jì)算

    在日常開(kāi)發(fā)中,我們常常需要進(jìn)行日期計(jì)算,比如計(jì)算兩個(gè)日期之間的天數(shù)、月數(shù),在Java中,我們可以使用Java?Calendar類來(lái)進(jìn)行日期計(jì)算,下面就跟隨小編一起來(lái)學(xué)習(xí)一下吧
    2023-10-10
  • mybatis-plus?分頁(yè)類型轉(zhuǎn)換工具類

    mybatis-plus?分頁(yè)類型轉(zhuǎn)換工具類

    用mybatis-plus?的分頁(yè)對(duì)象的時(shí)候,因?yàn)橛胢ybatis-puls?查詢出來(lái)的分頁(yè)對(duì)象的records里的泛型是實(shí)體,有時(shí)候需要將實(shí)體轉(zhuǎn)換為前端展示的對(duì)象,所有寫(xiě)了一個(gè)分頁(yè)數(shù)據(jù)的類型轉(zhuǎn)換工具,解決這個(gè)問(wèn)題,對(duì)mybatis-plus?分頁(yè)工具類相關(guān)知識(shí)感興趣的朋友一起看看吧
    2022-03-03
  • SpringBoot之@Value獲取application.properties配置無(wú)效的解決

    SpringBoot之@Value獲取application.properties配置無(wú)效的解決

    這篇文章主要介紹了SpringBoot之@Value獲取application.properties配置無(wú)效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • SpringBoot+fileUpload獲取文件上傳進(jìn)度

    SpringBoot+fileUpload獲取文件上傳進(jìn)度

    這篇文章主要為大家詳細(xì)介紹了SpringBoot+fileUpload獲取文件上傳進(jìn)度,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • 在SpringBoot框架下實(shí)現(xiàn)Excel導(dǎo)入導(dǎo)出的方法詳解

    在SpringBoot框架下實(shí)現(xiàn)Excel導(dǎo)入導(dǎo)出的方法詳解

    SpringBoot是由Pivotal團(tuán)隊(duì)提供的全新框架,其設(shè)計(jì)目的是用來(lái)簡(jiǎn)化新Spring應(yīng)用的初始搭建以及開(kāi)發(fā)過(guò)程,今天我們就使用純前對(duì)按表格控件帶大家了解,如何在Spring Boot框架下實(shí)現(xiàn)Excel服務(wù)端導(dǎo)入導(dǎo)出,需要的朋友可以參考下
    2023-06-06
  • JAVA基礎(chǔ) 語(yǔ)句標(biāo)簽的合法使用,以及{}語(yǔ)句塊到底有什么用?

    JAVA基礎(chǔ) 語(yǔ)句標(biāo)簽的合法使用,以及{}語(yǔ)句塊到底有什么用?

    以前的一個(gè)思維誤區(qū),for(){},if(){}之類的用法中,邏輯if()和語(yǔ)句塊{}應(yīng)該是相互獨(dú)立的兩種語(yǔ)法
    2012-08-08

最新評(píng)論

定安县| 泗洪县| 云林县| 深水埗区| 卓资县| 泰和县| 公主岭市| 南通市| 繁昌县| 旬邑县| 新闻| 卓资县| 马山县| 于都县| 大邑县| 拜城县| 屏南县| 宁远县| 巴林右旗| 林甸县| 新兴县| 张家口市| 报价| 延庆县| 赣榆县| 肃南| 定兴县| 天门市| 珲春市| 静安区| 图们市| 商都县| 凤翔县| 天峻县| 桂平市| 巴塘县| 贺州市| 辛集市| 城步| 甘孜| 大方县|