mybatis入門基礎(六)----高階對映(一對一,一對多,多對多)

阿赫瓦里發表於2015-07-07

一:訂單商品資料模型

1.資料庫執行指令碼

  建立資料庫表程式碼:

 1 CREATE TABLE items (
 2   id INT NOT NULL  AUTO_INCREMENT,
 3   itemsname VARCHAR(32) NOT NULL COMMENT '商品名稱',
 4   price FLOAT(10,1) NOT NULL COMMENT '商品定價',
 5   detail TEXT COMMENT '商品描述',
 6   pic VARCHAR(64) DEFAULT NULL COMMENT '商品圖片',
 7   createtime DATETIME NOT NULL COMMENT '生產日期',
 8   PRIMARY KEY (id)
 9 )  DEFAULT CHARSET=utf8;
10 
11 /*Table structure for table `orderdetail` */
12 
13 CREATE TABLE orderdetail (
14   id INT NOT NULL AUTO_INCREMENT,
15  orders_id INT NOT NULL COMMENT '訂單id',
16   items_id INT NOT NULL COMMENT '商品id',
17   items_num INT  DEFAULT NULL COMMENT '商品購買數量',
18   PRIMARY KEY (id),
19   KEY `FK_orderdetail_1` (`orders_id`),
20   KEY `FK_orderdetail_2` (`items_id`),
21   CONSTRAINT `FK_orderdetail_1` FOREIGN KEY (`orders_id`) REFERENCES `orders` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
22   CONSTRAINT `FK_orderdetail_2` FOREIGN KEY (`items_id`) REFERENCES `items` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
23 )  DEFAULT CHARSET=utf8;
24 
25 /*Table structure for table `orders` */
26 
27 CREATE TABLE orders (
28   id INT NOT NULL AUTO_INCREMENT,
29   user_id INT NOT NULL COMMENT '下單使用者id',
30   number VARCHAR(30) NOT NULL COMMENT '訂單號',
31   createtime DATETIME NOT NULL COMMENT '建立訂單時間',
32   note VARCHAR(100) DEFAULT NULL COMMENT '備註',
33   PRIMARY KEY (`id`),
34   KEY `FK_orders_1` (`user_id`),
35   CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
36 )  DEFAULT CHARSET=utf8;
37 
38 /*Table structure for table `t_user` */
39 
40 CREATE TABLE t_user (
41   id INT NOT NULL AUTO_INCREMENT,
42   username VARCHAR(32) NOT NULL COMMENT '使用者名稱稱',
43   birthday DATE DEFAULT NULL COMMENT '生日',
44   sex CHAR(1) DEFAULT NULL COMMENT '性別',
45   address  VARCHAR(256) DEFAULT NULL COMMENT '地址',
46   PRIMARY KEY (`id`)
47 ) DEFAULT CHARSET=utf8;
View Code

  測試資料程式碼:

 1 /*Data for the table `items` */
 2 
 3 INSERT  INTO items(itemsname,price,detail,pic,createtime) VALUES 
 4 ('桌上型電腦',3000.0,'該電腦質量非常好!',NULL,'2015-07-07 13:28:53'),
 5 ('筆記本',6000.0,'筆記本效能好,質量好!',NULL,'2015-07-08 13:22:57'),
 6 ('揹包',200.0,'名牌揹包,容量大質量好!',NULL,'2015-07-010 13:25:02');
 7 
 8 /*Data for the table `orderdetail` */
 9 
