MyBatis學習筆記(二)——DAO的開發、配置文件、動態SQL

Hibernate與Mybatis的本質區別和應用場景

  • Hibernate:標準的ORM框架,不須要寫SQL語句,可是優化和修改SQL語句比較難。java

    • 應用於需求變化固定的中小型的項目,例如後臺管理系統、ERP、ORM、OA。mysql

  • Mybatis:專一SQL自己,SQL的優化比較方便,是不徹底的ORM。程序員

    • 主要適用於需求變化較多的項目,例如互聯網項目。spring

mybatis開發dao的方法

SqlSession的應用場合

SqlSessionFactoryBuilder

經過SqlSessionFactoryBuilder建立會話工廠SqlSessionFactory
將SqlSessionFactoryBuilder當成一個工具類使用便可,不須要使用單例管理SqlSessionFactoryBuilder。
在須要建立SqlSessionFactory時候,只須要new一次SqlSessionFactoryBuilder便可。sql

SqlSessionFactory

經過SqlSessionFactory建立SqlSession,使用單例模式管理sqlSessionFactory(工廠一旦建立,使用一個實例)。
mybatis和spring整合後,可使用Ioc容器管理。shell

SqlSession

SqlSession是一個面向用戶(程序員)的接口。
SqlSession中提供了不少操做數據庫的方法:如:selectOne(返回單個對象)、selectList(返回單個或多個對象)。
SqlSession是線程不安全的,在SqlSesion實現類中除了有接口中的方法(操做數據庫的方法)還有數據域屬性。
SqlSession最佳應用場合在方法體內,定義成局部變量使用。數據庫

原始的DAO開發方法

程序員須要編寫DAO和DAO的實現類。須要向DAO實現類中注入SqlSessionFactory,在方法體內經過SqlSessionFactory來建立SqlSession。數組

  • 定義DAO接口緩存

public interface UserDAO {

    /**
     * 根據本id查詢用戶
     */
    User findUserById(int id) throws Exception;
    
    /**
     * 添加用戶
     */
    void insertUser(User user) throws Exception;
    
    /**
     * 根據id刪除用戶
     */
    void deleteUser(int id) throws Exception;
}
  • DAO實現類安全

/**
* @ClassName: UserDAOImpl 
* @Description: DAO實現類(注意:SqlSession是非線程安全的,故不能聲明爲全局的)
 */
public class UserDAOImpl implements UserDAO {

    SqlSessionFactory sqlSessionFactory;
    /**
     * 向DAO實現類中注入SqlSessionFactory(此處經過構造方法注入)
     */
    public UserDAOImpl(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }
    
    @Override
    public User findUserById(int id) throws Exception {
        
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = sqlSession.selectOne("test.findUserById", id);
        sqlSession.close();
        return user;
    }

    @Override
    public void insertUser(User user) throws Exception {
        
        SqlSession sqlSession = sqlSessionFactory.openSession();
        sqlSession.insert("test.insertUser", user);
        sqlSession.commit();
        sqlSession.close();
    }

    @Override
    public void deleteUser(int id) throws Exception {
        
        SqlSession sqlSession = sqlSessionFactory.openSession();
        sqlSession.delete("test.deleteUser", id);
        sqlSession.commit();
        sqlSession.close();
    }

}

新建一個源代碼目錄命名爲test,在UserDAOImpl類中鼠標右鍵新建一個Junit Test Case,更換源代碼目錄爲test並勾選咱們須要測試的方法:

public class UserDAOImplTest {

    private SqlSessionFactory sqlSessionFactory;
    
