1. 程式人生 > >Mybatis 對映檔案的 SQL 深入

Mybatis 對映檔案的 SQL 深入

1.1 概述

  • Mybatis 的對映檔案中,有些時候業務邏輯複雜時,我們的 SQL是動態變化的
  • 把判斷放在對映檔案中,使用動態SQL標籤:<if>、<where>、 <foreach>、<sql>、<include>

 

 1.2動態SQL之<if>標籤

  • 我們根據實體類的不同取值,使用不同的 SQL 語句來進行查詢。比如在 id 如果不為空時可以根據 id查詢,如果 username 不同空時還要加入使用者名稱作為條件。這種情況在我們的

    多條件組合查詢中經常會碰到。

  1. 需求: 根據id或者名稱查詢
  2. dao查詢方法
package com.sunny.dao;

import com.sunny.entity.QueryVo;
import com.sunny.entity.User;

import java.util.List;

/**
 * 使用者資料訪問介面
 */
public interface IUserDao {

    //根據id或username條件查詢使用者列表
    List<User> findByCondition(User user);

    //根據主鍵查詢實體類物件
    User findById(int id);
}

3.介面對映 IUserDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace名稱空間,用於定義是哪個類的對映檔案,這裡需要寫所對映介面的類全名-->
<mapper namespace="com.sunny.dao.IUserDao">
    <!--根據主鍵查詢實體類物件-->
    <select id="findById" resultType="com.sunny.entity.User" parameterType="int">
        SELECT *  FROM USER WHERE id = #{id}
    </select>

        動態SQL
            <if> 條件判斷標籤
                test屬性,用來判斷的表示式,返回boolean型別
                    test=" id != 0"這裡的id就是parameterType中User物件的id屬性
                                   這裡的id也是獲取物件的id屬性值
    <select id="findByCondition" resultType="com.sunny.entity.User">
        SELECT * FROM USER WHERE 1=1
        <if test="id != 0">
        AND id = #{id}
        </if>
        <if test="username != null and username != ''">
        and username like #{username}
        </if>
    </select>

</mapper>

 4.測試

import com.sunny.dao.IUserDao;
import com.sunny.entity.QueryVo;
import com.sunny.entity.User;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;


import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class UserDaoTest {

    InputStream in = null;
    SqlSession sqlSession = null;

    @Before
    public void before() throws IOException {
        //1.獲取主配置檔案的輸入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.建立資料庫連線工廠構建類
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //3.根據資料庫連線工廠構建類建立資料庫連線工廠
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
        //4.根據資料庫連線工廠獲取資料庫連線SqlSession物件
        sqlSession = sqlSessionFactory.openSession();
    }

    /**
     * 根據主鍵查詢實體類物件
     */
    @Test
    public void findById(){
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);
        System.out.println(userDao.findById(51));
    }

    /**
     * 根據id或username條件查詢使用者列表
     */
    @Test
    public  void findByCondition() throws IOException, ParseException {
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);
        User user = new User();
        user.setUsername("%張%");
        user.setId(41);
        List<User> userList = userDao.findByCondition(user);
        userList.forEach(System.out::println);
    }

 
    
    @After
    public void after() throws IOException {
        //提交事務
        sqlSession.commit();
        //關閉資源
        sqlSession.close();
        in.close();
    }
}

5.測試結果


 1.3 動態SQL之<where>標籤

  • 作用:通過<where>標籤拼接where條件,簡化where寫法。
  <!--使用where標籤優化上面的where-->
    <select id="findByCondition" resultType="com.sunny.entity.User">
        SELECT * FROM USER
            <where>
            <if test="id != 0">
            and id = #{id}
            </if>
            <if test="id != null and id !='' ">
            and username like #{username}
            </if>
        </where>
    </select>

1.4 動態標籤之foreach標籤

  1. 作用:遍歷引數值。
  2. 查詢需求:根據多個id查詢。

    SELECT * FROM USER WHERE id=41 OR id=42

    SELECT * FROM USER WHERE id IN (41,42)
  3. 定義查詢擴充套件物件,封裝多個id值
package com.sunny.entity;

import java.util.List;

