1. 程式人生 > >Mybatis的動態SQL詳解

Mybatis的動態SQL詳解

使用動態SQL完成多條件查詢

動態SQL是MyBatis的一個強大的特性。動態SQL基於OGNL的表示式。實現動態SQL的元素如下。

  • if:利用if實現簡單的條件選擇
  • choose(when,otherwise):相當於Java中的switch語句,通常與when和otherwise搭配
  • where:簡化SQL語句中where的條件判斷。
  • set:解決動態更新語句 trim:可以靈活地去除多餘的關鍵字
  • foreach:迭代一個集合,通常用於in條件

    使用if+where實現條件查詢

    1.if 多新增查詢 修改UserMapper.xml檔案
<select id="getUserList" resultMap="userList">
    SELECT u.*,r.roleName FROM USER u,Role r WHERE u.userRole=r.id
    <if test="userRole!=null">
        AND u.userRole=#{userRole}
    </if>
    <if test="userName!=null and userName!=''">
        AND u.userName LIKE concat('%',#{userName},'%')  
    </if> 
 </select>
 注:儘量避免用*此處作為演示

修改介面中的方法

List<User> getUserList(@Param("userRole")Integer roleId,@Param("userName")String userName);

編寫測試

SqlSession sqlSession=null;
try{
    sqlSession=MyBatisUtil.createSqlSession();
    List<User> userList=sqlSession.getMapper(UserMapper.class).getUserList(2,null);
    for (User user: userList) {
        System.out.println(user.getUserName());
    }
}catch (Exception ex){
    ex.printStackTrace();
}finally {
    MyBatisUtil.closeSqlSession(sqlSession);
}

2.where

where元素標籤會自動識別標籤內是否有返回值,若有,就插入一個where,此外,若該標籤返回的內容是and或者or卡頭的,會自動剔除

<!--注意開啟自動對映-->
<select id="getUserList" resultType="User">
    SELECT * FROM USER
    <where>
        <if test="userName!=null and userName!=''">
            AND userName LIKE concat('%',#{userName},'%')
        </if>
        <if test="userRole!=null">
            AND userRole=#{userRole}
        </if>
    </where>
</select>

編寫測試

SqlSession sqlSession=null;
try{
    sqlSession=MyBatisUtil.createSqlSession();
    Integer roleId=1;
    String userName="";     List<User> 
userList=sqlSession.getMapper(UserMapper.class).getUserList(roleId,userName);
    for (User user: userList) {
        System.out.println(user.getUserName());
    }
}catch (Exception ex){
    ex.printStackTrace();
}finally {
    MyBatisUtil.closeSqlSession(sqlSession);
}

3. 使用if+trim實現多條件查詢

trim會自動識別其標籤內是否有返回值,加入字首或字尾

<select id="getUserList" resultType="User">
    SELECT * FROM USER
    <trim prefix="where" prefixOverrides="and | or">
        <if test="userName!=null and userName!=''">
            AND userName LIKE concat('%',#{userName},'%')
        </if>
        <if test="userRole!=null">
            AND userRole=#{userRole}
        </if>
    </trim>
</select>

prefix:字首,作用是通過自動識別是否有返回值後,在trim包含的內容上加上字首,如此處的 where suffix:字尾,作用是在trim包含的內容上加上字尾 prefixOverrides:對於trim包含內容的首部進行指定內容(如此處的"and | or")的忽略 suffixOverrides:對用trim包含的內容的首尾進行指定內容的忽略

2 使用動態SQL實現更新操作

使用if+set改造更新操作 set元素主要用於更新操作,在包含的語句前輸出一個set,若包含的語句逗號結尾,自動忽略逗號

<update id="modify" parameterType="User">
    UPDATE USER
     <set>
         <if test="userCode!=null">userCode=#{userCode},</if>
         <if test="userName!=null">userName=#{userName},</if>
         <if test="userPassword!=null">userPassword=#{userPassword},</if>
         <if test="gender!=null">gender=#{gender},</if>
         <if test="phone!=null">phone=#{phone},</if>
         <if test="address!=null">address=#{address},</if>
         <if test="userRole!=null">userRole=#{userRole},</if>
         <if test="modifyBy!=null">modifyBy=#{modifyBy},</if>
         <if test="modifyDate!=null">modifyDate=#{modifyDate},</if>
         <if test="birthday!=null">birthday=#{birthday},</if>    
         </set>
    WHERE id=#{id}
</update>

使用if+trim改造修改操作

<update id="modify" parameterType="User">
    UPDATE USER
     <trim prefix="set" suffixOverrides="," suffix="where id=#{id}">
         <if test="userCode!=null">userCode=#{userCode},</if>
         <if test="userName!=null">userName=#{userName},</if>
         <if test="userPassword!=null">userPassword=#{userPassword},</if>
         <if test="gender!=null">gender=#{gender},</if>
         <if test="phone!=null">phone=#{phone},</if>
         <if test="address!=null">address=#{address},</if>
         <if test="userRole!=null">userRole=#{userRole},</if>
         <if test="modifyBy!=null">modifyBy=#{modifyBy},</if>
         <if test="modifyDate!=null">modifyDate=#{modifyDate},</if>
         <if test="birthday!=null">birthday=#{birthday},</if>    
         </trim>
</update>

3 使用foreach完成複雜查詢

foreach主要用在構建in條件中,在sql語句中迭代一個集合。它的主要屬性有,item、index、 collection、separator、close、open。

1.MyBatis入參為陣列型別的foreach型別 編寫介面

List<User> getUserByRoleId_foreach_array(Integer[] roleIds);

修改UserMapper.xml

<resultMap id="userMapByRole" type="User">
    <id property="id" column="id"/>
    <result property="userCode" column="userCode"/>
    <result property="userName" column="userName"/>
</resultMap>
<select id="getUserByRoleId_foreach_array" resultMap="userMapByRole">
    SELECT * FROM USER WHERE userRole IN
    <foreach collection="array" item="roleIds" open="(" separator="," close=")">
        #{roleIds}
    </foreach>
</select>

編寫測試

SqlSession sqlSession=null;
List<User> userList=new ArrayList<User>(); Integer[] roleIds={2,3}; try{
    sqlSession=MyBatisUtil.createSqlSession();
    
userList=sqlSession.getMapper(UserMapper.class).getUserByRoleId_foreach_array(roleIds); }catch (Exception ex){
    ex.printStackTrace();
}finally {
    MyBatisUtil.closeSqlSession(sqlSession);
}
for (User user:      userList) {
    System.out.println(user.getUserName()+"\t"+user.getAddress());
}

item:表示集合中每一個元素進行迭代時的別名 index:指定一個名稱,用於表示在迭代過程中,每次迭代到的位置 open:表示該語句以什麼開始(in語句以"("開始) separator:表示在每次迭代之間以什麼符號做分割符 close:表示該語句以什麼結束 collection:必須指定,入參為單參型別是List時,collection屬性值為list;入參為單參是陣列時,為 array;若為多參,需封裝Map parameterType可以不配置,MyBatis會自動封裝為Map傳入。

在介面中新增方法
List<User> getUserByRoleId_foreach_list(List<Integer> roleList);

修改UserMapper.xml

<resultMap id="userMapByRole" type="User">
    <id property="id" column="id"/>
    <result property="userCode" column="userCode"/>
    <result property="userName" column="userName"/>
</resultMap>
<select id="getUserByRoleId_foreach_list" resultMap="userMapByRole">
    SELECT * FROM USER WHERE userRole IN
    <foreach collection="list" item="roleIds" open="(" separator="," close=")">
        #{roleIds}     </foreach>
</select>

編寫測試

SqlSession sqlSession=null;
List<User> userList=new ArrayList<User>(); List<Integer> nums=new ArrayList<Integer>(); nums.add(1); nums.add(2); try{
    sqlSession=MyBatisUtil.createSqlSession();
    userList=sqlSession.getMapper(UserMapper.class).getUserByRoleId_foreach_list(nums); }catch (Exception ex){
    ex.printStackTrace();
}finally {
    MyBatisUtil.closeSqlSession(sqlSession);
}
for (User user:
        userList) {
    System.out.println(user.getUserName()+"\t"+user.getAddress());
}

3.MyBatis入參為Map型別的foreach迭代

處理多個引數入參,編寫介面中的方法

List<User> getUserByConditionMap_foreach_map(Map<String,Object> conditionMap); 

修改UserMapper.xml

<select id="getUserByConditionMap_foreach_map" resultMap="userMapByRole">
    SELECT * FROM USER WHERE gender=#{gender} AND userRole IN
    <foreach collection="roleIds" item="roleMap" open="(" separator="," close=")">         #{roleMap}
    </foreach>
</select>

編寫測試

SqlSession sqlSession = null;
List<User> userList = new ArrayList<User>();
Map<String, Object> param = new HashMap<String, Object>(); List<Integer> roleList = new ArrayList<Integer>(); roleList.add(1); roleList.add(2); param.put("gender",2); param.put("roleIds",roleList); try {
    sqlSession = MyBatisUtil.createSqlSession();
    userList = 
sqlSession.getMapper(UserMapper.class).getUserByConditionMap_foreach_map(param); } catch (Exception ex) {
    ex.printStackTrace();
} finally {
    MyBatisUtil.closeSqlSession(sqlSession);
}
for (User user :         userList) {
    System.out.println(user.getUserName() + "\t" + user.getAddress());
}

==剖析知識點==

1) MyBatis接受的引數型別:基本型別、物件、List、陣列、Map 2) 無論MyBatis的入參是哪種資料型別,MyBatis都會將引數放在一個Map中,對於單引數入參的情況:

  • 若入參為基本型別:變數名作為key,變數值為value,此時生成的Map只有一個元素若入
  • 若入參為List:預設“list”作為key,該List即為value 若入參為陣列:預設“array”作為key,該陣列即為value 若入參為Map:鍵值不變

    4. choose (when、otherwise)

    choose可以選擇其中一種情況下的查詢結果,流程和switch相同編寫介面方法
List<User> getUserList_choose(@Param("userName")String userName,@Param("userRole")Integer roleId,
                              @Param("userCode")String userCode,@Param("creationDate")Date creationDate);

修改UserMapper.xml

<select id="getUserList_choose" resultType="User">
    SELECT * from USER WHERE 1=1
    <choose>
        <when test="userName!=null and userName!=''">
            AND userName=#{userName}
        </when>
        <when test="userCode!=null and userCode!=''">
            AND userCode LIKE concat('%',#{userCode},'%')
        </when>
        <when test="userRole!=null and userRole!=''">
            AND userRole=#{userRole}
        </when>
        <otherwise>
            AND YEAR(creationDate)=YEAR(#{creationDate})         </otherwise>
    </choose>
</select>

編寫測試

String userName="";
Integer roleId=1;
String userCode="";
Date  creationDate=new SimpleDateFormat("yyyy-MM-dd").parse("2018-2-7");
SqlSession sqlSession = null;
List<User> userList = new ArrayList<User>();
try {
    sqlSession = MyBatisUtil.createSqlSession();
    userList = 
sqlSession.getMapper(UserMapper.class).getUserList_choose(userName,roleId,userCode,null); } catch (Exception ex) {
    ex.printStackTrace();
} finally {
    MyBatisUtil.closeSqlSession(sqlSession);
}
for (User user :         userList) {
    System.out.println(user.getUserName() + "\t" + user.getAddress());
}

4.MyBatis實現分頁功能

1) 使用聚合函式count() 獲得總記錄數(在之前的示例中已經完成) UserMapper.java int count();

2)實現分頁,通過limit(起始位置,頁面容量) 修改UserMapper.java,增加分頁方法

List<User> getUserList(@Param("userName")String userName,
                       @Param("userRole")Integer roleId,
                       @Param("from")Integer currentPageNo,
                       @Param("pageSize")Integer pageSize);

編寫UserMapper.xml

<select id="getUserList" resultType="User">
    SELECT u.*,r.roleName FROM USER u,role r WHERE u.userRole=r.id
    <if test="userRole!=null">
        AND u.userRole=#{userRole}
    </if>
    <if test="userName!=null and userName!=''">
        AND u.userName LIKE concat('%',#{userName},'%')
    </if>
    ORDER BY creationDate DESC limit #{from},#{pageSize}
</select>

編寫測試程式碼

SqlSession sqlSession = null; 
try {
    sqlSession = MyBatisUtil.createSqlSession();
    List<User> userList = 
sqlSession.getMapper(UserMapper.class).getAddressListByUserId(1);
    for (User user : userList) {
        System.out.println(user.getUserName());         List<Address> addresses = user.getAddressList();         for (Address address :
                addresses) {
            System.out.println("--- " + address.getContact());
        }
    }
} catch (Exception ex) {
    ex.printStackTrace();
} finally {
    MyBatisUtil.closeSqlSession(sqlSession);
}