【測試開發】知識點-mybatis,XML 映射文件介紹

MyBatis 的真正強大在於它的語句映射,它指導着 Mybatis 如何進行數據庫的增刪改查。在之前的demo當中已簡單使用過,寫sql的那個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">
<mapper namespace="com.pingguo.bloomtest.dao.UserMapper">
    <select id="getUserById" resultType="com.pingguo.bloomtest.pojo.User" databaseId="mysql">
      select * from user where id = #{id}
    </select>
</mapper>

SQL 映射文件只有很少的幾個頂級元素(按照應被定義的順序列出):

  • cache – 該命名空間的緩存配置。
  • cache-ref – 引用其它命名空間的緩存配置。
  • resultMap – 描述如何從數據庫結果集中加載對象,是最複雜也是最強大的元素。
  • parameterMap – 老式風格的參數映射。此元素已被廢棄,並可能在將來被移除!請使用行內參數映射。文檔中不會介紹此元素。
  • sql – 可被其它語句引用的可重用語句塊。
  • insert – 映射插入語句。
  • update – 映射更新語句。
  • delete – 映射刪除語句。
  • select – 映射查詢語句。

不過,先來看下增刪改查。

一、增刪改查速覽

我繼續在接口裏新增幾個方法(查詢已有):

public interface UserMapper {
    // 查詢
    User getUserById(Integer id);
    // 新增
    void addUser(User user);
    // 修改
    void updateUser(User user);
    // 刪除
    void deleteUser(Long id);
}

在映射文件UserMapper.xml中,使用insertupdatedelete標籤來寫對應方法的sql:

<!--新增-->
<insert id="addUser">
    insert into user(username, password, createTime, updateTime)
    values(#{username}, #{password}, #{createTime}, #{updateTime})
</insert>

<!--更新-->
<update id="updateUser">
    update user
      set username=#{username}, password=#{password}, createTime=#{createTime}, updateTime=#{updateTime}
      where id=#{id}
</update>

<!--刪除-->
<delete id="deleteUser">
    delete from user where id=#{id}
</delete>

另外,mybatis 允許增刪改直接定義如下的返回值:Integer、Long、Boolean。那麼接口裏定義的方法就可以寫成這樣:

public interface UserMapper {
    // 查詢
    User getUserById(Integer id);
    // 新增
    boolean addUser(User user);
    // 修改
    boolean updateUser(User user);
    // 刪除
    boolean deleteUser(Long id);
}

addUser方法來說,新增成功之後返回的就是true

在測試類裏可以測試下上面幾個方法,這裏貼出來測試一下新增:

@Test
void test3() throws IOException{
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    // 獲取到的 SqlSession 不會自動提交數據
    SqlSession session = sqlSessionFactory.openSession();
    User newUser = new User();
    newUser.setUsername("新用戶1");
    newUser.setPassword("111111");
    newUser.setCreateTime(new Date());
    newUser.setUpdateTime(new Date());
    try {
        UserMapper userMapper = session.getMapper(UserMapper.class);
        userMapper.addUser(newUser);
        // 需要這裏手動提交
        session.commit();
    } finally {
        session.close();
    }
}

注意這裏sqlSessionFactory.openSession()不會自動提交數據,需要session.commit()手動提交。如果需要自動提交,裏面傳入true即可:

sqlSessionFactory.openSession(true)

image.png

新增成功。

image.png

其他幾個也成功通過測試。

二、insert獲取自增主鍵的值

mysql 的自增主鍵,mybatis 也可以獲取到,只需要添加一個屬性useGeneratedKeys,默認是false

那獲取到的主鍵值,可以通過keyProperty將這個值封裝給 Javabean 的某個屬性,比如User類中的id

<!--新增-->
<insert id="addUser" useGeneratedKeys="true" keyProperty="id">
    insert into user(username, password, createTime, updateTime)
    values(#{username}, #{password}, #{createTime}, #{updateTime})
</insert>

可以在之前的測試方法里加一個打印,看下獲取到的主鍵值:

image.png

成功獲取到主鍵值爲 12 。

image.png

查看數據庫表裏新增多數據主鍵就是 12 。

image.png

三、單個參數、多個參數、對象

1. 單個參數

拿上面根據 id 進行查詢爲例:

<select id="getUserById" resultType="com.pingguo.bloomtest.pojo.User" databaseId="mysql">
  select * from user where id = #{id}
</select>

使用#{參數名},mybatis就會正確取出參數值。

當只有一個參數的時候,mybatis 不會做特殊處理,比如sql中的where條件是根據id來查詢,但我就算寫#{abc},也正常查詢。

2. 多個參數

在接口UserMapper裏再定義一個新的方法,裏面傳入多個參數:

// 查詢 使用多個參數
User getUserByIdAndUsername(Integer id, String username);

對應的增加sql映射:

<select id="getUserByIdAndUsername" resultType="com.pingguo.bloomtest.pojo.User" databaseId="mysql">
  select * from user where id = #{id} and username=#{username}
</select>

測試一下,可以正常查詢。

@Test
void test2params() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    SqlSession session = sqlSessionFactory.openSession();
    UserMapper userMapper = session.getMapper(UserMapper.class);
    System.out.println(userMapper.getUserByIdAndUsername(3, "大周"));
}

3. 對象

如果多個參數正好是我們業務邏輯的數據模型,那可以直接傳入對象,比如POJO,通過#{屬性名}取出屬性值。

如果多個參數沒有對應的POJO,爲了方便,也可以傳入 Map 。

在 UserMapper 裏增加方法:

    User getUserByMap(Map<String, Object> map);

sql 映射文件增加對應配置:

    <select id="getUserByMap" resultType="com.pingguo.bloomtest.pojo.User" databaseId="mysql">
      select * from user where id = #{id} and username=#{username}
    </select>

新增個測試方法,查詢正常。

    @Test
    void testByMap() throws IOException {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        SqlSession session = sqlSessionFactory.openSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);

        Map<String, Object> map = new HashMap<>();
        map.put("id", 3);
        map.put("username", "大周");
        System.out.println(userMapper.getUserByMap(map));
    }