/**
 * 當前QueryVo支援User物件的所有屬性查詢以及擴充套件屬性
 */
public class QueryVo extends User {

    //條件:多個id
    private List<Integer> ids;

    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }
}

 5.dao介面,對映

package com.sunny.dao;

import com.sunny.entity.QueryVo;
import com.sunny.entity.User;

import java.util.List;

/**
 * 使用者資料訪問介面
 */
public interface IUserDao {

    //根據多個id查詢使用者列表
    List<User> findByCondition2(QueryVo queryVo);
    
}

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace名稱空間,用於定義是哪個類的對映檔案,這裡需要寫所對映介面的類全名-->
<mapper namespace="com.sunny.dao.IUserDao">
    <!--根據主鍵查詢實體類物件-->
    <select id="findById" resultType="com.sunny.entity.User" parameterType="int">
        SELECT *  FROM USER WHERE id = #{id}
    </select>
    
    <!--根據多個id查詢使用者列表
        目標:使用mybatis動態標籤foreach標籤實現sql,select * from user where id in(41,43)
        foreach:用於迴圈遍歷
        collection:用於遍歷的集合,必須時輸入型別的屬性
        open:拼接SQL的開始部分
        close:拼接SQL的結束部分
        separator:迴圈拼接的分隔符號
        item:要遍歷的元素(集合裡面的每個元素名字)
        #{id}:佔位符的值,裡面id名字與item設定的名字保持一致
    -->
    <sql id="selectUser">
        SELECT * FROM USER
    </sql>
    <select id="findByCondition2" resultType="com.sunny.entity.User" parameterType="com.sunny.entity.QueryVo">
        <include refid="selectUser"></include>
        <where>
            <if test="ids!=null and ids.size()>0">
                <foreach collection="ids" open=" id IN (" separator="," close=")" item="id">
                    #{id}
                </foreach>
            </if>
        </where>
    </select>
    
</mapper>

 6.測試

import com.sunny.dao.IUserDao;
import com.sunny.entity.QueryVo;
import com.sunny.entity.User;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;


import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class UserDaoTest {

    InputStream in = null;
    SqlSession sqlSession = null;

    @Before
    public void before() throws IOException {
        //1.獲取主配置檔案的輸入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.建立資料庫連線工廠構建類
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //3.根據資料庫連線工廠構建類建立資料庫連線工廠
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
        //4.根據資料庫連線工廠獲取資料庫連線SqlSession物件
        sqlSession = sqlSessionFactory.openSession();
    }

    /**
     * 根據多個id查詢使用者列表
     * @throws IOException
     */
    @Test
    public  void findByCondition2() throws IOException, ParseException {
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);
        QueryVo queryVO = new QueryVo();
        List<Integer> ids = new ArrayList<>();
        ids.add(42);
        ids.add(47);
        ids.add(45);
        ids.add(46);
        queryVO.setIds(ids);

        List<User> userList = userDao.findByCondition2(queryVO);
        userList.forEach(System.out::println);
    }
    
    @After
    public void after() throws IOException {
        //提交事務
        sqlSession.commit();
        //關閉資源
        sqlSession.close();
        in.close();
    }
}

1.5mybatis中簡化編寫的SQL片段

  • Sql 中可將重複的 sql 提取出來,使用時用 include 引用即可,最終達到 sql 重用的目的。我們先到 UserDao.xml 檔案中使用<sql>標籤,定義出公共部分。
<!--
    sql標籤:定義SQL片段,抽取公用的SQL部分
    include標籤: 用來引用sql片段
-->
<sql id="selectUser">
    SELECT * FROM USER WHERE 1=1
</sql>
<select id="findByCondition2" parameterType="queryvo" resultType="user">
    <!--引用SQL片段-->
    <include refid="selectUser"></include>
    <if test="ids != null and ids.size()>0">
        <foreach collection="ids" open="and id IN (" separator="," close=")" item="id">
            #{id}
        </foreach>
    </if>
</select>
  •  再通過<include>標籤的 refid 屬性的值就是<sql> 標籤定義 id 的取值。

    注意:如果引用其它 mapper.xml 的 sql 片段,則在引用時需要加上 namespace,如下:
    <include refid="namespace.sql 片段”/>