10 INSERT  INTO `orderdetail`(`orders_id`,`items_id`,`items_num`) VALUES
11  (1,1,1),
12  (1,2,3),
13  (2,3,4),
14  (3,2,3);
15 
16 /*Data for the table `orders` */
17 
18 INSERT  INTO `orders`(`user_id`,`number`,`createtime`,`note`) VALUES 
19 (1,'1000010','2015-06-04 13:22:35',NULL),
20 (1,'1000011','2015-07-08 13:22:41',NULL),
21 (2,'1000012','2015-07-17 14:13:23',NULL),
22 (3,'1000012','2015-07-16 18:13:23',NULL),
23 (4,'1000012','2015-07-15 19:13:23',NULL),
24 (5,'1000012','2015-07-14 17:13:23',NULL),
25 (6,'1000012','2015-07-13 16:13:23',NULL);
26 
27 /*Data for the table `user` */
28 
29 INSERT  INTO `t_user`(`username`,`birthday`,`sex`,`address`) VALUES 
30 ('王五',NULL,'2',NULL),
31 ('張三','2014-07-10','1','北京市'),
32 ('張小明',NULL,'1','河南鄭州'),
33 ('陳小明',NULL,'1','河南鄭州'),
34 ('張三丰',NULL,'1','河南鄭州'),
35 ('陳小明',NULL,'1','河南鄭州'),
36 ('王五',NULL,NULL,NULL),
37  ('小A','2015-06-27','2','北京'),
38 ('小B','2015-06-27','2','北京'),
39 ('小C','2015-06-27','1','北京'),
40 ('小D','2015-06-27','2','北京');
View Code

2.資料模型分析思路

(1).每張表記錄的資料內容:分模組對每張表記錄的內容進行熟悉,相當 於你學習系統 需求(功能)的過程;

(2).每張表重要的欄位設定:非空欄位、外來鍵欄位;

(3).資料庫級別表與表之間的關係:外來鍵關係;

(4).表與表之間的業務關係:在分析表與表之間的業務關係時一定要建立在某個業務意義基礎上去分析

3.針對訂單商品模型的資料庫思路分析:

  使用者表:t_user-->記錄了購買商品的使用者資訊

  訂單表:orders-->記錄了使用者所建立的訂單(購買商品的訂單)

  訂單明細表:orderdetail-->記錄了訂單的詳細資訊即購買商品的資訊

  商品表:items-->記錄了商品資訊

表與表之間的業務關係:

  在分析表與表之間的業務關係時需要建立 在某個業務意義基礎上去分析。

  先分析資料級別之間有關係的表之間的業務關係:

t_userorders

  t_user---->orders:一個使用者可以建立多個訂單,一對多

  orders--->t_user:一個訂單隻由一個使用者建立,一對一

orders和orderdetail

  orders--->orderdetail:一個訂單可以包括多個訂單明細,因為一個訂單可以購買多個商品,每個商品的購買資訊在orderdetail記錄,一對多關係

  orderdetail--> orders:一個訂單明細只能包括在一個訂單中,一對一

orderdetail和itesm

  orderdetail--->itesms:一個訂單明細只對應一個商品資訊,一對一

  items--> orderdetail:一個商品可以包括在多個訂單明細 ,一對多

再分析資料庫級別沒有關係的表之間是否有業務關係:

orders和items

  ordersitems之間可以通過orderdetail表建立 關係。

4.分析之後畫出對應的圖,方便直觀的瞭解業務關係

二、一對一查詢

2.1.需求:查詢訂單資訊,關聯查詢使用者資訊

2.2.resultType實現

2.2.1.sql語句   

    確定查詢的主表:訂單表,確定查詢的關聯表:使用者表。

1 SELECT    t1.*,
2         t2.username,
3         t2.sex,
4         t2.address
5     FROM 
6         orders t1,
7         t_user t2
8 WHERE t1.user_id=t2.id

2.2.2.建立entity實體

  使用者實體:User.java

package com.mybatis.entity;
import java.util.Date;
import java.util.List;
/** 
 * @ClassName: User
 * @Description: TODO(使用者實體)
 * @author 阿赫瓦里
 */
public class User {
    private Integer id;
    // 姓名
    private String username;
    // 性別
    private String sex;
    // 地址
    private String address;
    // 生日
    private Date birthday;
    // 使用者建立的訂單列表
    private List<Orders> ordersList;
       // getter and setter ......
}

訂單實體:orders.java

package com.mybatis.entity;
import java.util.Date;
import java.util.List;
/**
 * @ClassName: Orders
 * @Description: TODO(訂單實體)
 * @author 阿赫瓦里
 */