    /**
     * 此方法在執行測試以前執行,獲得一個SqlSessionFactory
     */
    @Before
    public void setUp() throws Exception {
        
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("SqlMapConfig.xml"));
    }

    @Test
    public void testFindUserById() throws Exception {
        // 建立UserDAO(在構造中注入SqlSessionFactory)
        UserDAO userDAO = new UserDAOImpl(sqlSessionFactory);
        User user = userDAO.findUserById(1);
        System.out.println(user);
    }

    @Test
    public void testInsertUser() throws Exception {
        UserDAO userDAO = new UserDAOImpl(sqlSessionFactory);
        User user = new User();
        user.setSex("2");
        user.setUsername("孫悟空");
        user.setAddress("方寸靈臺山");
        user.setBirthday(new Date());
        userDAO.insertUser(user);
    }

    @Test
    public void testDeleteUser() throws Exception {
        UserDAO userDAO = new UserDAOImpl(sqlSessionFactory);
        userDAO.deleteUser(27);
    }

}

原始DAO開發中存在的問題

  • DAO的接口實現類中存在大量的模板方法,設想:能夠將重複的代碼提取出來。

  • 在SqlSession的方法時將Statement的id硬編碼在你DAO的實現類中。

  • 調用sqlSession的相關方法傳入參數是泛型,即便傳入錯誤的參數,編譯時候也不會報錯

Mapper接口開發

程序員只須要編寫Mapper接口(至關於DAO接口)和Mapper.xml。Mapper接口的編寫須要遵照相關規範:

  1. mapper.xml中的命名空間等於Mapper接口類的全路徑;

  2. mapper.java中的方法名和mapper.xml中的statement的id一致;

  3. mapper接口中方法的輸入參數類型和mapper.xml中的ParameterType一致;

  4. mapper接口中的方法的返回值類型和mapper.xml中的ResultType一致。

遵循以上規範,mybatis就能夠自動生成(反射機制)相關的mapper代理的實現類的對象。

UserMapper.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">

<!-- 命名空間用來對SQL進行分類管理(SQL隔離)
    此處採用Mapper代理開發,命名空間爲Mapper接口的地址
 -->    
<mapper namespace="org.gpf.mapper.UserMapper">

<!-- statement的id和Mapper接口中的方法名一致
     parameterType和Mapper接口中的方法參數一致
     resultType和Mapper接口中的方法返回值一致
 -->
<!-- 根據id查詢用戶 -->
<select id="findUserById" parameterType="int" resultType="org.gpf.po.User">
    SELECT * FROM user WHERE id = #{id};
</select>

<!-- 根據用戶名查找用戶 -->
<select id="findUsersByName" parameterType="java.lang.String" resultType="org.gpf.po.User">
    <!--  易出錯
        SELECT * FROM user WHERE username LIKE #{value};
    -->
    <!-- 使用${}進行SQL拼接 -->
    SELECT * FROM user WHERE username LIKE '%${value}%';
