MyBatis執(zhí)行動(dòng)態(tài)SQL的方法
大家基本上都知道如何使用 MyBatis 執(zhí)行任意 SQL,使用方法很簡單,例如在一個(gè) XXMapper.xml 中:
<select id="executeSql" resultType="map"> ${_parameter} </select>
你可以如下調(diào)用:
sqlSession.selectList("executeSql", "select * from sysuser where enabled = 1");
或者你可以在 XXMapper.java 接口中定義如下方法:
List<Map> executeSql(String sql);
然后使用接口調(diào)用方法:
xxMapper.executeSql("select * from sysuser where enabled = 1");
上面這些內(nèi)容可能都會(huì),下面在此基礎(chǔ)上再復(fù)雜一點(diǎn)。
假如像上面SQL中的enabled = 1
我想使用參數(shù)方式傳值,也就是寫成enabled = #{enabled}
,如果你沒有遇到過類似這種需求,可能不明白為什么要這么寫,舉個(gè)例子,要實(shí)現(xiàn)一種動(dòng)態(tài)查詢,可以在前臺(tái)通過配置 SQL,提供一些查詢條件就能實(shí)現(xiàn)一個(gè)查詢的功能(為了安全,這些配置肯定是開發(fā)或者實(shí)施做的,不可能讓用戶直接操作數(shù)據(jù)庫)。
針對這個(gè)功能,使用 MyBatis 實(shí)現(xiàn)起來相當(dāng)容易。配置 SQL 肯定要執(zhí)行,用上面講的這種方式肯定可以執(zhí)行 SQL,如何提供參數(shù)呢?參數(shù)就是enabled = #{enabled}
中的#{enabled}
部分。如果再多一些條件,一個(gè)配置好的 SQL 如下:
select * from sysuser where enabled = #{enabled} and userName like concat('%',#{userName},'%')
這種情況下,該怎么用 MyBatis 實(shí)現(xiàn)呢?
首先 XML 中修改如下:
<select id="executeSql" resultType="map"> ${sql} </select>
接口中的方法修改為:
List<Map> executeSql(Map map);
然后調(diào)用方法:
Map map = new HashMap(); //這里的 sql 對應(yīng) XML 中的 ${sql} map.put("sql", "select * from sysuser " + " where enabled = #{enabled} " + " and userName like concat('%',#{userName},'%')"); //#{enabled} map.put("enabled", 1); //#{userName} map.put("userName", "admin"); //接口方式調(diào)用 List<Map> list = xxMapper.executeSql(map); //sqlSession方式調(diào)用 sqlSession.selectList("executeSql", map);
有了這個(gè)SQL之后,就可以將 enabled 和 userName 作為條件提供給用戶。這兩個(gè)條件顯然是必填的。如果是可選的,那該怎么寫?
也許有人想到了是不是可以用 MyBatis 中的動(dòng)態(tài) SQL,使用<if>
標(biāo)簽等等?
再回答這個(gè)問題前,我們先看處理動(dòng)態(tài) SQL 的DynamicSqlSource
中的代碼:
@Override public BoundSql getBoundSql(Object parameterObject) { DynamicContext context = new DynamicContext(configuration, parameterObject); rootSqlNode.apply(context); SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); Class < ?>parameterType = parameterObject == null ? Object.class: parameterObject.getClass(); SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings()); BoundSql boundSql = sqlSource.getBoundSql(parameterObject); for (Map.Entry < String, Object > entry: context.getBindings().entrySet()) { boundSql.setAdditionalParameter(entry.getKey(), entry.getValue()); } return boundSql; }
MyBatis 處理動(dòng)態(tài) SQL 時(shí),所有動(dòng)態(tài) SQL 的標(biāo)簽都會(huì)處理為 SqlNode (這里的rootSqlNode
)對象,包含${}
的也會(huì)處理為TextSqlNode
對象,在上面方法的前兩行,就是 MyBatis 處理動(dòng)態(tài) SQL 的地方。
因此如果我們在${sql}
中的內(nèi)容包含嵌套的${}
和<if>
,<where>
等標(biāo)簽時(shí),他們在 MyBatis 解析 XML 為 SqlNode 對象時(shí),XML <select>
元素包含的內(nèi)容只有${sql},只有${sql}會(huì)被解析,在運(yùn)行時(shí)這個(gè)參數(shù)字符串中可能包含的${}和<if>,<where>等標(biāo)簽,但是這都發(fā)生在 MyBatis 解析后,因此當(dāng)這些內(nèi)容作為字符串中的一部分出現(xiàn)時(shí),他們不會(huì)被特殊處理,他們只是SQL中的一部分,只是原樣輸出(由于數(shù)據(jù)庫不認(rèn)會(huì)報(bào)錯(cuò))無法被處理,因此沒法通過 MyBatis 自帶的這種方式來寫動(dòng)態(tài) SQL。
提示
在上面的代碼中:
sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
這一段代碼是處理動(dòng)態(tài)參數(shù)(#{})的
這個(gè)處理在動(dòng)態(tài) SQL 處理之后,
因此可以在 SQL 中使用這種類型的參數(shù)。
既然不能用 MyBatis 動(dòng)態(tài) SQL 方式,該怎么實(shí)現(xiàn)動(dòng)態(tài) SQL 呢?
這里提供一個(gè)簡單的思路,在 SQL 中使用模板標(biāo)記語言來實(shí)現(xiàn)動(dòng)態(tài)SQL(例如freemarker),在 SQL 交給 MyBatis 執(zhí)行之前,使用模板對 SQL 進(jìn)行處理生成最終執(zhí)行的 SQL(需要避免處理#{}
參數(shù)),將這個(gè)SQL交給 MyBatis 執(zhí)行。
舉一個(gè)Freemarker模板的例子,仍然以上面的SQL為基礎(chǔ):
select * from sysuser where 1 = 1 <#if enabled??> enabled = #{enabled} </#if> <#if userName?? && userName != ''> and userName like concat('%',#{userName},'%') </#if>
注意,這里的<#if>
是Freemarker的元素。在不考慮SQL注入的情況下,上面的SQL還可以寫成:
select * from sysuser where 1 = 1 <#if enabled??> enabled = #{enabled} </#if> <#if userName?? && userName != ''> and userName like '%${userName}%' </#if>
區(qū)別就是'%${userName}%'
,因?yàn)?Freemarker 也會(huì)處理${userName}
,也會(huì)用實(shí)際的值來替換這里的參數(shù)。
在前面調(diào)用的代碼中,這里修改如下:
//#{enabled} map.put("enabled", 1); //#{userName} map.put("userName", "admin"); //這里的 sql 對應(yīng) XML 中的 ${sql} String sql = "上面兩個(gè)復(fù)雜SQL中的一個(gè)"; //使用Freemarker處理sql sql = processSqlByFreemarker(sql, map); //將處理后的sql放到map中 map.put("sql", sql); //執(zhí)行方法 List<Map> list = xxMapper.executeSql(map);
注:processSqlByFreemarker
方法就是根據(jù)map中的數(shù)據(jù)來處理sql字符串,實(shí)現(xiàn)方式可以自己搜索。
到這里,一個(gè)不是很復(fù)雜的動(dòng)態(tài)SQL功能就實(shí)現(xiàn)了。
不知道有沒有更貪心的人,你會(huì)不會(huì)想,上面返回值都是List<Map>
類型,能不能返回一個(gè)我指定的實(shí)體類呢?
例如在map中:
map.put("class", "tk.mybatis.model.SysUser");
能不能通過這種方式讓返回值變成SysUser
類型呢?由于這篇文章耗時(shí)已經(jīng)太長,這里就提供一個(gè)方案,不深入。
你可以使用攔截器實(shí)現(xiàn),獲取MappedStatement
后,復(fù)制一份,然后修改resultMaps
中resultMap
的type
屬性為你指定的class
類型就能實(shí)現(xiàn),說起來容易,實(shí)際操作起來能有 PageHelper 分頁插件 1/10 左右的工作量。
注:如果是動(dòng)態(tài)的update,insert,delete 語句,可以將上面的<select>
改為<update>
(不需要使用<delete>
和<insert>
),返回值用int
,比select
的情況容易很多。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請查看下面相關(guān)鏈接
- mybatis的動(dòng)態(tài)sql詳解(精)
- MyBatis 動(dòng)態(tài)拼接Sql字符串的問題
- MyBatis 執(zhí)行動(dòng)態(tài) SQL語句詳解
- Mybatis中動(dòng)態(tài)SQL,if,where,foreach的使用教程詳解
- mybatis動(dòng)態(tài)sql之Map參數(shù)的講解
- MyBatis動(dòng)態(tài)Sql之if標(biāo)簽的用法詳解
- mybatis的動(dòng)態(tài)sql之if test的使用說明
- MyBatis if choose 動(dòng)態(tài) SQL的實(shí)現(xiàn)
相關(guān)文章
Spring?boot集成easy?excel實(shí)現(xiàn)導(dǎo)入導(dǎo)出功能
這篇文章主要介紹了Spring?boot集成easy?excel實(shí)現(xiàn)導(dǎo)入導(dǎo)出操作,使用easyexcel,首先要引入easyexcel的maven依賴,具體的版本根據(jù)你的需求去設(shè)置,本文結(jié)合實(shí)例代碼講解的非常詳細(xì),需要的朋友可以參考下2024-05-05Spring Core動(dòng)態(tài)代理的實(shí)現(xiàn)代碼
通過JDK的Proxy方式或者CGLIB方式生成代理對象的時(shí)候,相關(guān)的攔截器已經(jīng)配置到代理對象中去了,接下來通過本文給大家介紹Spring Core動(dòng)態(tài)代理的相關(guān)知識(shí),需要的朋友可以參考下2021-10-10springboot3請求參數(shù)種類及接口測試案例小結(jié)
這篇文章主要介紹了springboot3請求參數(shù)種類及接口測試案例小結(jié),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-10-10spring mvc中直接注入的HttpServletRequst安全嗎
這篇文章主要給大家介紹了關(guān)于spring mvc中直接注入的HttpServletRequst是不是安全的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2018-04-04