public class Orders {
    /** 主鍵訂單Id */
    private Integer id;
    /** 下單使用者id */
    private Integer userid;
    /** 訂單號 */
    private String number;
    /** 建立訂單時間 */
    private Date createTime;
    /** 備註 */
    private String note;
    // 使用者資訊
    private User user;
    // 訂單明細
    private List<OrderDetail> orderdetails;
       //  getter and setter ......
}

商品實體:Items.java

package com.mybatis.entity;
import java.util.Date;
/**
 * @ClassName: Items
 * @Description: TODO(商品實體類)
 * @author 阿赫瓦里
 */
public class Items {
    /** 商品表主鍵Id */
    private Integer id;
    /** 商品名稱 */
    private String itemsName;
    /** 商品定價 */
    private float price;
    /** 商品描述 */
    private String detail;
    /** 商品圖片 */
    private String picture;
    /** 生產日期 */
    private Date createTime;
// getter and setter ......
}

訂單明細實體:OrderDetail.java

package com.mybatis.entity;
/**
 * @ClassName: OrderDetail
 * @Description: TODO(訂單明細實體)
 * @author 阿赫瓦里
 */
public class OrderDetail {
    /** 主鍵,訂單明細表Id */
    private Integer id;
    /** 訂單Id */
    private Integer ordersId;
    /** 商品id */
    private Integer itemsId;
    /** 商品購買數量 */
    private Integer itemsNum;
    // 明細對應的商品資訊
    private Items items;
        //  getter and setter ......
}

 建立一個包裝類,將查詢到的資訊可以全部對映到此類:OrdersCustom.java

/**
 * @ClassName: OrdersCustom
 * @Description: TODO(訂單的擴充套件類,通過此類對映訂單和使用者的查詢結果,讓此類繼承欄位較多的實體類)
 * @author: 阿赫瓦里
 */
public class OrdersCustom extends Orders {
    // 新增使用者的屬性
    private String username;
    private String sex;
    private String address;
        // getter and setter......
}

2.2.3.建立OrdersCustomMapper.java

package com.mybatis.Mapper;
import java.util.List;
import com.mybatis.entity.OrdersCustom;

/**
 * @ClassName: OrdersMapperCustom
 * @Description: TODO(OrdersMapperCustom的mapper)
 * @author 阿赫瓦里
 */
public interface OrdersCustomMapper {
    /** 查詢訂單,關聯查詢使用者資訊 */
    public List<OrdersCustom> findOrdersUser();
}

2.2.4.建立OrdersCustomMapper.xml和上面對應的介面名稱一致,以便通過mapper介面載入配置檔案

<?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">
<!-- namespace名稱空間,作用就是對sql進行分類化的管理,理解為sql隔離
    注意:使用mapper代理開發時,namespace有特殊作用,namespace等於mapper介面地址
 -->
<mapper namespace="com.mybatis.mapper.OrdersCustomMapper">
        <!-- 查詢訂單,關聯查詢使用者資訊 -->
    <select id="findOrdersUser" resultType="com.mybatis.entity.OrdersCustom">
    SELECT t1.*,
        t2.username,
        t2.sex,
        t2.address
    FROM
        orders t1,
        t_user t2
    WHERE t1.user_id=t2.id
    </select>
</mapper>

2.3.resultMap實現

2.3.1. sql語句同上

2.3.2. resultMap對映思路:

    使用resultMap將查詢結果中的訂單資訊對映到Orders物件中,在orders類中新增User屬性,將關聯查詢出來的使用者資訊對映到orders物件中的user屬性中(上面orders實體中已經新增)。

2.3.3 ordersCustomMapper.xml

   1. 定義resultMap

 1 <!-- 定義查詢訂單關聯使用者的 resultMap,將整個的查詢結果對映到com.mybatis.entity.Orders中 -->
 2     <resultMap type="com.mybatis.entity.Orders" id="OrdersUserResultMap">
 3         <!-- 配置對映的訂單資訊 -->
 4         
 5         <!-- id:查詢列中的唯一標識,訂單資訊中的唯一標識,如果多列組成唯一標識(如:一般資料庫設計中的字典表 使用聯合主鍵),就需要配置多個id 
 6             column:訂單資訊的唯一標識 列
 7             property:訂單資訊的唯一標識列所對映到orders中的那個屬性(假如:資料庫中orders表中的主鍵為orders_id,而實體屬性名稱為ordersId,
 8                 則這個配置應為<id column="orders_id" property="ordersId"/>,類似hibernate實體對映檔案配置)。
 9         -->
