大家基本上都知道如何使用MyBatis 執行任意SQL,使用方法很簡單,例如在一個XXMapper.xml 中:
<select id="executeSql" resultType="map">${_parameter}</select>你可以如下調用:
sqlSession.selectList("executeSql", "select * from sysuser where enabled = 1");或者你可以在XXMapper.java 接口中定義如下方法:
List<Map> executeSql(String sql);
然後使用接口調用方法:
xxMapper.executeSql("select * from sysuser where enabled = 1");上面這些內容可能都會,下面在此基礎上再复雜一點。
假如像上面SQL中的enabled = 1我想使用參數方式傳值,也就是寫成enabled = #{enabled},如果你沒有遇到過類似這種需求,可能不明白為什麼要這麼寫,舉個例子,要實現一種動態查詢,可以在前台通過配置SQL,提供一些查詢條件就能實現一個查詢的功能(為了安全,這些配置肯定是開發或者實施做的,不可能讓用戶直接操作數據庫)。
針對這個功能,使用MyBatis 實現起來相當容易。配置SQL 肯定要執行,用上面講的這種方式肯定可以執行SQL,如何提供參數呢?參數就是enabled = #{enabled}中的#{enabled}部分。如果再多一些條件,一個配置好的SQL 如下:
select * from sysuser where enabled = #{enabled} and userName like concat('%',#{userName},'%')這種情況下,該怎麼用MyBatis 實現呢?
首先XML 中修改如下:
<select id="executeSql" resultType="map">${sql}</select>接口中的方法修改為:
List<Map> executeSql(Map map);
然後調用方法:
Map map = new HashMap();//這裡的sql 對應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");//接口方式調用List<Map> list = xxMapper.executeSql(map);//sqlSession方式調用sqlSession.selectList("executeSql", map);有了這個SQL之後,就可以將enabled 和userName 作為條件提供給用戶。這兩個條件顯然是必填的。如果是可選的,那該怎麼寫?
也許有人想到了是不是可以用MyBatis 中的動態SQL,使用<if>標籤等等?
再回答這個問題前,我們先看處理動態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 處理動態SQL 時,所有動態SQL 的標籤都會處理為SqlNode (這裡的rootSqlNode)對象,包含${}的也會處理為TextSqlNode 對象,在上面方法的前兩行,就是MyBatis 處理動態SQL 的地方。
因此如果我們在${sql} 中的內容包含嵌套的${}和<if>,<where>等標籤時,他們在MyBatis 解析XML 為SqlNode 對象時,XML <select> 元素包含的內容只有${sql},只有${sql}會被解析,在運行時這個參數字符串中可能包含的${}和<if>,<where>等標籤,但是這都發生在MyBatis 解析後,因此當這些內容作為字符串中的一部分出現時,他們不會被特殊處理,他們只是SQL中的一部分,只是原樣輸出(由於數據庫不認會報錯)無法被處理,因此沒法通過MyBatis 自帶的這種方式來寫動態SQL。
提示
在上面的代碼中:
sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
這一段代碼是處理動態參數(#{})的這個處理在動態SQL 處理之後, 因此可以在SQL 中使用這種類型的參數。
既然不能用MyBatis 動態SQL 方式,該怎麼實現動態SQL 呢?
這裡提供一個簡單的思路,在SQL 中使用模板標記語言來實現動態SQL(例如freemarker),在SQL 交給MyBatis 執行之前,使用模闆對SQL 進行處理生成最終執行的SQL(需要避免處理#{}參數),將這個SQL交給MyBatis 執行。
舉一個Freemarker模板的例子,仍然以上面的SQL為基礎:
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>區別就是'%${userName}%',因為Freemarker 也會處理${userName},也會用實際的值來替換這裡的參數。
在前面調用的代碼中,這裡修改如下:
//#{enabled}map.put("enabled", 1);//#{userName}map.put("userName", "admin");//這裡的sql 對應XML 中的${sql}String sql = "上面兩個複雜SQL中的一個";//使用Freemarker處理sqlsql = processSqlByFreemarker(sql, map);//將處理後的sql放到map中map.put("sql", "select * from sysuser "+ " where enabled = #{enabled} "+ " and userName like concat('%',#{userName},'%')");//執行方法List<Map> list = xxMapper.executeSql(map);注:processSqlByFreemarker方法就是根據map中的數據來處理sql字符串,實現方式可以自己搜索。
到這裡,一個不是很複雜的動態SQL功能就實現了。
不知道有沒有更貪心的人,你會不會想,上面返回值都是List<Map>類型,能不能返回一個我指定的實體類呢?
例如在map中:
map.put("class", "tk.mybatis.model.SysUser");能不能通過這種方式讓返回值變成SysUser類型呢?由於這篇文章耗時已經太長,這裡就提供一個方案,不深入。
你可以使用攔截器實現,獲取MappedStatement 後,複製一份,然後修改resultMaps中resultMap的type屬性為你指定的class類型就能實現,說起來容易,實際操作起來能有PageHelper 分頁插件1/10 左右的工作量。
因為這篇是應媳婦要求所寫,所以假如媳婦有最後的這個需求,我就協助媳婦實現這個插件,然後再共享出來。
注:如果是動態的update,insert,delete 語句,可以將上面的<select>改為update(不需要使用<delete>和<insert>),返回值用int,比select 的情況容易很多。
以上所述是小編給大家介紹的MyBatis 執行動態SQL語句詳解,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!