前言

最近在開發專案的時候涉及到複雜的動態條件查詢,但是mybaits本身不支援if elseif類似的判斷但是我們可以間接通過 chose when otherwise 去實現其中choose為一個整體 when是if otherwise是else

快速使用

以前我們進行條件判斷時候使用if標籤進行判斷,條件並列存在

		    <if test="seat_no != null and seat_no != '' ">
AND seat_no = #{seat_no}
</if>

現在 使用chose when otherwise條件只要有一個成立,其他的就不會再判斷了。如果沒有成立的條件則預設執行otherwise中的內容

		<choose>
<when test="……">
……
</when>
<otherwise>
……
</otherwise>
</choose>

以下是我自己真實使用的例子,並且經過了測試,僅供參考:

根據動態條件篩選查詢使用者資訊

<select id="findUsersByUser" resultType="cn.soboys.kmall.sys.entity.User">
select tu.USER_ID,tu.USERNAME,tu.SSEX,td.DEPT_NAME,tu.MOBILE,tu.EMAIL,tu.STATUS,tu.CREATE_TIME,
td.DEPT_ID
from t_user tu left join t_dept td on tu.DEPT_ID = td.DEPT_ID <where>
<choose>
<when test="userParams.adminType==4">
and tu.ADMIN_TYPE_ID in(2,3)
</when>
<otherwise>
<include refid="search"></include>
</otherwise>
</choose> </where> </select>
 <sql id="search">