</select>
<!-- 添加用戶 -->
<insert id="insertUser" parameterType="org.gpf.po.User">
    <selectKey keyProperty="id" order="AFTER" resultType="int">
         SELECT  LAST_INSERT_ID();
    </selectKey>
    INSERT INTO user(username,birthday,sex,address) VALUES(#{username},#{birthday},#{sex},#{address});
</insert>
<!-- 刪除用戶 -->
<delete id="deleteUser" parameterType="java.lang.Integer">
    DELETE FROM user WHERE id = #{id}
</delete>
<!-- 更新用戶 -->
<update id="updateUser" parameterType="org.gpf.po.User">
    UPDATE user SET username = #{username},birthday=#{birthday},sex=#{sex},address=#{address} WHERE id = #{id}
</update>
</mapper>

UserMapper.java

/**
* @Description: 用戶管理相關Mapper接口
 */
public interface UserMapper {

    /**
     * 根據本id查詢用戶
     */
    User findUserById(int id) throws Exception;
    
    /**
     * 根據用戶名模糊查詢用戶
     */
    List<User> findUsersByName(String name) throws Exception;
    
    /**
     * 添加用戶
     */
    void insertUser(User user) throws Exception;
    
    /**
     * 根據id刪除用戶
     */
    void deleteUser(int id) throws Exception;
}

完成前面2步以後不要忘了在映射文件SqlMapConfig.xml中加載UserMapper.xml哦!

<!-- 配置映射文件 -->
<mappers>
    <mapper resource="sqlmap/User.xml"/>
    <mapper resource="mapper/UserMapper.xml"/>
</mappers>

針對UserMapper接口編寫Junit單元測試:

public class UserMapperTest {

    private SqlSessionFactory sqlSessionFactory;
    
    @Before
    public void setUp() throws Exception {
        
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("SqlMapConfig.xml"));
    }

    @Test
    public void testFindUserById() throws Exception {
        
        // 獲得SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 獲得UserMapper對象(Mybatis自動生成Mapper代理對象)
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        // 調用UserMapper的方法
        User user = userMapper.findUserById(1);
        System.out.println(user);
        // 關閉SqlSession
        sqlSession.close();
    }

    @Test
    public void testFindUsersByName() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = userMapper.findUsersByName("明");
        System.out.println(users);
        sqlSession.close();
    }

    @Test
    public void testInsertUser() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setAddress("晴川");
        user.setUsername("百里屠蘇");
        user.setBirthday(new Date());
        user.setSex("1");
        userMapper.insertUser(user);
        sqlSession.commit();
        sqlSession.close();
        System.out.println(user.getId());
    }

    @Test
    public void testDeleteUser() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        userMapper.deleteUser(28);
        sqlSession.commit();
        sqlSession.close();
    }

}
  • 咱們比較疑問的就是咱們在UserMapper.xml的根據用戶名查找用戶的ResultType使用的是普通的POJO,可是咱們本身的Mapper接口中返回值是List類型。這不就形成了類型不一致麼?可是,實際上代理對象內部調用了selectOne()或者selectList(),代理對象內部會自動進行判斷是不是單獨的POJO選用合適的方法。

  • Mapper接口中方法的參數只有一個是否會影響系統的維護?DAO層的代碼是被業務層公用的,即便Mapper接口的參數只有一個咱們也可使用包裝的POJO來知足系統需求。

  • 注意:持久層中方法的參數中可使用包裝類型,可是Service層中不建議使用包裝類型(不利於業務層的拓展維護)。

SqlMapConfig.xml文件

屬性配置

該配置文件是mybatis的全局配置文件,配置內容以下:

properties(屬性)
settings(全局配置參數)
typeAliases(類型別名)
typeHandlers(類型處理器)
objectFactory(對象工廠)
plugins(插件)
environments(環境集合屬性對象)
environment(環境子屬性對象)
transactionManager(事務管理)
dataSource(數據源)
mappers(映射器)

將數據庫鏈接參數單獨配置在db.properties中,只須要在SqlMapConfig.xml中加載db.properties的屬性值。
在SqlMapConfig.xml中就不須要對數據庫鏈接參數硬編碼。

將數據庫鏈接參數只配置在db.properties中,緣由:方便對參數進行統一管理,其它xml能夠引用該db.properties。

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=mysqladmin

在SqlMapConfig中加載屬性文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <!-- 加載屬性文件 -->
    <properties resource="db.properties">
        <!-- 通常不建議在這裏配置屬性
        <property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
         -->
    </properties>
    <!-- 與Spring整合後該配置將會被移除 -->
    <environments default="development">
        <environment id="development">
            <!-- 使用JDBC事務進行管理,事務的控制由mybatis管理 -->
            <transactionManager type="JDBC">
                <property name="" value="" />
            </transactionManager>
             <!-- 從屬性文件中取出數據庫的配置信息 -->
             <dataSource type="UNPOOLED">
                <property name="driver" value="${jdbc.driver}" />
                <property name="url" value="${jdbc.url}" />
                <property name="username" value="${jdbc.username}" />
                <property name="password" value="${jdbc.password}" />
            </dataSource>
        </environment>
    </environments>
</configuration>

properties特性:

