深入淺出Mybatis(八)高階對映
前面的章節使用的都是對單張表的操作,但是在實際開發當中,很多時候需要關聯多張表,這時就需要用到我們的高階對映,包括一對一,一對多和多對多。
下面先來分析一個簡單的業務場景。
有四張表:
使用者表:user儲存使用者相關資訊。
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) DEFAULT NULL COMMENT '使用者名稱', `birthday` date DEFAULT NULL COMMENT '出生年月', `sex` varchar(255) DEFAULT NULL COMMENT '性別', `addr` varchar(255) DEFAULT NULL COMMENT '地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
新增測試資料:
商品表:items儲存商品相關的資訊。
CREATE TABLE `items` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(32) NOT NULL, `price` decimal(10,2) NOT NULL, `detail` varchar(255) DEFAULT NULL, `pic` varchar(255) DEFAULT NULL, `createtime` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
新增測試資料:
訂單表:orders儲存訂單的資訊,關聯使用者表user,一個訂單對應一個user使用者。
CREATE TABLE `orders` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '訂單號', `user_id` int(11) NOT NULL, `number` int(32) NOT NULL, `createtime` datetime NOT NULL, `note` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `user_id` (`user_id`), CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
新增測試資料:
訂單明細表:orderdetail一個訂單當中包含多個商品。
CREATE TABLE `orderdetail` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`orders_id` int(11) NOT NULL,
`items_id` int(11) NOT NULL,
`items_num` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `orders_id` (`orders_id`),
KEY `items_id` (`items_id`),
CONSTRAINT `orderdetail_ibfk_1` FOREIGN KEY (`orders_id`) REFERENCES `orders` (`id`),
CONSTRAINT `orderdetail_ibfk_2` FOREIGN KEY (`items_id`) REFERENCES `items` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
新增測試資料:
第一個需求,查詢訂單並關聯使用者的資訊。
建立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;
}
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;
}
}
sql語句
select orders.*,user.username,user.sex,user.addr from orders,user WHERE orders.user_id=user.id
1:使用resultType作對映。
<!-- 查詢訂單關聯查詢使用者 -->
<select id="findOrdersUser" resultType="OrdersCustom">
select orders.*,user.username,user.sex,user.addr from orders,user WHERE orders.user_id=user.id
</select>
由於orders類中沒有使用者的相關資訊,所以這裡需要使用一個擴充套件類繼承orders,並新增使用者的屬性。/**
* @author:kevin
* @description: 訂單的擴充套件類
* 通過此類對映訂單和使用者查詢的結果,讓此類繼承包括欄位較多的pojo類
* @create 2018-03-31 9:40
*/
public class OrdersCustom extends Orders{
//新增屬性
private String username;
private String sex;
private String addr;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
}
在當前包下新建一個介面OrdersMapperCustom和xml OrdersMapperCustom.xml將xml中的namespace改成介面的路徑
com.beyond.mybatis.mapper.OrdersMapperCustom
<!-- 查詢訂單關聯查詢使用者 -->
<select id="findOrdersUser" resultType="OrdersCustom">
select orders.*,user.username,user.sex,user.addr from orders,user WHERE orders.user_id=user.id
</select>
在介面中新增對應的方法。
//查詢訂單關聯查詢使用者資訊
List<OrdersCustom> findOrdersUser() throws Exception;
測試: @Test
public void testfindOrdersUser() throws Exception{
SqlSession sqlSession = factory.openSession();
//通過反射拿到UserMapper的代理物件
OrdersMapperCustom ordersMapperCustom = sqlSession.getMapper(OrdersMapperCustom.class);
List<OrdersCustom> list = ordersMapperCustom.findOrdersUser();
System.out.println(list);
}
通過斷點除錯,檢視list中的值:查詢到了三條記錄:
2:使用resultMap進行對映
在orders類中新增一個屬性User。
<!-- 查詢訂單關聯查詢使用者,使用resultMap -->
<select id="findOrdersUserResultMap" resultMap="ordersUserResultMap">
select orders.*,user.username,user.sex,user.addr from orders,user WHERE orders.user_id=user.id
</select>
<!-- 訂單查詢關聯使用者的resultMap -->
<resultMap id="ordersUserResultMap" type="Orders">
<!-- 配置對映的訂單資訊 -->
<!-- id:指定查詢列中的唯一標識,如果多個列組成唯一標識,配置多個id -->
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 配置對映的關聯的使用者資訊 -->
<!-- association:用於對映關聯查詢單個物件的資訊
property:要將關聯查詢的使用者資訊對映到Orders中的哪個屬性
-->
<association property="user" javaType="com.beyond.mybatis.po.User">
<!-- id:關聯查詢使用者的唯一標識
column:指定唯一標識使用者資訊的列
javaType:對映到user的哪個屬性
-->
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="addr" property="addr"/>
</association>
</resultMap>
//查詢訂單關聯查詢使用者資訊,resultMap
List<Orders> findOrdersUserResultMap() throws Exception;
測試:
@Test
public void testfindOrdersUser() throws Exception{
SqlSession sqlSession = factory.openSession();
//通過反射拿到UserMapper的代理物件
OrdersMapperCustom ordersMapperCustom = sqlSession.getMapper(OrdersMapperCustom.class);
List<Orders> list = ordersMapperCustom.findOrdersUserResultMap();
System.out.println(list);
}
對其進行斷點除錯:實現一對一查詢時的resultType和resultMap的小結:
resultType:使用非常簡單,如果pojo中沒有包括查詢出來的列名,需要增加列名對應的屬性,即可完成對映,
如果沒有查詢結果的特殊要求,建議使用resultType。
resultMap:需要單獨定義resultMap,實現有點麻煩,如果有對查詢結果的特殊要求,使用resultMap可完成將
關聯查詢對映到pojo屬性中。
resultMap可以實現延遲載入,resultType不能實現延遲載入。
第二個需求,查詢訂單及訂單明細,一對多的查詢
主表:orders
關聯表:orderdetail
sql語句:
<!-- 查詢訂單關聯查詢使用者及訂單明細,使用resultMap -->
<select id="findOrdersAndOrderDetailResultMap" resultMap="ordersAndOrderDetailResultMap">
select
orders.*,
user.username,
user.sex,
user.addr,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num,
orderdetail.orders_id
from
orders,
user,
orderdetail
where
orders.user_id=user.id
and
orders.id=orderdetail.orders_id
</select>
然後需在orders類中新增一個orderDetails屬性,並生成get和set方法。
定義resultMap:
由於前面定義了訂單及使用者的資訊,這裡可以直接繼承;
<!-- 查詢訂單及訂單明細的resultMap -->
<resultMap id="ordersAndOrderDetailResultMap" type="orders" extends="ordersUserResultMap">
<!-- 訂單資訊,通過繼承得到 -->
<!-- 使用者資訊,通過繼承得到 -->
<!-- 訂單明細資訊
collection:對關聯查詢的到的多條記錄對映到集合中
property:orders中要對映的哪個屬性
ofType:集合屬性中pojo的型別
-->
<collection property="orderDetails" ofType="com.beyond.mybatis.po.OrderDetail">
<!-- 主鍵 -->
<id column="orderdetail_id" property="id"/>
<!--普通列-->
<result column="items_id" property="itemsId"/>
<result column="items_num" property="itemsNum"/>
<result column="orders_id" property="orderId"/>
</collection>
</resultMap>
新增介面方法://查詢訂單關聯查詢訂單明細資訊
List<Orders> findOrdersAndOrderDetailResultMap() throws Exception;
測試: @Test
public void testfindOrdersAndOrderDetailResultMap() throws Exception{
SqlSession sqlSession = factory.openSession();
//通過反射拿到UserMapper的代理物件
OrdersMapperCustom ordersMapperCustom = sqlSession.getMapper(OrdersMapperCustom.class);
List<Orders> list = ordersMapperCustom.findOrdersAndOrderDetailResultMap();
System.out.println(list);
}
利用斷點除錯,觀察list集合。一共有兩個訂單,第一個訂單屬於id為的使用者,且包含三個訂單明細(即包含三件商品)。
第三個需求,查詢使用者及使用者購買的商品,多對多
主表:user
思路:在user中新增訂單列表的屬性,List<Orders> orders
在orders中新增訂單明細列表的屬性,List<OrderDetail> orderDetails;
在orderDetail中新增商品屬性,Items items,
sql語句:
<!-- 查詢使用者及使用者購買的商品資訊,使用resultMap -->
<select id="findUsersAndItemsResultMap" resultMap="usersAndItemsResultMap">
select
orders.*,
user.username,
user.sex,
user.addr,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num,
orderdetail.orders_id,
items.name items_name,
items.detail items_detail,
items.price items_price
from
orders,
user,
orderdetail,
items
where
orders.user_id=user.id
and
orders.id=orderdetail.orders_id
and
orderdetail.items_id=items.id
</select>
User類中新增:private List<Orders> orders;生成get和set方法。Orders類中在上面已經新增,
Orders類中在上面已經新增,在OrderDetail中新增Items屬性同樣生成get和set方法。
編寫resultMap:
<!-- 查詢使用者及使用者所購買的商品資訊 -->
<resultMap id="usersAndItemsResultMap" type="user">
<!-- 使用者資訊 -->
<id column="user_id" property="userId"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="addr" property="addr"/>
<!-- 一個使用者對應多個訂單資訊 -->
<collection property="orders" ofType="com.beyond.mybatis.po.Orders">
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 一個訂單對應多個訂單明細資訊 -->
<collection property="orderDetails" ofType="com.beyond.mybatis.po.OrderDetail">
<!-- 主鍵 -->
<id column="orderdetail_id" property="id"/>
<!--普通列-->
<result column="items_id" property="itemsId"/>
<result column="items_num" property="itemsNum"/>
<result column="orders_id" property="orderId"/>
<!-- 一個訂單明細對應一條商品資訊 -->
<association property="items" javaType="com.beyond.mybatis.po.Items">
<id column="items_id" property="id"/>
<result column="items_name" property="name"/>
<result column="items_detail" property="detail"/>
<result column="items_price" property="price"/>
</association>
</collection>
</collection>
</resultMap>
編寫介面方法:
//查詢使用者及使用者購買的商品資訊
List<User> findUsersAndItemsResultMap() throws Exception;
測試:
@Test
public void testfindUsersAndItemsResultMap() throws Exception{
SqlSession sqlSession = factory.openSession();
//通過反射拿到UserMapper的代理物件
OrdersMapperCustom ordersMapperCustom = sqlSession.getMapper(OrdersMapperCustom.class);
List<User> list = ordersMapperCustom.findUsersAndItemsResultMap();
System.out.println(list);
}
斷點除錯檢視list的資訊:
一共有兩個使用者有訂單,
一個使用者購買了三件商品:華碩、聯想和神州
另外一個使用者購買了兩件商品:華碩和蘋果