1. 程式人生 > >深入淺出Mybatis(八)高階對映

深入淺出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的資訊:

一共有兩個使用者有訂單,

一個使用者購買了三件商品:華碩、聯想和神州


另外一個使用者購買了兩件商品:華碩和蘋果