如果上述這種 map 還要經常使用,推薦編寫一個TO(Transfer Object)數據傳輸對象。

四、$ 和 # 取值

mybatis 中是採用#{}${}都是可以取值的,但是兩者還是有區別的,比如:

    <select id="getUserByIdAndUsername" resultType="com.pingguo.bloomtest.pojo.User" databaseId="mysql">
      select * from user where id = ${id} and username=#{username}
    </select>

我在這裏把2個參數分別用了不同的方法來取值:${id}#{username},然後執行之前的測試函數,看下打印結果。

區別就是:

  • #{},是以預編譯形式,講參數設置到 sql 語句中
  • ${},取出的值直接拼裝在 sql 語句中,會存在一定的安全問題

大多數情況下,我們都去使用#{}

不過在某些情況下,還是會用到${}。比如一個工資表按照年份進行了拆分,表名前面是年份,那麼要動態查詢表的時候,年份就是個變量。

表名是不能預編譯的,所以不能使用#{}。對於這種原生JDBC不支持佔位符的地方,就可以使用${}

select * from ${year}_salary where xxx;

再比如,用到排序,我想把排序的條件和升序降序可以動態的穿進來,也可以使用${}

select * user order by ${age} ${desc_order};

五、select 元素

1. select 返回 list

如果我定義了一個查詢,返回的是一個 list。

    List<User> getUserByUsernameLike(String username);

sql 映射文件這樣寫:

    <select id="getUserByLastNameLike" resultType="com.pingguo.bloomtest.pojo.User" databaseId="mysql">
      select * from user where username like #{username}
    </select>

注意這裏如果返回的是一個集合類型,resultType裏要寫集合中的類型。

2. select 返回 map

如果需要返回一條記錄的 map,key 就是列名,value 就是對應的值。

Map<String, Object> getUserByIdReturnMap(Integer id);

sql 映射文件這樣寫:

    <select id="getUserByIdReturnMap" resultType="map">
        select * from user where id = #{id}
    </select>

測試下,查詢正常。

如果需要封裝多條記錄比如Map<Integer, User>,key 是記錄的主鍵,value 是記錄封裝後的 javabean 。

Map<Integer, User> getUserByUsernameReturnMap(String username);

sql 映射文件:

    <select id="getUserByUsernameReturnMap" resultType="com.pingguo.bloomtest.pojo.User">
        select * from user where username like #{username}
    </select>

這裏的返回類型還是 User,現在 value 有了,那麼如何把主鍵作爲 map 的 key 呢?

使用註解@MapKey("id"),告訴 mybatis 使用哪個屬性作爲 key:

    @MapKey("id")
    Map<Integer, User> getUserByUsernameReturnMap(String username);

測試一下:

如果換成其他屬性作爲 key 也是可以的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章