注意: MyBatis 將按照下面的順序來加載屬性:

  • 在 properties 元素體內定義的屬性首先被讀取。

  • 而後會讀取properties 元素中resource或 url 加載的屬性,它會覆蓋已讀取的同名屬性。

  • 最後讀取parameterType傳遞的屬性,它會覆蓋已讀取的同名屬性。

建議:
不要在properties元素體內添加任何屬性值,只將屬性值定義在properties文件中。在properties文件中定義屬性名要有必定的特殊性(防止覆蓋),如:XXXXX.XXXXX.XXXX

 settings

settings配置全局參數,mybatis在運行時能夠調整一些運行參數。例如:開啓二級緩存、開啓延遲加載。參數詳見:Mybatis settings詳解

typeAliases

在mapper.xml中,定義不少的statement,statement須要parameterType指定輸入參數的類型、須要resultType指定輸出結果的映射類型。

若是在指定類型時輸入類型全路徑,不方便進行開發,能夠針對parameterType或resultType指定的類型定義一些別名,在mapper.xml中經過別名定義,方便開發。

mybatis內置的別名

mybatis內置的別名

自定義別名

在SqlMapConfig.xml中配置別名:

<!-- 別名定義 -->
<typeAliases>
    <!-- 針對單個別名定義
    type:類型的路徑
    alias:別名
     -->
    <typeAlias type="org.gpf.po.User" alias="user"/>
</typeAliases>

在mapperr.xml中使用別名替代ResultType:

<!-- 根據id查詢用戶
    此處的resultType指定爲SqlMapConfig.xml中配置的別名
 -->
<select id="findUserById" parameterType="int" resultType="user">
    SELECT * FROM user WHERE id = #{id};
</select>

以上的方法是定義簡單的單個別名,咱們也可使用批量別名定義:

<!-- 別名定義 -->
<typeAliases>
    <!-- 批量別名定義 
    指定包名,mybatis自動掃描包中的po類,自動定義別名,別名就是類名(首字母大寫或小寫均可以)
    -->
    <package name="org.gpf.po"/>
</typeAliases>

在mapper.xml中咱們可使用以上的別名,此時是大小寫不敏感的:

<!-- 根據id查詢用戶
    此處的resultType指定爲SqlMapConfig.xml中配置的別名
 -->
<select id="findUserById" parameterType="int" resultType="User">
    SELECT * FROM user WHERE id = #{id};
</select>

其中批量設置別名比較經常使用(只須要配置po所在的類路徑便可)。

TypeHandlers

類型處理器,mybatis經過typeHandlers完成JDBC類型和java類型的轉化。通常來講自帶的類型處理器已經夠用了,不須要單獨定義。

mybatis類型處理器g

mapper

映射配置。

  • 經過resource一次加載一個映射文件:

resource加載映射文件

  • 經過mapper接口加載單個映射文件,須要遵循相關的規範:將mapper.xml和mapper接口的名稱保持一致且在一個目錄中(前提是使用mapper代理的方式進行開發)。

經過mapper接口加載映射文件1
經過mapper接口加載映射文件2

  • 批量加載mapper。一樣須要遵照上面的規範。
    批量加載mapper

輸入映射

經過parameterType來指定輸入參數的類型,能夠是簡單類型、hashmap、pojo。

傳遞POJO的包裝對象

需求:完成用戶信息的綜合查詢,須要傳入查詢條件很複雜(可能包括用戶信息、其它信息,好比商品、訂單的)

針對上邊需求,建議使用自定義的包裝類型的pojo。在包裝類型的pojo中將複雜的查詢條件包裝進去。

  • 新建一個UserCustomer類(該類繼承User,實現對Us而的拓展)

/**
* @ClassName: UserCustom 
* @Description: 用戶拓展類
 */
public class UserCustom extends User {

}
  • 新建一個UserQueryVO(在該類內部以組合的方式包裝UserCustomer類)

/**
* @ClassName: UserQueryVO 
* @Description: 視圖層面javabean(用來包裝查詢條件)
 */
