【測試開發】知識點-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
中,使用insert
、update
、delete
標籤來寫對應方法的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)
新增成功。
其他幾個也成功通過測試。
二、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>
可以在之前的測試方法里加一個列印,看下獲取到的主鍵值:
成功獲取到主鍵值為 12 。
檢視資料庫表裡新增多資料主鍵就是 12 。
三、單個引數、多個引數、物件
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 也是可以的。
--不要用肉體的勤奮,去掩蓋思考的懶惰--