10         <id column="id" property="id"/>
11         <result column="user_id" property="userid"/>
12         <result column="number" property="number"/>
13         <result column="createtime" property="createTime"/>
14         <result column="note" property="note"/>
15         
16         <!-- 配置對映的關聯使用者資訊 -->
17         
18         <!--association:用於對映關聯查詢單個物件的資訊
19             property:要將關聯查詢的使用者資訊對映到Orders中那個屬性
20           -->
21         <association property="user" javaType="com.mybatis.entity.User">
22             <!-- id:關聯查詢使用者的唯一標識 
23                 column:指定唯一標識使用者資訊的列
24                 property:對映到user的那個屬性
25             -->
26             <id column="user_id" property="id"/>
27             <result column="username" property="username"/>
28             <result column="sex" property="sex"/>
29             <result column="address" property="address"/>
30         </association>
31         
32     </resultMap>

   2. statement定義

 1     <!-- 查詢訂單,關聯查詢使用者資訊,使用resultMap實現 -->
 2     <select id="findOrdersUserResultMap" resultMap="OrdersUserResultMap">
 3             SELECT t1.*,
 4                 t2.username,
 5                 t2.sex,
 6                 t2.address
 7             FROM
 8                 orders t1,
 9                 t_user t2
10             WHERE t1.user_id=t2.id
11     </select>

 3.OrderCustomMapper.java介面中新增下面的方法

/** 查詢訂單關聯查詢使用者資訊,使用reslutMap實現*/
public List<Orders>findOrdersUserResultMap();

4.對是resultType和resultMap實現的Junit測試

 1 package com.mybatis.test;
 2 
 3 import java.io.InputStream;
 4 import java.util.List;
 5 
 6 import org.apache.ibatis.io.Resources;
 7 import org.apache.ibatis.session.SqlSession;
 8 import org.apache.ibatis.session.SqlSessionFactory;
 9 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
10 import org.junit.Before;
11 import org.junit.Test;
12 
13 import com.mybatis.entity.Orders;
14 import com.mybatis.entity.OrdersCustom;
15 import com.mybatis.mapper.OrdersCustomMapper;
16 
17 public class OrdersCustomMapperTest {
18 
19     private SqlSessionFactory sqlSessionFactory;
20 
21     // 此方法是在執行findUserByIdTest之前執行
22     @Before
23     public void setUp() throws Exception {
24         String resource = "SqlMapConfig.xml";
25         InputStream inputStream = Resources.getResourceAsStream(resource);
26         // 建立SqlSessionFcatory
27         sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
28     }
29 
30     // 查詢訂單,關聯查詢使用者資訊,使用resultType實現的測試
31     @Test
32     public void TestFindOrdersUser() {
33         SqlSession sqlSession = sqlSessionFactory.openSession();
34         // 建立代理物件
35         OrdersCustomMapper oc = sqlSession.getMapper(OrdersCustomMapper.class);
36         // 呼叫mapper的方法
37         List<OrdersCustom> list = oc.findOrdersUser();
38         System.out.println(list);
39         sqlSession.close();
40     }
41 
42     // 查詢訂單,關聯查詢使用者資訊,使用resultMap實現的測試
43     @Test
44     public void TestFindOrdersUserResultMap() {
45         SqlSession sqlSession = sqlSessionFactory.openSession();
46         // 建立代理物件
47         OrdersCustomMapper oc = sqlSession.getMapper(OrdersCustomMapper.class);
48         // 呼叫mapper的方法
49         List<Orders> list = oc.findOrdersUserResultMap();
50         System.out.println(list);
51         sqlSession.close();
52 
53     }
54 
55 }
View Code