public class UserQueryVO {

    // 包裝所須要的查詢條件(能夠經過組合的方式包裝其餘的查詢條件,例如:商品、訂單)
    private UserCustom userCustom;

    public UserCustom getUserCustom() {
        return userCustom;
    }

    public void setUserCustom(UserCustom userCustom) {
        this.userCustom = userCustom;
    }
    
}
  • mapper.xml。在UserMapper.xml中定義用戶信息的綜合(複雜的關聯查詢)查詢。

<!-- 用戶信息的綜合查詢
     #{UserCustom.sex}    取出POJO包裝類型中用戶的性別(注意大小寫:OGNL表達式)
 -->
<select id="findUserList" parameterType="org.gpf.po.UserQueryVO" resultType="org.gpf.po.UserCustom">
    SELECT * FROM user WHERE user.sex = #{userCustom.sex} OR user.username LIKE '%${userCustom.username}%'
</select>
  • mapper.java。在UserMapper接口中增長一個方法。

/**
 * 用戶信息的綜合查詢
 */
List<UserCustom> findUserList(UserQueryVO userQueryVO) throws Exception;
  • 測試類

@Test
public void testfindUserList() throws Exception {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
    // 建立包裝對象,傳入查詢條件
    UserQueryVO userQueryVO = new UserQueryVO();
    UserCustom userCustom = new UserCustom();
    userCustom.setSex("1");
    userCustom.setUsername("明");
    userQueryVO.setUserCustom(userCustom);
    List<UserCustom> users = userMapper.findUserList(userQueryVO);
    System.out.println(users);
}

輸出映射

ResultType

使用ResultType進行輸出映射只有當查詢輸出列名和POJO中的字段名一致時,該列才能夠映射成功;若是查詢出來的列名和屬性名所有不一致(例如使用了SQL的別名);一旦有一個查詢的輸出列名和POJO的屬性名一致就會映射成對象。

需求:用戶信息的綜合查詢列表總數,並結合上邊的綜合查詢列表實現分頁功能。

mapper.xml

<!-- 用戶信息的綜合查詢總數(分頁的基礎)
    COUNT(*) 和COUNT(1)、COUNT(2)同樣
 -->
<select id="findUserCount" parameterType="org.gpf.po.UserQueryVO" resultType="int">
    SELECT COUNT(1) FROM user WHERE user.sex = #{userCustom.sex} OR user.username LIKE '%${userCustom.username}%'
</select>

mapper.java

/**
 * 用戶信息的綜合查詢總數
 */
int findUserCount(UserQueryVO userQueryVO) throws Exception;

測試類:

@Test
public void testFindUserCount() throws Exception{
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
    // 建立包裝對象,傳入查詢條件
    UserQueryVO userQueryVO = new UserQueryVO();
    UserCustom userCustom = new UserCustom();
    userCustom.setSex("1");
    userCustom.setUsername("明");
    userQueryVO.setUserCustom(userCustom);
    int count = userMapper.findUserCount(userQueryVO);
    System.out.println(count);
}

小結:

  • 查詢出來的結果集只有一行且一列,可使用簡單類型進行輸出映射。

  • 輸出POJO對象和POJO列表。

    • 不論是輸出的pojo單個對象仍是一個列表(list中包括pojo),在mapper.xml中resultType指定的類型是同樣的。
      在mapper.java指定的方法返回值類型不同

mapper接口中方法的區別

生成的動態代理對象中是根據mapper方法的返回值類型肯定是調用selectOne(返回單個對象調用)仍是selectList (返回集合對象調用 ).

ResultMap

可使用ResultMap來完成高級的輸出結果映射。若是數據庫輸出的列名和POJO的屬性名不一致,就能夠經過定義ResultMap對列名和屬性名進行咱們本身的映射。

範例:使用ResultMap改寫如下的SQL,完成輸出結果的映射;

