大家基本上都知道如何使用 mybatis 執(zhí)行任意 sql,使用方法很簡單,例如在一個 xxmapper.xml 中:
1
2
3
|
<select id= "executesql" resulttype= "map" > ${_parameter} </select> |
你可以如下調(diào)用:
1
|
sqlsession.selectlist( "executesql" , "select * from sysuser where enabled = 1" ); |
或者你可以在 xxmapper.java 接口中定義如下方法:
1
|
list<map> executesql(string sql); |
然后使用接口調(diào)用方法:
1
|
xxmapper.executesql( "select * from sysuser where enabled = 1" ); |
上面這些內(nèi)容可能都會,下面在此基礎(chǔ)上再復(fù)雜一點。
假如像上面sql中的enabled = 1
我想使用參數(shù)方式傳值,也就是寫成enabled = #{enabled}
,如果你沒有遇到過類似這種需求,可能不明白為什么要這么寫,舉個例子,要實現(xiàn)一種動態(tài)查詢,可以在前臺通過配置 sql,提供一些查詢條件就能實現(xiàn)一個查詢的功能(為了安全,這些配置肯定是開發(fā)或者實施做的,不可能讓用戶直接操作數(shù)據(jù)庫)。
針對這個功能,使用 mybatis 實現(xiàn)起來相當(dāng)容易。配置 sql 肯定要執(zhí)行,用上面講的這種方式肯定可以執(zhí)行 sql,如何提供參數(shù)呢?參數(shù)就是enabled = #{enabled}
中的#{enabled}
部分。如果再多一些條件,一個配置好的 sql 如下:
1
2
3
|
select * from sysuser where enabled = #{enabled} and username like concat( '%' ,#{username}, '%' ) |
這種情況下,該怎么用 mybatis 實現(xiàn)呢?
首先 xml 中修改如下:
1
2
3
|
<select id= "executesql" resulttype= "map" > ${sql} </select> |
接口中的方法修改為:
1
|
list<map> executesql(map map); |
然后調(diào)用方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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); |
有了這個sql之后,就可以將 enabled 和 username 作為條件提供給用戶。這兩個條件顯然是必填的。如果是可選的,那該怎么寫?
也許有人想到了是不是可以用 mybatis 中的動態(tài) sql,使用<if>
標(biāo)簽等等?
再回答這個問題前,我們先看處理動態(tài) sql 的dynamicsqlsource
中的代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@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 處理動態(tài) sql 時,所有動態(tài) sql 的標(biāo)簽都會處理為 sqlnode (這里的rootsqlnode
)對象,包含${}
的也會處理為textsqlnode
對象,在上面方法的前兩行,就是 mybatis 處理動態(tài) sql 的地方。
因此如果我們在${sql}
中的內(nèi)容包含嵌套的${}
和<if>
,<where>
等標(biāo)簽時,他們在 mybatis 解析 xml 為 sqlnode 對象時,xml <select>
元素包含的內(nèi)容只有${sql},只有${sql}會被解析,在運行時這個參數(shù)字符串中可能包含的${}和<if>,<where>等標(biāo)簽,但是這都發(fā)生在 mybatis 解析后,因此當(dāng)這些內(nèi)容作為字符串中的一部分出現(xiàn)時,他們不會被特殊處理,他們只是sql中的一部分,只是原樣輸出(由于數(shù)據(jù)庫不認(rèn)會報錯)無法被處理,因此沒法通過 mybatis 自帶的這種方式來寫動態(tài) sql。
提示
在上面的代碼中:
sqlsourceparser.parse(context.getsql(), parametertype, context.getbindings());
這一段代碼是處理動態(tài)參數(shù)(#{})的
這個處理在動態(tài) sql 處理之后,
因此可以在 sql 中使用這種類型的參數(shù)。
既然不能用 mybatis 動態(tài) sql 方式,該怎么實現(xiàn)動態(tài) sql 呢?
這里提供一個簡單的思路,在 sql 中使用模板標(biāo)記語言來實現(xiàn)動態(tài)sql(例如freemarker),在 sql 交給 mybatis 執(zhí)行之前,使用模板對 sql 進行處理生成最終執(zhí)行的 sql(需要避免處理#{}
參數(shù)),將這個sql交給 mybatis 執(zhí)行。
舉一個freemarker模板的例子,仍然以上面的sql為基礎(chǔ):
1
2
3
4
5
6
7
8
|
select * from sysuser where 1 = 1 <# if enabled??> enabled = #{enabled} </# if > <# if username?? && username != '' > and username like concat( '%' ,#{username}, '%' ) </# if > |
注意,這里的<#if>
是freemarker的元素。在不考慮sql注入的情況下,上面的sql還可以寫成:
1
2
3
4
5
6
7
8
|
select * from sysuser where 1 = 1 <# if enabled??> enabled = #{enabled} </# if > <# if username?? && username != '' > and username like '%${username}%' </# if > |
區(qū)別就是'%${username}%'
,因為 freemarker 也會處理${username}
,也會用實際的值來替換這里的參數(shù)。
在前面調(diào)用的代碼中,這里修改如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
//#{enabled} map.put( "enabled" , 1 ); //#{username} map.put( "username" , "admin" ); //這里的 sql 對應(yīng) xml 中的 ${sql} string sql = "上面兩個復(fù)雜sql中的一個" ; //使用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字符串,實現(xiàn)方式可以自己搜索。
到這里,一個不是很復(fù)雜的動態(tài)sql功能就實現(xiàn)了。
不知道有沒有更貪心的人,你會不會想,上面返回值都是list<map>
類型,能不能返回一個我指定的實體類呢?
例如在map中:
1
|
map.put( "class" , "tk.mybatis.model.sysuser" ); |
能不能通過這種方式讓返回值變成sysuser
類型呢?由于這篇文章耗時已經(jīng)太長,這里就提供一個方案,不深入。
你可以使用攔截器實現(xiàn),獲取mappedstatement
后,復(fù)制一份,然后修改resultmaps
中resultmap
的type
屬性為你指定的class
類型就能實現(xiàn),說起來容易,實際操作起來能有 pagehelper 分頁插件 1/10 左右的工作量。
注:如果是動態(tài)的update,insert,delete 語句,可以將上面的<select>
改為<update>
(不需要使用<delete>
和<insert>
),返回值用int
,比select
的情況容易很多。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對服務(wù)器之家的支持。如果你想了解更多相關(guān)內(nèi)容請查看下面相關(guān)鏈接
原文鏈接:https://blog.csdn.net/isea533/article/details/52368341