5.resultTyperesultMap實現一對一查詢小結

 實現一對一查詢:

  a.resultType:使用resultType實現較為簡單,如果pojo中沒有包括查詢出來的列名,需要增加列名對應的屬性,即可完成對映。

  b.如果沒有查詢結果的特殊要求建議使用resultType

  c.resultMap:需要單獨定義resultMap,實現有點麻煩,如果對查詢結果有特殊的要求,使用resultMap可以完成將關聯查詢對映pojo的屬性中。

  d.resultMap可以實現延遲載入,resultType無法實現延遲載入。

三、一對多查詢

 3.1. 需求:查詢訂單(關聯使用者)及訂單明細

 3.2. 在orders.java類中新增List<orderDetail> orderDetails屬性(上面實體已新增)

    最終會將訂單資訊對映到orders中,訂單所對應的訂單明細對映到orders中的orderDetails屬性中.

3.3.在ordersCustomMapper.xml中新增如下程式碼

    <!-- 查詢訂單關聯查詢使用者及訂單明細 -->
    <select id="findOrdersAndOrderDetailResultMap" resultMap="ordersAndOrderDetailResultMap">
            SELECT 
                  t1.*,
                  t2.username,
                  t2.sex,
                  t2.address,
                  t3.id orderdetail_id,
                  t3.items_id,
                  t3.items_num,
                  t3.orders_id
            FROM
                  orders t1,
                  t_user t2,
                  orderdetail t3
            WHERE t1.user_id = t2.id AND t3.orders_id=t1.id
    </select>

resultMap的定義同樣新增到ordersCustomMapper.xml

<!-- 查詢訂單(關聯使用者)及訂單明細的resultMap -->
    <resultMap type="com.mybatis.entity.Orders" id="ordersAndOrderDetailResultMap" extends="OrdersUserResultMap">
        <!-- 訂單資訊 -->
        <!-- 關聯使用者資訊 -->
        <!-- 使用extends繼承,不用在中配置訂單資訊和使用者資訊的對映-->
        
        <!-- 關聯訂單明細資訊 
            一個訂單關聯查詢出了多條訂單明細,要使用collection對映
            collection:對關聯查詢到的多條記錄對映到集合中
            property:將關聯查詢到的多條記錄對映到orders類的那個屬性
            ofType:指定對映的集合屬性中pojo的型別
        -->
        <collection property="orderdetails" ofType="com.mybatis.entity.OrderDetail">
            <!-- id:唯一標識
                 property:要將訂單明細的唯一標識對映到com.mybatis.entity.OrderDetail的那個屬性
             -->
            <id column="orderdetail_id" property="id"/>
            <result column="items_id" property="itemsId"/>
            <result column="items_num" property="itemsNum"/>
            <result column="orders_id" property="ordersId"/>
        </collection>
    </resultMap>

3.4. 在OrderCustomMapper.java介面類中新增一個方法

/**查詢訂單(關聯使用者)以及訂單明細*/
    public List<OrderDetail>findOrdersAndOrderDetailResultMap();

3.5.在Junit測試類中新增測試方法

// 查詢訂單(關聯使用者)以及訂單明細的測試
    @Test
    public void TestFindOrdersAndOrderDetailResultMap() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 建立代理物件
        OrdersCustomMapper oc = sqlSession.getMapper(OrdersCustomMapper.class);
        // 呼叫mapper的方法
        List<OrderDetail> list = oc.findOrdersAndOrderDetailResultMap();
        System.out.println(list);
        sqlSession.close();  
    }

3.6. 小結 

 mybatis使用resultMapcollection對關聯查詢的多條記錄對映到一個list集合屬性中。

 使用resultType實現:將訂單明細對映到orders中的orderdetails中,需要自己處理,使用雙重迴圈遍歷,去掉重複記錄,將訂單明細放在orderdetails中。

四、多對多查詢

 4.1.需求:查詢使用者以及使用者購買的商品資訊

4.2.對映思路

  將使用者資訊對映到user中。

  在user類中新增訂單列表屬性List<Orders> orderslist,將使用者建立的訂單對映到orderslist;

  在Orders中新增訂單明細列表屬性List<OrderDetail>orderdetials,將訂單的明細對映到orderdetials;

  在OrderDetail中新增Items屬性,將訂單明細所對應的商品對映到Item;

