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

于是,在荒誕的盡頭,筆者做出了一個近乎宿命的選擇:親手拼接SQL——不是出于信念,而是因為別無選擇。在這片邏輯崩塌的廢墟上,唯有將字符串縫合成一句句臨時的禱詞,交付給數(shù)據(jù)庫那沉默而全能的神諭。一旦接受了這種徒勞的合理性,道路便在虛無中浮現(xiàn)。
JdbcTemplate
那是來自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());
}
}
然而這樸素之中藏著代價。它不夠優(yōu)雅,更談不上智能。而當多數(shù)據(jù)源的陰影悄然降臨,它便顯露出更深的無力:每一次切換,都像在荒原上重新挖一口井,徒增重復(fù)、耦合與混亂。于是,這份最初的慰藉,終究在現(xiàn)實的重壓下顯出裂痕——它能承載希望,卻無法馴服復(fù)雜。
@SelectProvider
后來,我遇見了 SelectProvider——它披著優(yōu)雅的外衣,攜帶著一種近乎詩意的承諾。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Repeatable(SelectProvider.List.class)
/* 該注解允許定制多種選項,平常使用這兩個即可 */
public @interface SelectProvider {
/* 填寫生產(chǎn)SQL語句的類 */
Class<?> type() default void.class;
/* 填寫生產(chǎn)SQL語句的方法名 */
String method() default "";
}
只需在方法上輕輕標注,指明誰將為SQL賦形、何處孕育語句,MyBatis 便會如虔誠的信使,自動召喚那段動態(tài)生成的咒語,并將其送入數(shù)據(jù)庫的圣殿。代碼簡潔,結(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語句提供類 */
static class SqlProvider {
/* 提供語句的方法 */
public static String findById(String tbl, String id) {
return "select * from " + tbl + " where id = " + id;
}
}
}
然而,這優(yōu)雅之下潛伏著古老的危險:若對輸入不加審視,任其流入拼接的縫隙,SQL 注入便如幽靈般悄然附體——一句惡意的字符串,足以撕裂整個系統(tǒng)的防線。因此,我不得不低聲警告:凡使用此道者,必先以轉(zhuǎ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語句提供類 */
static class SqlProvider {
/* 提供語句的方法 */
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)定,又何須動用SelectProvider這般為混沌而生的機制?這就像試圖為風(fēng)塑形,再宣稱它的輪廓可以復(fù)用。
總結(jié)
回望整個探索過程,從 JdbcTemplate 的直白拼接到 SelectProvider 的動態(tài)封裝,每一種方案都像是在不確定性的迷宮中點亮一盞臨時的燈——足夠照亮腳下的路,卻照不透整座建筑的結(jié)構(gòu)。
工具沒有對錯,只有適配與否;而所謂“優(yōu)雅”或“原始”,往往只是我們面對復(fù)雜時情緒的投射。真正關(guān)鍵的,是在開放性與安全性、靈活性與性能之間,做出清醒的權(quán)衡,并為自己的選擇負責(zé)。
最終,代碼不只是邏輯的堆砌,更是判斷的痕跡。我選擇接受它的不完美,并用克制的拼接、謹慎的校驗和明確的邊界,為系統(tǒng)保留最后一道理性的防線。
到此這篇關(guān)于Mybatis實現(xiàn)動態(tài)拼接SQL的實踐指南的文章就介紹到這了,更多相關(guān)Mybatis動態(tài)拼接SQL內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java多文件以ZIP壓縮包導(dǎo)出的實現(xiàn)方法
這篇文章主要為大家詳細介紹了Java多文件以ZIP壓縮包導(dǎo)出的實現(xiàn)方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-07-07
mybatis plus saveOrUpdate實現(xiàn)有重復(fù)數(shù)據(jù)就更新,否則新增方式
這篇文章主要介紹了mybatis plus saveOrUpdate實現(xiàn)有重復(fù)數(shù)據(jù)就更新,否則新增方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
詳解JVM的內(nèi)存對象介紹[創(chuàng)建和訪問]
這篇文章主要介紹了JVM的內(nèi)存對象介紹[創(chuàng)建和訪問],文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
java使用FastJson解析Json數(shù)據(jù)
本篇文章主要介紹了java使用FastJson解析Json數(shù)據(jù),fastjson 是一個性能極好的用 Java 語言實現(xiàn)的 JSON 解析器和生成器,有興趣的可以了解一下。2017-02-02
Java中String類getBytes()方法詳解與完整實例
這篇文章主要給大家介紹了關(guān)于Java中String類getBytes()方法詳解與完整實例的相關(guān)資料,getBytes()是Java編程語言中將一個字符串轉(zhuǎn)化為一個字節(jié)數(shù)組byte[]的方法,需要的朋友可以參考下2023-10-10

