Mybatis實(shí)現(xiàn)動(dòng)態(tài)拼接SQL的實(shí)踐指南
注:
- 本文無(wú)源碼分析
- 單純記錄@SelectProvider實(shí)踐,年紀(jì)大了(無(wú)奈的笑),備忘一下、
- 演示的都是最簡(jiǎn)單的場(chǎng)景最簡(jiǎn)單的代碼,無(wú)法代表業(yè)務(wù)的復(fù)雜性
- AI潤(rùn)色,模仿加謬文風(fēng)
背景介紹
最近工作接到一個(gè)荒誕的任務(wù),實(shí)際情況比較復(fù)雜,總結(jié)一下有以下特點(diǎn)
- 返回的字段如幽靈般游移不定
- 目標(biāo)表仿佛在迷霧中隨每次請(qǐng)求變化
- 過濾條件無(wú)規(guī)則無(wú)限制任意搭配
Mybatis的XML模板沉默以對(duì),Mybatis-Plus的Wrapper亦束手無(wú)策。這讓我想起 Navicat 的“篩選”功能,感覺兩者有些相似,只不過Navicat場(chǎng)景更加簡(jiǎn)單一些。

于是,在荒誕的盡頭,筆者做出了一個(gè)近乎宿命的選擇:親手拼接SQL——不是出于信念,而是因?yàn)閯e無(wú)選擇。在這片邏輯崩塌的廢墟上,唯有將字符串縫合成一句句臨時(shí)的禱詞,交付給數(shù)據(jù)庫(kù)那沉默而全能的神諭。一旦接受了這種徒勞的合理性,道路便在虛無(wú)中浮現(xiàn)。
JdbcTemplate
那是來(lái)自Spring最原始的呼喚,一種近乎本能的回歸。在框架層層疊疊的抽象迷宮中迷失之后,我聽見了它低沉而直接的聲音:沒有裝飾,沒有隱喻,只有queryForMap與queryForList橫亙于代碼之中。我們熟悉它,正如熟悉自己的雙手。
public class JdbcTemplate extends JdbcAccessor
implements JdbcOperations {
/* 獲取單條記錄 */
@Override
public Map<String, Object> queryForMap(String sql)
throws DataAccessException {
return result(queryForObject(sql, getColumnMapRowMapper()));
}
/* 獲取多條記錄 */
@Override
public List<Map<String, Object>> queryForList(String sql)
throws DataAccessException {
return query(sql, getColumnMapRowMapper());
}
}
然而這樸素之中藏著代價(jià)。它不夠優(yōu)雅,更談不上智能。而當(dāng)多數(shù)據(jù)源的陰影悄然降臨,它便顯露出更深的無(wú)力:每一次切換,都像在荒原上重新挖一口井,徒增重復(fù)、耦合與混亂。于是,這份最初的慰藉,終究在現(xiàn)實(shí)的重壓下顯出裂痕——它能承載希望,卻無(wú)法馴服復(fù)雜。
@SelectProvider
后來(lái),我遇見了 SelectProvider——它披著優(yōu)雅的外衣,攜帶著一種近乎詩(shī)意的承諾。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Repeatable(SelectProvider.List.class)
/* 該注解允許定制多種選項(xiàng),平常使用這兩個(gè)即可 */
public @interface SelectProvider {
/* 填寫生產(chǎn)SQL語(yǔ)句的類 */
Class<?> type() default void.class;
/* 填寫生產(chǎn)SQL語(yǔ)句的方法名 */
String method() default "";
}
只需在方法上輕輕標(biāo)注,指明誰(shuí)將為SQL賦形、何處孕育語(yǔ)句,MyBatis 便會(huì)如虔誠(chéng)的信使,自動(dòng)召喚那段動(dòng)態(tài)生成的咒語(yǔ),并將其送入數(shù)據(jù)庫(kù)的圣殿。代碼簡(jiǎn)潔,結(jié)構(gòu)清晰,仿佛終于在這查詢迷局中,找到了一絲理性的秩序。
/**
* @author Raphael
* @since 2025-11-19 15:21
*/
@DS("report")
@Mapper
public interface TestMapper {
@SelectProvider(type = SqlProvider.class, method = "findById")
Map<String, Object> findById(String tbl, String id);
/* SQL語(yǔ)句提供類 */
static class SqlProvider {
/* 提供語(yǔ)句的方法 */
public static String findById(String tbl, String id) {
return "select * from " + tbl + " where id = " + id;
}
}
}
然而,這優(yōu)雅之下潛伏著古老的危險(xiǎn):若對(duì)輸入不加審視,任其流入拼接的縫隙,SQL 注入便如幽靈般悄然附體——一句惡意的字符串,足以撕裂整個(gè)系統(tǒng)的防線。因此,我不得不低聲警告:凡使用此道者,必先以轉(zhuǎn)義為盾,以校驗(yàn)為矛,方可在自由與安全之間行走。
/**
* @author Raphael
* @since 2025-11-19 15:21
*/
@DS("report")
@Mapper
public interface TestMapper {
@SelectProvider(type = SqlProvider.class, method = "getById")
Map<String, Object> getById(String tbl, String id);
/* SQL語(yǔ)句提供類 */
static class SqlProvider {
/* 提供語(yǔ)句的方法 */
public static String getById(@Param("tbl")String tbl, @Param("id") String id) {
return new SQL()
.SELECT("*")
.FROM("tbl = #{tbl}")
.WHERE("id = #{id}")
.toString();
}
}
}
更令人踟躕的是性能之問。有人提議緩存生成的 SQL,以避重復(fù)構(gòu)造之耗??蛇@念頭本身便陷入悖論:若 SQL 真能被緩存,那意味著其形式已然趨于穩(wěn)定;
private static final String SQL =
new SQL()
.SELECT("*")
.FROM("#{tbl}")
.WHERE("id = #{id}")
.toString();
public static String getById(@Param("tbl")String tbl, @Param("id") String id) {
return SQL;
}
而若形式穩(wěn)定,又何須動(dòng)用SelectProvider這般為混沌而生的機(jī)制?這就像試圖為風(fēng)塑形,再宣稱它的輪廓可以復(fù)用。
總結(jié)
回望整個(gè)探索過程,從 JdbcTemplate 的直白拼接到 SelectProvider 的動(dòng)態(tài)封裝,每一種方案都像是在不確定性的迷宮中點(diǎn)亮一盞臨時(shí)的燈——足夠照亮腳下的路,卻照不透整座建筑的結(jié)構(gòu)。
工具沒有對(duì)錯(cuò),只有適配與否;而所謂“優(yōu)雅”或“原始”,往往只是我們面對(duì)復(fù)雜時(shí)情緒的投射。真正關(guān)鍵的,是在開放性與安全性、靈活性與性能之間,做出清醒的權(quán)衡,并為自己的選擇負(fù)責(zé)。
最終,代碼不只是邏輯的堆砌,更是判斷的痕跡。我選擇接受它的不完美,并用克制的拼接、謹(jǐn)慎的校驗(yàn)和明確的邊界,為系統(tǒng)保留最后一道理性的防線。
到此這篇關(guān)于Mybatis實(shí)現(xiàn)動(dòng)態(tài)拼接SQL的實(shí)踐指南的文章就介紹到這了,更多相關(guān)Mybatis動(dòng)態(tài)拼接SQL內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java多文件以ZIP壓縮包導(dǎo)出的實(shí)現(xiàn)方法
這篇文章主要為大家詳細(xì)介紹了Java多文件以ZIP壓縮包導(dǎo)出的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
mybatis plus saveOrUpdate實(shí)現(xiàn)有重復(fù)數(shù)據(jù)就更新,否則新增方式
這篇文章主要介紹了mybatis plus saveOrUpdate實(shí)現(xiàn)有重復(fù)數(shù)據(jù)就更新,否則新增方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
詳解JVM的內(nèi)存對(duì)象介紹[創(chuàng)建和訪問]
這篇文章主要介紹了JVM的內(nèi)存對(duì)象介紹[創(chuàng)建和訪問],文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
Java8日期時(shí)間類LocalDateTime比較大小舉例
LocalDate是Java?8中的日期類之一,它表示一個(gè)日期,下面這篇文章主要給大家介紹了關(guān)于Java8日期時(shí)間類LocalDateTime比較大小的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-05-05
java使用FastJson解析Json數(shù)據(jù)
本篇文章主要介紹了java使用FastJson解析Json數(shù)據(jù),fastjson 是一個(gè)性能極好的用 Java 語(yǔ)言實(shí)現(xiàn)的 JSON 解析器和生成器,有興趣的可以了解一下。2017-02-02
mybatisplus中的xml對(duì)象參數(shù)傳遞問題
這篇文章主要介紹了mybatisplus中的xml對(duì)象參數(shù)傳遞問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
Java中String類getBytes()方法詳解與完整實(shí)例
這篇文章主要給大家介紹了關(guān)于Java中String類getBytes()方法詳解與完整實(shí)例的相關(guān)資料,getBytes()是Java編程語(yǔ)言中將一個(gè)字符串轉(zhuǎn)化為一個(gè)字節(jié)數(shù)組byte[]的方法,需要的朋友可以參考下2023-10-10