SELECT id _id,username _username FROM user WHERE id = #{value}

UserMapper.xml

<!-- 定義ResultMap 
    將SELECT id _id,username _username FROM user和User類中的屬性進行映射
    type:ResultType最終映射的java對象的類型(可以使用別名)
    id:ResultMap的惟一標識
-->
<resultMap type="org.gpf.po.User" id="userResultMap">
    <!-- id:查詢結果集中的惟一標識,column:查詢出的列名,property:POJO的屬性名,最終column和property具備映射關係 -->
    <id column="_id" property="id" />
    <!-- 普通名映射定義 -->
    <result column="_username" property="username"/>
</resultMap>
<!-- 使用ResultMap進行輸出的映射
    resultMap:指定定義的ResultMap的id,若是ResultMap在其餘文件中須要指定命名空間
 -->
<select id="findUserByIdResultMap" parameterType="int" resultMap="userResultMap">
    SELECT id _id,username _username FROM user WHERE id = #{value}
</select>

UserMapper.java

/**
 * 根據id查詢用戶(經過ResultMap手工完成映射)
 */
User findUserByIdResultMap(int id) throws Exception;

測試類:

@Test
public void testFindUserByIdResultMap() throws Exception{
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    User user = userMapper.findUserByIdResultMap(1);
    System.out.println(user);
    sqlSession.close();
}

小結:

  • 使用resultType進行輸出映射,只有查詢出來的列名和pojo中的屬性名一致,該列才能夠映射成功。

  • 若是查詢出來的列名和pojo的屬性名不一致,經過定義一個resultMap對列名和pojo屬性名之間做一個映射關係。

動態SQL

mybatis核心對sql語句進行靈活操做,經過表達式進行判斷,對sql進行靈活拼接、組裝。

if判斷

需求:用戶信息綜合查詢列表和用戶信息查詢列表總數這兩個statement的定義使用動態sql。對查詢條件進行判斷,若是輸入參數不爲空才進行查詢條件拼接。

if判斷相似於JSTL.

mapper.xml

<!-- 動態SQL -->
<select id="findUserList" parameterType="org.gpf.po.UserQueryVO" resultType="org.gpf.po.UserCustom">
    SELECT * FROM user
    <!-- where能夠自動去掉條件中的第一個AND -->
    <where>
        <if test="userCustom != null">
            <if test="userCustom.sex !=null and userCustom.sex!=''">
                AND user.sex = #{userCustom.sex}
            </if>
            <if test="userCustom.username != null and userCustom.username != ''">
                AND user.username LIKE '%${userCustom.username}%'
            </if>
        </if>
    </where>
</select>
<select id="findUserCount" parameterType="org.gpf.po.UserQueryVO" resultType="int">
    SELECT COUNT(1) FROM user
    <where>
        <if test="userCustom != null">
            <if test="userCustom.sex != null and userCustom.sex != ''">
                AND user.sex = #{userCustom.sex}
            </if>
            <if test="userCustom.username != null and userCustom.username != ''">
                AND user.username LIKE '%${userCustom.username}%'
            </if>
        </if>
    </where>
</select>

maper.java

/**
 * 用戶信息的綜合查詢
 */
List<UserCustom> findUserList(UserQueryVO userQueryVO) throws Exception;

/**
 * 用戶信息的綜合查詢總數
 */
int findUserCount(UserQueryVO userQueryVO) throws Exception;

測試類

@Test
public void testfindUserList() throws Exception {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
    // 取消部分查詢條件
    UserQueryVO userQueryVO = new UserQueryVO();
    UserCustom userCustom = new UserCustom();
//        userCustom.setSex("1");
    userCustom.setUsername("明");
    userQueryVO.setUserCustom(userCustom);
    List<UserCustom> users = userMapper.findUserList(userQueryVO);
    System.out.println(users);
}

