MyBatis框架的學習(四)
前面對MyBatis框架的學習中,我們對Mapper.xml對映檔案多少有些瞭解。本文將對Mapper.xml對映檔案作更加細緻的梳理,首先從Mapper.xml檔案中的輸入和輸出對映開始。本文案例程式碼的編寫是建立在前文MyBatis框架的學習(三)案例基礎之上的!
輸入對映和輸出對映
Mapper.xml對映檔案中定義了操作資料庫的sql,每個sql是一個statement,對映檔案是mybatis的核心。
parameterType(輸入型別)
傳遞簡單型別
傳遞簡單型別,我之前已講過,這裡只給出案例,如下:
傳遞pojo物件
MyBatis使用ognl表示式解析物件欄位的值,#{}或者${}括號中的值為pojo屬性名稱。傳遞pojo物件之前也已講過,這裡同樣只給出案例,如下:
傳遞pojo包裝物件
開發中通過pojo傳遞查詢條件,查詢條件是綜合的查詢條件,不僅包括使用者查詢條件還包括其它的查詢條件(比如將使用者購買商品資訊也作為查詢條件),這時可以使用包裝物件傳遞輸入引數。即pojo類中包含pojo類。 例如這樣一個需求:根據使用者id查詢使用者資訊,查詢條件放到QueryVo類的user屬性中。有需求,就要解決它。我們可在cn.itheima.mybatis.po包下新建一個QueryVo類,如下:
public class QueryVo {
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
接下來我們就要在UserMapper.xml對映檔案中編寫sql語句了,即在UserMapper.xml對映檔案中新增如下配置資訊:
<select id="getUserByQueryVo" parameterType="queryvo" resultType="user">
select * from user where id=#{user.id};
</select>
使用包裝型別查詢使用者時,可使用ognl從物件中取屬性值,並且如果是包裝物件可以使用.
User getUserByQueryVo(QueryVo queryVo);
- 1
最後在UserMapperTest單元測試類編寫如下測試方法:
@Test
public void testGetUserByQueryVo() {
SqlSession sqlSession = sqlSessionFactory.openSession();
// 獲得mapper代理物件
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 建立一個QueryVo物件
QueryVo queryVo = new QueryVo();
User user = new User();
user.setId(10);
queryVo.setUser(user);
// 執行查詢
User result = userMapper.getUserByQueryVo(queryVo);
System.out.println(result);
sqlSession.close();
}
傳遞HashMap
傳遞HashMap在實際開發中用的很少,但我還是要講一下。以例明示——傳遞HashMap綜合查詢使用者資訊,在UserMapper.xml對映檔案中新增如下配置資訊:
<!-- 傳遞HashMap綜合查詢使用者資訊 -->
<select id="findUserByHashmap" parameterType="hashmap" resultType="user">
select * from user where id=#{id} and username like '%${username}%'
</select>
上面的id和username是HashMap的key。 接著在UserMapper介面中新增如下方法:
User findUserByHashmap(HashMap<String, Object> map);
- 1
最後在UserMapperTest單元測試類編寫如下測試方法:
@Test
public void testFindUserByHashmap() {
SqlSession sqlSession = sqlSessionFactory.openSession();
// 獲得mapper代理物件
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 構造查詢條件Hashmap物件
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("id", 30);
map.put("username", "趙雲");
// 執行查詢
User result = userMapper.findUserByHashmap(map);
System.out.println(result);
sqlSession.close();
}
resultType(輸出型別)
輸出簡單型別
有這樣一個需求:查詢使用者表中的記錄數。有需求就要解決它,我們首先在UserMapper.xml對映檔案中新增如下配置資訊:
<!-- 查詢使用者表中的記錄數 -->
<select id="getUserCount" resultType="int">
SELECT COUNT(*) FROM `user`
</select>
接著在UserMapper介面中新增如下方法:
Integer getUserCount();
- 1
最後在UserMapperTest單元測試類編寫如下測試方法:
@Test
public void testGetUserCount() {
SqlSession sqlSession = sqlSessionFactory.openSession();
// 獲得mapper代理物件
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 執行查詢
int count = userMapper.getUserCount();
System.out.println(count);
sqlSession.close();
}
輸出簡單型別必須查詢出來的結果集只有一條記錄,最終將第一個欄位的值轉換為輸出型別。
輸出pojo物件
輸出pojo物件,我之前已講過,這裡只給出案例,如下:
輸出pojo列表
輸出pojo列表,我之前同樣已講過,這裡只給出案例,如下: 這兒再給出一個案例——查詢訂單的所有資訊。我們mybatis資料庫中已經有了訂單表(orders表)了,如下: 大家可能想到不會想,就會像下面這樣做。首先在cn.itheima.mybatis.po包下新建一個Orders類:
public class Orders {
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number == null ? null : number.trim();
}
public Date getCreatetime() {
return createtime;
}
public void setCreatetime(Date createtime) {
this.createtime = createtime;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note == null ? null : note.trim();
}
}
然後在cn.itheima.mybatis.mapper包下建立一個OrderMapper.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="cn.itheima.mybatis.mapper.OrderMapper">
<select id="getOrderList" resultType="orders">
select * from orders;
</select>
</mapper>
緊接著在cn.itheima.mybatis.mapper包下建立一個OrderMapper介面:
public interface OrderMapper {
List<Orders> getOrderList();
}
最後建立OrderMapper介面的單元測試類——OrderMapperTest.java,修改OrderMapperTest類的內容為:
public class OrderMapperTest {
private SqlSessionFactory sqlSessionFactory = null; // 工廠物件一般在我們的系統中是單例的
@Before
public void init() throws IOException {
// 第一步,建立SqlSessionFactoryBuilder物件
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 第二步,載入配置檔案
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 第三步,建立SqlSessionFactory物件
sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
}
@Test
public void testGetOrderList() {
SqlSession sqlSession = sqlSessionFactory.openSession();
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
List<Orders> orderList = orderMapper.getOrderList();
for (Orders orders : orderList) {
System.out.println(orders);
}
sqlSession.close();
}
}
在testGetOrderList方法中的以下程式碼處打一個斷點:
for (Orders orders : orderList) {
- 1
然後以Debug模式執行該方法,可發現查詢出來的每一個Orders物件中的userId屬性值都為null,如下: 很明顯這並不是我們所想要的結果。為了達到我們預期的效果,可為user_id列加別名,即將OrderMapper.xml對映檔案中id為getOrderList的select元素修改為:
<select id="getOrderList" resultType="orders">
select id,user_id userId,number,createtime,note from orders;
</select>
只要你返回的結果的列名和pojo中的屬性一致,就可以自動映射了。 這樣當我們再次以Debug模式執行testGetOrderList方法,就能達到我們預期的結果了,如下: 這種方式比較簡單粗暴,其實要達到我們所預期的效果,還有另一種方式,那就是使用resultMap這個屬性,下面我就會講到。
resultMap
resultMap可以指定pojo將查詢結果對映為pojo,但需要pojo的屬性名和sql查詢的列名一致方可對映成功。如果sql查詢欄位名和pojo的屬性名不一致,可以通過resultMap將欄位名和屬性名作一個對應關係 ,resultMap實質上還需要將查詢結果對映到pojo物件中。
resultMap可以實現將查詢結果對映為複雜型別的pojo,比如在查詢結果對映物件中包括pojo和list實現一對一查詢和一對多查詢。
現在我就來實現查詢訂單所有資訊的需求,而不是像上面那樣簡單粗暴地給user_id列加別名。首先在OrderMapper.xml對映檔案中新增如下<select>
元素:
<select id="getOrderListResultMap" resultMap="order_list_result_map">
select id,user_id,number,createtime,note from orders;
</select>
使用resultMap指定上邊定義的order_list_result_map。 接著定義resultMap。由於上邊的OrderMapper.xml對映檔案中sql查詢列和Orders.java類屬性不一致,因此需要定義resultMap:order_list_result_map將sql查詢列和Orders.java類屬性對應起來。
<resultMap type="orders" id="order_list_result_map">
<!-- id是主鍵的對映,其中property是pojo中主鍵的屬性,column是返回結果中主鍵的列 -->
<id property="id" column="id" />
<!-- 普通列使用result對映 -->
<result property="userId" column="user_id" />
<result property="number" column="number" />
<result property="createtime" column="createtime" />
<result property="note" column="note" />
</resultMap>
- type:指resultMap要對映成的資料型別(返回結果對映的pojo,可以使用別名)。
<id />
:此屬性表示查詢結果集的唯一標識,非常重要。如果是多個欄位為複合唯一約束則定義多個<id />
。- property:表示Orders類的屬性。
- column:表示sql查詢出來的欄位名。 column和property放在一塊兒表示將sql查詢出來的欄位對映到指定的pojo類屬性上。
<result />
:普通列使用result標籤對映。
然後在OrderMapper介面新增如下方法:
List<Orders> getOrderListResultMap();
- 1
最後在OrderMapperTest單元測試類中新增如下測試方法:
@Test
public void testGetOrderListResultMap() {
SqlSession sqlSession = sqlSessionFactory.openSession();
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
List<Orders> orderList = orderMapper.getOrderListResultMap();
for (Orders orders : orderList) {
System.out.println(orders);
}
sqlSession.close();
}
同樣在testGetOrderListResultMap方法中的以下程式碼處打一個斷點:
for (Orders orders : orderList) {
- 1
然後以Debug模式執行該方法,可發現查詢出來的每一個Orders物件中的userId屬性都有值了。
動態sql
我們可通過mybatis提供的各種標籤方法實現動態拼接sql。
if
現有這樣一個需求:傳遞pojo類綜合查詢使用者資訊,更具體地說就是我們使用使用者的id和username能更加靈活地查詢使用者資訊。
為了解決這個需求,我們就要使用<if>
標籤了。首先在UserMapper.xml對映檔案中新增如下<select>
元素:
<select id="findUserList" parameterType="user" resultType="user">
select * from user
where 1=1
<if test="id!=null">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</select>
注意:
- username要做不等於空字串的校驗。
- User類中id屬性的型別要改為Integer包裝型別,因為int型別的id是不可能為null的!
然後在UserMapper介面新增如下方法:
List<User> findUserList(User user);
- 1
最後在UserMapperTest單元測試類中新增如下測試方法:
@Test
public void testFindUserList() {
SqlSession sqlSession = sqlSessionFactory.openSession();
// 獲得mapper代理物件
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 設定查詢條件
User user = new User();
// user.setId(10);
user.setUsername("張");
// 執行查詢
List<User> userList = userMapper.findUserList(user);
for (User user2 : userList) {
System.out.println(user2);
}
sqlSession.close();
}
讀者可試著給User物件只為id屬性賦值,或者只為username屬性賦值,又或者兩者同時賦值。
where
UserMapper.xml對映檔案中如下<select>
元素:
<select id="findUserList" parameterType="user" resultType="user">
select * from user
where 1=1
<if test="id!=null">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like '%${username}%'
</if>
</select>
也可使用<where>
標籤寫為:
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<if test="id!=null">
and id=#{id}
</if>
<if test="username != null and username != ''">
and username like '%${username}%'
</if>
</where>
</select>
<where />
可以自動處理第一個and。
foreach
現有這樣一個需求:傳入多個id查詢使用者資訊。如若編寫sql語句,可用下邊兩個sql實現:
SELECT * FROM USER WHERE username LIKE '%張%' AND (id =10 OR id =89 OR id=16)
SELECT * FROM USER WHERE username LIKE '%張%' id IN (10,89,16)
為了解決這個需求,首先在QueryVo類中定義List屬性ids儲存多個使用者id,並新增getter/setter方法,如下:
public class QueryVo {
private User user;
private List<Integer> ids;
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
然後在UserMapper.xml對映檔案中新增如下<select>
元素:
<!-- 動態sql foreach測試 -->
<select id="findUserByIds" parameterType="queryvo" resultType="user">
SELECT * FROM `user`
<where>
<!-- and id IN(1,10,20,21,31) -->
<foreach collection="ids" item="id" open="and id in(" close=")" separator=",">
#{id}
</foreach>
</where>
</select>
向sql中傳遞陣列或List,mybatis將使用foreach解析。 接著在UserMapper介面新增如下方法:
List<User> findUserByIds(QueryVo queryVo);
- 1
最後在UserMapperTest單元測試類中新增如下測試方法:
@Test
public void testFindUserByIds() {
SqlSession sqlSession = sqlSessionFactory.openSession();
// 獲得mapper代理物件
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 設定查詢條件
QueryVo queryVo = new QueryVo();
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(10);
ids.add(16);
ids.add(22);
queryVo.setIds(ids);
// 執行查詢
List<User> userList = userMapper.findUserByIds(queryVo);
for (User user2 : userList) {
System.out.println(user2);
}
sqlSession.close();
}
sql片段
sql中可將重複的sql提取出來,使用時用include引用即可,最終達到sql重用的目的,如下:
<select id="findUserList" parameterType="user" resultType="user">
select * from user
<where>
<if test="id!=null">
and id=#{id}
</if>
<if test="username != null and username != ''">
and username like '%${username}%'
</if>
</where>
</select>
將where條件抽取出來,同時我們也可將要查詢的欄位抽取出來。
<sql id="find_user_list_where">
<where>
<if test="id!=null">
and id=#{id}
</if>
<if test="username != null and username != ''">
and username like '%${username}%'
</if>
</where>
</sql>
<sql id="user_field_list">
id,username,birthday,sex,address
</sql>
使用include引用:
<select id="findUserList" parameterType="user" resultType="user">
select <include refid="user_field_list"/> from user
<include refid="find_user_list_where"/>
</select>
注意:如果引用其它mapper.xml對映檔案的sql片段,則在引用時需要加上namespace,如下:
<include refid="namespace.sql片段"/>
- 1
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-778f64ae39.css" rel="stylesheet">
</div>