<if test="userParams.adminType==null or userParams.adminType==''">
and tu.ADMIN_TYPE_ID in(0,1)
</if> <if test="userParams.adminType != null and userParams.adminType != ''">
and tu.ADMIN_TYPE_ID=#{userParams.adminType}
</if> <if test="userParams.roleId != null and userParams.roleId != ''">
and (select group_concat(ur.ROLE_ID)
from t_user u
right join t_user_role ur on ur.USER_ID = u.USER_ID,
t_role r
where r.ROLE_ID = ur.ROLE_ID
and u.USER_ID = tu.USER_ID and r.ROLE_ID=#{userParams.roleId})
</if> <if test="userParams.mobile != null and userParams.mobile != ''">
AND tu.MOBILE =#{userParams.mobile}
</if>
<if test="userParams.username != null and userParams.username != ''">
AND tu.USERNAME like CONCAT('%',#{userParams.username},'%')
</if>
<if test="userParams.ssex != null and userParams.ssex != ''">
AND tu.SSEX =#{userParams.ssex}
</if>
<if test="userParams.status != null and userParams.status != ''">
AND tu.STATUS =#{userParams.status}
</if>
<if test="userParams.deptId != null and userParams.deptId != ''">
AND td.DEPT_ID =#{userParams.deptId}
</if>
<if test="userParams.createTime != null and userParams.createTime != ''">
AND DATE_FORMAT(tu.CREATE_TIME,'%Y%m%d') BETWEEN substring_index(#{userParams.createTime},'#',1) and substring_index(#{userParams.createTime},'#',-1)
</if>
</sql>

這裡就用到啦 if else if 判斷。 choose標籤中when條件一但不成立,就會執行otherwise標籤中的條件,判斷語句,也就是我下面包含的sql片段條件

更詳細的條件標籤使用參考我這一篇文章點選進入

SQL片段拼接

我們再寫sql語句的時候往往會有這樣一些要求,一些重複的sql語句片段,我們不想重複去寫,那麼可以通過sql片段方式去抽離,公共sql然後在需要的地方去引用

MyBatis<sql> 元素用於定義一個 SQL 片段,用於分離一些公共的 SQL 語句,例如:SELECT 關鍵字和 WHERE 關鍵字之間的部分。其中:

id:唯一識別符號,用於在其他地方使用 <include> 標籤引用;

lang:設定字元編碼;

databaseId:指定執行該 SQL 語句的資料庫ID,資料庫ID在 mybatis-cfg.xml 中的 中配置。

同時,你也能夠看見 <sql> 標籤中可以使用<include>、<trim>、<where>、<set>、<foreach>、<choose>、<if>、<bind>等標籤定義複雜的 SQL 片段

簡單使用定義sql片段如下:

<sql id="user_columns">
`user_id`, `name`, `sex`, `age`
</sql>

<sql> 標籤中使用 <include> 標籤引入定義的sql片段,如下:

<!-- 定義基礎列 -->
<sql id="user_base_columns">
`user_id`, `name`
</sql> <!-- 定義一個SQL片段 -->
<sql id="user_columns">
<include refid="user_base_columns"/>, `sex`, `age`
</sql>

場景使用案例如:查詢使用者資訊

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hxstrive.mybatis.sql.demo1.UserMapper">
<!-- 對映結果 -->
<resultMap id="RESULT_MAP" type="com.hxstrive.mybatis.sql.demo1.UserBean">
<id column="user_id" jdbcType="INTEGER" property="userId" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="sex" jdbcType="VARCHAR" property="sex" />
<result column="age" jdbcType="INTEGER" property="age" />
</resultMap> <!-- 定義一個SQL片段 -->
<sql id="user_columns">
`user_id`, `name`, `sex`, `age`
</sql> <!-- 查詢所有使用者資訊 -->
<select id="findAll" resultMap="RESULT_MAP">
select <include refid="user_columns" /> from `user`
</select> </mapper>

SQL引數取值和OGNL表示式

看到我們上面去值引數通過#{params}這種方式來去值的其中傳進來的引數 #{xx} 就是使用的 OGNL 表示式。

Mybatis 官方文件中「XML 對映檔案」模組裡邊,有解析到:

說當我們使用 #{} 型別引數符號的時候,其實就是告訴 Mybatis 建立一個預處理語句引數,通過 JDBC,這樣的一個引數在 SQL 中會由一個 "?" 來標識,並傳遞到一個新的預處理語句中。



也就是說當我們使用 #{XX} OGNL 表示式的時候, 它會先幫我們生成一條帶佔位符的 SQL 語句,然後在底層幫我們設定這個引數:ps.setInt(1, id);

OGNL 是 Object-Graph Navigation Language 的縮寫,物件-圖行導航語言,語法為:#{ }。

是不是有點懵,不知道這是個啥?

OGNL 作用是在物件和檢視之間做資料的互動,可以存取物件的屬性和呼叫物件的方法,通過表示式可以迭代出整個物件的結構圖

MyBatis常用OGNL表示式如下:

上述內容只是合適在MyBatis中使用的OGNL表示式,完整的表示式點選這裡

MyBatis中可以使用OGNL的地方有兩處:

  1. 動態SQL表示式中
  2. ${param}引數中

如下例子MySql like 查詢:

<select id="xxx" ...>
select id,name,... from country
<where>
<if test="name != null and name != ''">
name like concat('%', #{name}, '%')
</if>
</where>
</select>

上面程式碼中test的值會使用OGNL計算結果。

例二,通用 like 查詢:

<select id="xxx" ...>
select id,name,... from country
<bind name="nameLike" value="'%' + name + '%'"/>
<where>
<if test="name != null and name != ''">
name like #{nameLike}
</if>
</where>
</select>

這裡的value值會使用OGNL計算。

注:對<bind引數的呼叫可以通過#{}或 ${} 方式獲取,#{}可以防止注入。

在通用Mapper中支援一種UUID的主鍵,在通用Mapper中的實現就是使用了標籤,這個標籤呼叫了一個靜態方法,大概方法如下:

<bind name="username_bind"
value='@java.util.UUID@randomUUID().toString().replace("-", "")' />

這種方式雖然能自動呼叫靜態方法,但是沒法回寫對應的屬性值,因此使用時需要注意。

  1. ${params}中的引數

上面like的例子中使用下面這種方式最簡單

<select id="xxx" ...>
select id,name,... from country
<where>
<if test="name != null and name != ''">
name like '${'%' + name + '%'}'
</if>
</where>
</select>

這裡注意寫的是${'%' + name + '%'},而不是%${name}%,這兩種方式的結果一樣,但是處理過程不一樣。

MyBatis中處理${}的時候,只是使用OGNL計算這個結果值,然後替換SQL中對應的${xxx},OGNL處理的只是${這裡的表示式}。

這裡表示式可以是OGNL支援的所有表示式,可以寫的很複雜,可以呼叫靜態方法返回值,也可以呼叫靜態的屬性值。

例子,條件判斷入參屬性值是否包含子字串可以直接使用 contains判斷

<foreach collection="list" item="item" index="index" separator="AND" open="(" close=")">

    <choose>
<when test='item.cname.contains("select") or item.cname.contains("checkbox") or item.cname.contains("date")'>
<if test='item.cname.contains("select") or item.cname.contains("checkbox")'>
find_in_set(#{item.value},base.${item.cname})
</if> <if test='item.cname.contains("date")'>
DATE_FORMAT(base.${item.cname},'%Y-%m-%d') = DATE_FORMAT(#{item.value},'%Y-%m-%d')
</if>
</when>
<otherwise>
base.${item.cname} = #{item.value}
</otherwise>
</choose> </foreach>