@Test
public void testFindUserCount() throws Exception{
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
    // 取消部分查詢條件
    UserQueryVO userQueryVO = new UserQueryVO();
    UserCustom userCustom = new UserCustom();
    userCustom.setSex("1");
//        userCustom.setUsername("明");
    userQueryVO.setUserCustom(userCustom);
    int count = userMapper.findUserCount(userQueryVO);
    System.out.println(count);
}

sql片斷

以上的代碼中mapper.xml中的SQL語句有一些重複,咱們能夠將上面的動態SQL中共有的部分抽取出來造成本身的sql片斷,在其餘的Statement中就可使用此SQL片斷。

  • 定義並使用SQL片斷

<!-- 定義SQL片斷 
    id:SQL片斷的惟一標識
    
    經驗:SQL片斷通常是基於單表的,這樣的話SQL片斷的可重用性高
        在SQL片斷中不要包含where
-->
<sql id="query_user_where">
    <if test="userCustom != null">
        <if test="userCustom.sex != null and userCustom.sex != ''">
            AND user.sex = #{userCustom.sex}
        </if>
        <if test="userCustom.username != null and userCustom.username != ''">
            AND user.username LIKE '%${userCustom.username}%'
        </if>
    </if>
</sql>
<select id="findUserCount" parameterType="org.gpf.po.UserQueryVO" resultType="int">
    SELECT COUNT(1) FROM user
    <where>
        <!-- 使用SQL片斷,若是不在本文件中須要加上命名空間 --> 
        <include refid="query_user_where"></include>
    </where>
</select>

for-each

向sql傳遞數組或List,mybatis使用foreach解析。

例如:在用戶查詢列表和查詢總數的statement中增長多個id輸入查詢。
兩種方法:

SELECT * FROM USER WHERE id=1 OR id=10 OR id=16

SELECT * FROM USER WHERE id IN(1,10,16)
  • 在輸入參數類型中添加List<Integer> ids傳入多個id(並添加getter和setter)

查詢條件

  • 修改mapper.xml中的SQL片斷

<!-- 定義SQL片斷 
    id:SQL片斷的惟一標識
    
    經驗:SQL片斷通常是基於單表的,這樣的話SQL片斷的可重用性高
        在SQL片斷中不要包含where
-->
<sql id="query_user_where">
    <if test="userCustom != null">
        <if test="userCustom.sex != null and userCustom.sex != ''">
            AND user.sex = #{userCustom.sex}
        </if>
        <if test="userCustom.username != null and userCustom.username != ''">
            AND user.username LIKE '%${userCustom.username}%'
        </if>
    </if>
    <if test="ids != null">
        <!-- 使用for-each遍歷傳入的ids
            collection:指定輸入 對象中集合屬性
            item:每一個遍歷生成對象中
            open:開始遍歷時拼接的串
            close:結束遍歷時拼接的串
            separator:遍歷的兩個對象中須要拼接的串        
         -->
         <!-- 使用實現下邊的sql拼接:
              AND (id=1 OR id=10 OR id=16) 
          -->
        <foreach collection="ids" item="user_id" open="AND (" close=")" separator="OR">
            <!-- 每一個遍歷須要拼接的串 -->
            id=#{user_id}
        </foreach>
    </if>
</sql>

測試類:

@Test
public void testfindUserList() throws Exception {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
    UserQueryVO userQueryVO = new UserQueryVO();
    UserCustom userCustom = new UserCustom();
    userCustom.setUsername("明");
    
    // 傳入多個id
    List<Integer> ids = Arrays.asList(1,16,22,25);
    userQueryVO.setId(ids);
    userQueryVO.setUserCustom(userCustom);
    List<UserCustom> users = userMapper.findUserList(userQueryVO);
    System.out.println(users);
}
<!-- 實現  and id IN(1,10,16)拼接 -->
<foreach collection="ids" item="user_id" open="and id IN(" close=")" separator=",">
        每一個遍歷須要拼接的串
        #{user_id}
</foreach>
相關文章
相關標籤/搜索