選單

我這樣寫程式碼,比直接使用 MyBatis 效率提高了 100 倍 ?

我這樣寫程式碼,比直接使用 MyBatis 效率提高了 100 倍 ?

作者:喬伊醬

對一個 Java 後端程式設計師來說,、、 等都是我們常用的 ORM 框架。它們有時候很好用,比如簡單的 CRUD,事務的支援都非常棒。但有時候用起來也非常繁瑣,比如接下來我們要聊到的一個常見的開發需求,最後本文會給出一個比直接使用這些 ORM 開發效率至少會提高

100

倍的方法(絕無誇張)。

在這裡插入圖片描述

首先資料庫有兩張表

使用者表(user):(簡單起見,假設只有 4 個欄位)

角色表(role):(簡單起見,假設只有 2 個欄位)

接下來我們要實現一個使用者查詢的功能

這個查詢有點複雜,它的要求如下:

可按

欄位查詢,要求:

可精確匹配(等於某個值)

可全模糊匹配(包含給定的值)

可後模糊查詢(以。。。開頭)

可前模糊查詢(以。。 結尾)

可指定以上四種匹配是否可以忽略大小寫

可按

欄位查詢,要求:

可精確匹配(等於某個年齡)

可大於匹配(大於某個值)

可小於匹配(小於某個值)

可區間匹配(某個區間範圍)

可按查詢,要求:精確匹配

可按查詢,要求:同欄位

可指定只輸出哪些列(例如,只查詢 與 列)

支援分頁(每次查詢後,頁面都要顯示滿足條件的使用者總數)

查詢時可選擇按  、、 等任意欄位排序

後端介面該怎麼寫呢?

試想一下,對於這種要求的查詢,後端接口裡的程式碼如果用 、、 直接來寫的話,

100 行程式碼

能實現嗎?

反正我是沒這個信心,算了,我還是直接坦白,面對這種需求後端如何

只用一行程式碼搞定

吧(有興趣的同學可以 mybatis 等寫個試試,最後可以對比一下)

手把手:只一行程式碼實現以上需求

首先,重點人物出場啦:

Bean Searcher

,  它就是專門來對付這種列表檢索的,無論簡單的還是複雜的,統統一行程式碼搞定!而且它還非常輕量,Jar 包體積僅不到 100KB,無第三方依賴。

假設我們專案使用的框架是 Spring Boot(當然 Bean Searcher 對框架沒有要求,但在 Spring Boot 中使用更加方便)

新增依賴

Maven :

Gradle :

然後寫個實體類來承載查詢的結果

接著就可以寫使用者查詢介面了

介面路徑就叫 /user/index 吧:

上述程式碼中的 是 Bean Searcher 提供的一個工具類, 只是為了把前端傳來的請求引數統一收集起來,然後剩下的,就全部交給 檢索器了。

這樣就完了?那我們來測一下這個介面,看看效果吧

(1)無參請求

GET /user/index

返回結果:

(2)分頁請求(page | size)

GET /user/index? page = 2 & size = 10

返回結果:結構同

(1)

(只是每頁 10 條,返回第 2 頁)

引數名 和 可自定義, 預設從 開始,同樣可自定義,並且可與其它引數組合使用

(3)資料排序(sort | order)

GET /user/index? sort = age & order = desc

返回結果:結構同

(1)

(只是 dataList 資料列表以 age 欄位降序輸出)

引數名 和 可自定義,可與其它引數組合使用

(4)指定(排除)欄位(onlySelect | selectExclude)

GET /user/index? onlySelect = id,name,role

GET /user/index? selectExclude = age,roleId

返回結果:( 列表只含 id,name 與 role 三個欄位)

引數名 和 可自定義,可與其它引數組合使用

(5)欄位過濾(op = eq)

GET /user/index? age=20

GET /user/index? age=20 & age-op=eq

返回結果:結構同

(1)

(但只返回 age = 20 的資料)

引數 表示 的

欄位運算子

是 ( 的縮寫),表示引數 與引數值 之間的關係是 ,由於 是一個預設的關係,所以 也可以省略