4.3.OrdersCustomMapper.xml新增如下程式碼

<!-- 查詢使用者即購買的商品資訊的ResultMap -->
        <resultMap type="com.mybatis.entity.User" id="userAndItemsResultMap">
            <!-- 使用者資訊 -->
            <id column="user_id" property="id"/>
            <result column="username" property="username"/>
            <result column="sex" property="sex"/>
            <result column="address" property="address"/>
        <!-- 訂單資訊
            一個使用者對應多個訂單,使用collection對映 -->
            <collection property="ordersList" ofType="com.mybatis.entity.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.mybatis.entity.OrderDetail">
                          <id column="orderdetail_id" property="id"/>
                         <result column="items_id"   property="itemsId"/>
                         <result column="items_num"  property="itemsNum"/>
                         <result column="orders_id"  property="ordersId"/>
                         <!-- 商品資訊
                              一個訂單明細對應一個商品
                           -->
                       <association property="items" javaType="com.mybatis.entity.Items">
                           <id column="items_id" property="id"/>
                           <result column="items_name" property="itemsName"/>
                           <result column="items_detail" property="detail"/>
                           <result column="items_price" property="price"/>
                       </association>
                  </collection>
              </collection>
            
        </resultMap>
    <!-- 查詢使用者及使用者購買的商品資訊,使用resulaMap-->
    <select id="findUserAndItemsResultMap" resultMap="userAndItemsResultMap">
            SELECT 
                   t1.*,
                   t2.username,
                   t2.sex,
                   t2.address,
                   t3.id orderdetail_id,
                   t3.items_id,
                   t3.items_num,
                   t3.orders_id,
                   t4.itemsname items_name,
                   t4.detail items_detail,
                   t4.price items_price
            FROM
                  orders t1,
                  t_user t2,
                  orderdetail t3,
                  items t4
            WHERE t1.user_id =  t2.id AND  t3.orders_id=t1.id AND t3.items_id = t4.id
    </select>

4.4. 在OrderCustomMapper.java新增如下方法

    /** 查詢使用者及使用者所購買的商品資訊 */
    public List<User> findUserAndItemsResultMap();

4.5.在Junit測試類中新增測試方法 

    // 查詢使用者及使用者購買的商品的資訊
    @Test
    public void TestFindUserAndItemsResultMap() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 建立代理物件
        OrdersCustomMapper oc = sqlSession.getMapper(OrdersCustomMapper.class);
        // 呼叫mapper的方法
        List<User> list = oc.findUserAndItemsResultMap();
        System.out.println(list);
        sqlSession.close();
    }

4.6.resultMap總結

resultType

  作用:將查詢結果按照sql列名pojo屬性名一致性對映到pojo中。

  場合

    常見一些明細記錄的展示,比如使用者購買商品明細,將關聯查詢資訊全部展示在頁面時,此時可直接使用resultType將每一條記錄對映到pojo中,在前端頁面遍歷listlist中是pojo)即可。

resultMap

  使用associationcollection完成一對一和一對多高階對映(對結果有特殊的對映要求)。

association

  作用:將關聯查詢資訊對映到一個pojo物件中。

  場合

    為了方便查詢關聯資訊可以使用association將關聯訂單資訊對映為使用者物件的pojo屬性中,比如:查詢訂單及關聯使用者資訊。

    使用resultType無法將查詢結果對映到pojo物件的pojo屬性中,根據對結果集查詢遍歷的需要選擇使用resultType還是resultMap

collection

  作用:將關聯查詢資訊對映到一個list集合中。

  場合

    為了方便查詢遍歷關聯資訊可以使用collection將關聯資訊對映到list集合中,比如:查詢使用者許可權範圍模組及模組下的選單,可使用collection將模組對映到模組list中,將選單列表對映到模組物件的選單list屬性中,這樣的作的目的也是方便對查詢結果集進行遍歷查詢。

  如果使用resultType無法將查詢結果對映到list集合中。

相關文章