引數名 的字尾 可自定義,且可與其它欄位引數 和 上文所列的引數(分頁、排序、指定欄位)組合使用,下文所列的欄位引數也是一樣,不再複述。

(6)欄位過濾(op = ne)

GET /user/index? age=20 & age-op=ne

返回結果:結構同

(1)

(但只返回 age != 20 的資料, 是 的縮寫)

(7)欄位過濾(op = ge)

GET /user/index? age=20 & age-op=ge

返回結果:結構同

(1)

(但只返回 age >= 20 的資料, 是 的縮寫)

(8)欄位過濾(op = le)

GET /user/index? age=20 & age-op=le

返回結果:結構同

(1)

(但只返回 age

(9)欄位過濾(op = gt)

GET /user/index? age=20 & age-op=gt

返回結果:結構同

(1)

(但只返回 age > 20 的資料, 是 的縮寫)

(10)欄位過濾(op = lt)

GET /user/index? age=20 & age-op=lt

返回結果:結構同

(1)

(但只返回 age < 20 的資料, 是 的縮寫)

(11)欄位過濾(op = bt)

GET /user/index? age-0=20 & age-1=30 & age-op=bt

返回結果:結構同

(1)

(但只返回 20

引數 表示 的第 0 個引數值是 。上述提到的 實際上是 的簡寫形式。另:引數名 與 中的連字元 可自定義。

(12)欄位過濾(op = mv)

GET /user/index? age-0=20 & age-1=30 & age-2=40 & age-op=mv

返回結果:結構同

(1)

(但只返回 age in (20, 30, 40) 的資料, 是 的縮寫,表示有多個值的意思)

(13)欄位過濾(op = in)

GET /user/index? name=Jack & name-op=in

返回結果:結構同

(1)

(但只返回 name 包含 Jack 的資料, 是 的縮寫)

(14)欄位過濾(op = sw)

GET /user/index? name=Jack & name-op=sw

返回結果:結構同

(1)

(但只返回 name 以 Jack 開頭的資料, 是 的縮寫)

(15)欄位過濾(op = ew)

GET /user/index? name=Jack & name-op=ew

返回結果:結構同

(1)

(但只返回 name 以 Jack 結尾的資料, 是 的縮寫)

(16)欄位過濾(op = ey)

GET /user/index? name-op=ey

返回結果:結構同

(1)

(但只返回 name 為空 或為 null 的資料, 是 的縮寫)

(17)欄位過濾(op = ny)

GET /user/index? name-op=ny

返回結果:結構同

(1)

(但只返回 name 非空 的資料, 是 的縮寫)

(18)忽略大小寫(ic = true)

GET /user/index? name=Jack & name-ic=true

返回結果:結構同

(1)

(但只返回 name 等於 Jack (忽略大小寫) 的資料, 是 的縮寫)

引數名 中的字尾 可自定義,該引數可與其它的引數組合使用,比如這裡檢索的是 name 等於 Jack 時忽略大小寫,但同樣適用於檢索 name 以 Jack 開頭或結尾時忽略大小寫。

當然,以上各種條件都可以組合,例如

查詢 name 以 Jack (忽略大小寫) 開頭,且 roleId = 1,結果以 id 欄位排序,每頁載入 10 條,查詢第 2 頁:

GET /user/index? name=Jack & name-op=sw & name-ic=true & roleId=1 & sort=id & size=10 & page=2

返回結果:結構同

(1)

OK,效果看完了, 接口裡我們確實只寫了一行程式碼,它便可以支援這麼多種的檢索方式,有沒有覺得現在

你寫的一行程式碼

就可以

幹過別人的一百行

呢?

我這樣寫程式碼,比直接使用 MyBatis 效率提高了 100 倍 ?

Bean Searcher

本例中,我們只使用了 Bean Searcher 提供的 檢索器的一個 方法,其實,它有很多 方法。

檢索方法

查詢指定條件下的資料

總條數

查詢指定條件下的

某欄位

統計值

查詢指定條件下的

多欄位

統計值

分頁

查詢指定條件下資料

列表

總條數

同上

+ 多欄位

統計

查詢指定條件下的

第一條

資料

分頁

查詢指定條件下資料

列表

查詢指定條件下

所有

資料

列表

MapSearcher 與 BeanSearcher

另外,Bean Searcher 除了提供了 檢索器外,還提供了 檢索器,它同樣擁有 擁有的方法,只是它返回的單條資料不是 ,而是一個 泛型 物件。

引數構建工具

另外,如果你是在 Service 裡使用 Bean Searcher,那麼直接使用 型別的引數可能不太優雅,為此, Bean Searcher 特意提供了一個引數構建工具。

例如,同樣查詢 name 以 Jack (忽略大小寫) 開頭,且 roleId = 1,結果以 id 欄位排序,每頁載入 10 條,載入第 2 頁,使用引數構建器,程式碼可以這麼寫:

這裡使用的是 檢索器,以及它的 方法。

運算子約束

上文我們看到,Bean Searcher 對實體類中的每一個欄位,都直接支援了很多的檢索方式。

但某同學:哎呀!檢索方式太多了,我根本不需要這麼多,

我的資料量幾十個億呀,使用者名稱欄位的前模糊查詢方式利用不到索引,萬一把我的資料庫查崩了怎麼辦呀?

好辦

,Bean Searcher 支援運算子的約束,實體類的使用者名稱 欄位只需要註解一下即可:

如上,透過 註解的 屬性,指定這個使用者名稱 只能適用與

精確匹配

後模糊查詢

,其它檢索方式它將直接忽略。

上面的程式碼是限制了 只能有兩種檢索方式,如果再嚴格一點,

只允許 精確匹配

,那其實有兩種寫法。

(1)還是使用運算子約束:

(2)在 Controller 的介面方法裡把運算子引數覆蓋:

條件約束

該同學又:哎呀!

我的資料量還是很大,age 欄位沒有索引,根本不能參與 where 條件,一查就是一條 慢 SQL 啊!

不急

,Bean Searcher 還支援條件的約束,讓這個欄位直接不能作為條件:

如上,透過 註解的 屬性, 就直接不允許 欄位參與條件了,無論前端怎麼傳參,Bean Searcher 都不搭理。

引數過濾器

該同學仍:哎呀!哎呀 。。。

別怕!

Bean Searcher 還支援配置全域性引數過濾器,可自定義任何引數過濾規則,在 Spring Boot 專案中,只需要配置一個 Bean:

某同學問

引數咋這麼怪,這麼多呢,和前端有仇呀

引數名是否奇怪,這其實看個人喜好,如果你不喜歡中劃線 ,不喜歡 、 字尾,完全可以自定義,參考這篇文件:

searcher。ejlchina。com/guide/lates…

引數個數的多少,其實是和需求的複雜程度相關,如果需求很簡單,其實很多引數沒必要讓前端傳,後端直接塞進去就好,比如: 只要求後模糊匹配, 只要求區間匹配,那可以這樣:

這樣前端就不用傳 與 這兩個引數了。

其實還有一種更簡單的方法,那就是

運算子約束

(當約束存在時,運算子預設就是 屬性中指定的第一個值,前端可以省略不傳):

入參是 request,我 swagger 文件不好渲染了呀

其實,Bean Searcher 的檢索器只是需要一個 型別的引數,至於這個引數是怎麼來的,和 Bean Searcher 並沒有直接關係。前文之所以從 裡取,只是因為這樣程式碼看起來簡潔,如果你喜歡宣告引數,完全可以把程式碼寫成這樣:

結語

本文介紹了

Bean Searcher

在複雜列表檢索領域的超強能力,但由於篇幅所限,本文所述仍只是冰山一角,比如它還:

支援

嵌入引數

支援

欄位轉換器

支援

Sql 攔截器

支援

多資料來源

支援

自定義註解

等等

專案 GitHub 地址

https://github。com/ejlchina/bean-searcher

有興趣的小夥伴可以點個 Star 喲~

最近面試BAT,整理一份面試資料《

Java面試BATJ通關手冊

》,覆蓋了Java核心技術、JVM、Java併發、SSM、微服務、資料庫、資料結構等等。

文章有幫助的話,在看,轉發吧。

謝謝支援喲 (*^__^*)