日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

MyBatis初級(jí)實(shí)戰(zhàn)之六:一對(duì)多關(guān)聯(lián)查詢(xún)

 頭號(hào)碼甲 2021-05-16

歡迎訪問(wèn)我的GitHub

https://github.com/zq2599/blog_demos

內(nèi)容:所有原創(chuàng)文章分類(lèi)匯總及配套源碼,涉及Java、Docker、Kubernetes、DevOPS等;

本篇概覽

  • 本文是《MyBatis初級(jí)實(shí)戰(zhàn)》系列的第六篇,繼續(xù)實(shí)踐從多表獲取數(shù)據(jù);

  • 回顧上一篇,咱們實(shí)戰(zhàn)了多表關(guān)聯(lián)的一對(duì)一關(guān)系,如下圖所示,查找日志記錄時(shí),把對(duì)應(yīng)的用戶(hù)信息查出:
    在這里插入圖片描述

  • 本篇要實(shí)踐的是一對(duì)多關(guān)系:查詢(xún)用戶(hù)記錄時(shí),把該用戶(hù)的所有日志記錄都查出來(lái),邏輯關(guān)系如下圖:

在這里插入圖片描述

  • 在具體編碼實(shí)現(xiàn)一對(duì)多查詢(xún)時(shí),分別使用聯(lián)表和嵌套兩種方式實(shí)現(xiàn),每種方式都按照下圖的步驟執(zhí)行:

在這里插入圖片描述

源碼下載

  1. 如果您不想編碼,可以在GitHub下載所有源碼,地址和鏈接信息如下表所示(https://github.com/zq2599/blog_demos):

名稱(chēng)鏈接備注
項(xiàng)目主頁(yè)https://github.com/zq2599/blog_demos該項(xiàng)目在GitHub上的主頁(yè)
git倉(cāng)庫(kù)地址(https)https://github.com/zq2599/blog_demos.git該項(xiàng)目源碼的倉(cāng)庫(kù)地址,https協(xié)議
git倉(cāng)庫(kù)地址(ssh)git@github.com:zq2599/blog_demos.git該項(xiàng)目源碼的倉(cāng)庫(kù)地址,ssh協(xié)議
  1. 這個(gè)git項(xiàng)目中有多個(gè)文件夾,本章的應(yīng)用在mybatis文件夾下,如下圖紅框所示:

在這里插入圖片描述
3. mybatis是個(gè)父工程,里面有數(shù)個(gè)子工程,本篇的源碼在relatedoperation子工程中,如下圖紅框所示:

在這里插入圖片描述

準(zhǔn)備數(shù)據(jù)

  1. 本次實(shí)戰(zhàn),在名為mybatis的數(shù)據(jù)庫(kù)中建立兩個(gè)表(和前面幾篇文章中的表結(jié)構(gòu)一模一樣):user和log表;

  2. user表記錄用戶(hù)信息,非常簡(jiǎn)單,只有三個(gè)字段:主鍵、名稱(chēng)、年齡

  3. log表記錄用戶(hù)行為,四個(gè)字段:主鍵、用戶(hù)id、行為描述、行為時(shí)間

  4. user和log的關(guān)系如下圖:

在這里插入圖片描述
5. 建表和添加數(shù)據(jù)的語(yǔ)句如下:

use mybatis;

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
  `id` int(32) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL,
  `age` int(32) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `log`;

CREATE TABLE `log` (
  `id` int(32) NOT NULL AUTO_INCREMENT,
  `user_id` int(32),
  `action` varchar(255) NOT NULL,
  `create_time` datetime not null,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

INSERT INTO mybatis.user (id, name, age) VALUES (3, 'tom', 11);

INSERT INTO mybatis.log (id, user_id, action, create_time) VALUES (3, 3, 'read book', '2020-08-07 08:18:16');
INSERT INTO mybatis.log (id, user_id, action, create_time) VALUES (4, 3, 'go to the cinema', '2020-09-02 20:00:00');
INSERT INTO mybatis.log (id, user_id, action, create_time) VALUES (5, 3, 'have a meal', '2020-10-05 12:03:36');
INSERT INTO mybatis.log (id, user_id, action, create_time) VALUES (6, 3, 'have a sleep', '2020-10-06 13:00:12');
INSERT INTO mybatis.log (id, user_id, action, create_time) VALUES (7, 3, 'write', '2020-10-08 09:21:11');

關(guān)于多表關(guān)聯(lián)查詢(xún)的兩種方式

  • 多表關(guān)聯(lián)查詢(xún)的實(shí)現(xiàn)有聯(lián)表嵌套查詢(xún)兩種,它們的差異在Mybatis中體現(xiàn)在resultMap的定義上:

  1. 聯(lián)表時(shí),resultMap內(nèi)使用collection子節(jié)點(diǎn),將聯(lián)表查詢(xún)的結(jié)果映射到關(guān)聯(lián)對(duì)象集合;

  2. 嵌套時(shí),resultMap內(nèi)使用association子節(jié)點(diǎn),association的select屬性觸發(fā)一次新的查詢(xún);

  • 上述兩種方式都能成功得到查詢(xún)結(jié)果,接下來(lái)逐一嘗試;

聯(lián)表查詢(xún)

  1. 本篇繼續(xù)使用上一篇中創(chuàng)建的子工程relatedoperation

  2. 實(shí)體類(lèi)UserWithLogs.java如下,可見(jiàn)成員變量logs是用來(lái)保存該用戶(hù)所有日志的集合:

package com.bolingcavalry.relatedoperation.entity;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;

@Data
@NoArgsConstructor
@ApiModel(description = "用戶(hù)實(shí)體類(lèi)(含行為日志集合)")
public class UserWithLogs {

    @ApiModelProperty(value = "用戶(hù)ID")
    private Integer id;

    @ApiModelProperty(value = "用戶(hù)名", required = true)
    private String name;

    @ApiModelProperty(value = "用戶(hù)地址", required = false)
    private Integer age;

    @ApiModelProperty(value = "行為日志", required = false)
    private List<Log> logs;
}
  1. 保存SQL的UserMapper.xml如下,先把聯(lián)表查詢(xún)的SQL寫(xiě)出來(lái),結(jié)果在名為
    leftJoinResultMap的resultMap中處理:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-////DTD Mapper 3.0//EN" "http:///dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bolingcavalry.relatedoperation.mapper.UserMapper">

    <select id="leftJoinSel" parameterType="int" resultMap="leftJoinResultMap">
        select
            u.id as user_id,
            u.name as user_name,
            u.age as user_age,
            l.id as log_id,
            l.user_id as log_user_id,
            l.action as log_action,
            l.create_time as log_create_time

        from mybatis.user as u
                 left join mybatis.log as l
                           on u.id = l.user_id
        where u.id = #{id}
    </select>
</mapper>
  1. leftJoinResultMap這個(gè)resultMap是一對(duì)多的關(guān)鍵,里面的collection將log的所有記錄映射到logs集合中:

    <resultMap id="leftJoinResultMap" type="UserWithLogs">
        <id property="id" column="user_id"/>
        <result  property="name" column="user_name" jdbcType="VARCHAR"/>
        <result property="age" column="user_age" jdbcType="INTEGER" />
        <collection property="logs" ofType="Log">
            <id property="id" column="log_id"/>
            <result property="userId" column="log_user_id" jdbcType="INTEGER" />
            <result property="action" column="log_action" jdbcType="VARCHAR" />
            <result property="createTime" column="log_create_time" jdbcType="TIMESTAMP" />
        </collection>
    </resultMap>
  1. 接口定義UserMapper.java :

@Repository
public interface UserMapper {
    UserWithLogs leftJoinSel(int id);
}
  1. service層:

@Service
public class UserService {
    @Autowired
    UserMapper userMapper;

    public UserWithLogs leftJoinSel(int id) {
        return userMapper.leftJoinSel(id);
    }
}
  1. controller層的代碼略多,是因?yàn)橄氚裺wagger信息做得盡量完整:

@RestController
@RequestMapping("/user")
@Api(tags = {"UserController"})
public class UserController {
    @Autowired
    private UserService userService;

    @ApiOperation(value = "根據(jù)ID查找user記錄(包含行為日志),聯(lián)表查詢(xún)", notes="根據(jù)ID查找user記錄(包含行為日志),聯(lián)表查詢(xún)")
    @ApiImplicitParam(name = "id", value = "用戶(hù)ID", paramType = "path", required = true, dataType = "Integer")
    @RequestMapping(value = "/leftjoin/{id}", method = RequestMethod.GET)
    public UserWithLogs leftJoinSel(@PathVariable int id){
        return userService.leftJoinSel(id);
    }
}
  1. 最后是單元測(cè)試,在前文創(chuàng)建的ControllerTest.java中新建內(nèi)部類(lèi)User用于user表相關(guān)的單元測(cè)試,可見(jiàn)封裝了一個(gè)私有方法queryAndCheck負(fù)責(zé)請(qǐng)求和驗(yàn)證結(jié)果,后面的嵌套查詢(xún)也會(huì)用到:

    @Nested
    @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
    @DisplayName("用戶(hù)服務(wù)")
    class User {

        /**
         * 通過(guò)用戶(hù)ID獲取用戶(hù)信息有兩種方式:left join和嵌套查詢(xún),
         * 從客戶(hù)端來(lái)看,僅一部分path不同,因此將請(qǐng)求和檢查封裝到一個(gè)通用方法中,
         * 調(diào)用方法只需要指定不同的那一段path
         * @param subPath
         * @throws Exception
         */
        private void queryAndCheck(String subPath) throws Exception {
            String queryPath = "/user/" + subPath + "/" + TEST_USER_ID;

            log.info("query path [{}]", queryPath);

            mvc.perform(MockMvcRequestBuilders.get(queryPath).accept(MediaType.APPLICATION_JSON))
                    .andExpect(status().isOk())
                    .andExpect(jsonPath("$.id").value(TEST_USER_ID))
                    .andExpect(jsonPath("$..logs.length()").value(5))
                    .andDo(print());
        }

        @Test
        @DisplayName("通過(guò)用戶(hù)ID獲取用戶(hù)信息(包含行為日志),聯(lián)表查詢(xún)")
        @Order(1)
        void leftJoinSel() throws Exception {
            queryAndCheck(SEARCH_TYPE_LEFT_JOIN);
        }
    }
  1. 執(zhí)行上述單元測(cè)試方法leftJoinSel,得到結(jié)果如下:

在這里插入圖片描述

  1. 為了便于觀察,我將上圖紅框中的JSON數(shù)據(jù)做了格式化,如下所示,可見(jiàn)log表中的五條記錄都被關(guān)聯(lián)出來(lái)了,作為整個(gè)user對(duì)象的一個(gè)字段:

{
    "id": 3,
    "name": "tom",
    "age": 11,
    "logs": [
        {
            "id": 3,
            "userId": 3,
            "action": "read book",
            "createTime": "2020-08-07"
        },
        {
            "id": 4,
            "userId": 3,
            "action": "go to the cinema",
            "createTime": "2020-09-02"
        },
        {
            "id": 5,
            "userId": 3,
            "action": "have a meal",
            "createTime": "2020-10-05"
        },
        {
            "id": 6,
            "userId": 3,
            "action": "have a sleep",
            "createTime": "2020-10-06"
        },
        {
            "id": 7,
            "userId": 3,
            "action": "write",
            "createTime": "2020-10-08"
        }
    ]
}
  1. 以上就是通過(guò)聯(lián)表的方式獲取一對(duì)多關(guān)聯(lián)結(jié)果,接下來(lái)咱們嘗試嵌套查詢(xún);

嵌套查詢(xún)

  1. 嵌套查詢(xún)的基本思路是將多次查詢(xún)將結(jié)果合并,關(guān)鍵點(diǎn)還是在SQL和resultMap的配置上,先看嵌套查詢(xún)的SQL,在UserMapper.xml文件中,如下,可見(jiàn)僅查詢(xún)了user表,并未涉及l(fā)og表:

    <select id="nestedSel" parameterType="int" resultMap="nestedResultMap">
        select
            u.id as user_id,
            u.name as user_name,
            u.age as user_age
        from mybatis.user as u
        where u.id = #{id}
    </select>
  1. 上面的SQL顯示結(jié)果保存在名為nestedResultMap的resultMap中,來(lái)看這個(gè)resultMap,如下,可見(jiàn)實(shí)體類(lèi)的logs字段對(duì)應(yīng)的是一個(gè)association節(jié)點(diǎn),該節(jié)點(diǎn)的select屬性代表這是個(gè)子查詢(xún),查詢(xún)條件是user_id

    <!-- association節(jié)點(diǎn)的select屬性會(huì)觸發(fā)嵌套查詢(xún)-->
    <resultMap id="nestedResultMap" type="UserWithLogs">
        <!-- column屬性中的user_id,來(lái)自前面查詢(xún)時(shí)的"u.id as user_id" -->
        <id property="id" column="user_id"/>
        <!-- column屬性中的user_name,來(lái)自前面查詢(xún)時(shí)的"u.name as user_name" -->
        <result  property="name" column="user_name" jdbcType="VARCHAR"/>
        <!-- column屬性中的user_age,來(lái)自前面查詢(xún)時(shí)的"u.age as user_age" -->
        <result property="age" column="user_age" jdbcType="INTEGER" />
        <!-- select屬性,表示這里要執(zhí)行嵌套查詢(xún),將user_id傳給嵌套的查詢(xún) -->
        <association property="logs" column="user_id" select="selectLogByUserId"></association>
    </resultMap>
  1. 名為selectLogByUserId的SQL和resultMap如下,即查詢(xún)log表:

    <select id="selectLogByUserId" parameterType="int" resultMap="log">
        select
            l.id,
            l.user_id,
            l.action,
            l.create_time
        from mybatis.log as l
        where l.user_id = #{user_id}
    </select>

    <resultMap id="log" type="log">
        <id property="id" column="id"/>
        <result column="user_id" jdbcType="INTEGER" property="userId"/>
        <result column="action" jdbcType="VARCHAR" property="action"/>
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
        <result column="user_name" jdbcType="VARCHAR" property="userName"/>
    </resultMap>
  1. 以上就是嵌套查詢(xún)的關(guān)鍵點(diǎn)了,接下來(lái)按部就班的在LogMapper、LogService、LogController中添加方法即可,下面是LogController中對(duì)應(yīng)的web接口,稍后會(huì)在單元測(cè)試中調(diào)用這個(gè)接口進(jìn)行驗(yàn)證:

    @ApiOperation(value = "根據(jù)ID查找user記錄(包含行為日志),嵌套查詢(xún)", notes="根據(jù)ID查找user記錄(包含行為日志),嵌套查詢(xún)")
    @ApiImplicitParam(name = "id", value = "用戶(hù)ID", paramType = "path", required = true, dataType = "Integer")
    @RequestMapping(value = "/nested/{id}", method = RequestMethod.GET)
    public UserWithLogs nestedSel(@PathVariable int id){
        return userService.nestedSel(id);
    }
  1. 單元測(cè)試的代碼很簡(jiǎn)單,調(diào)用前面封裝好的queryAndCheck方法即可:

        @Test
        @DisplayName("通過(guò)用戶(hù)ID獲取用戶(hù)信息(包含行為日志),嵌套查詢(xún)")
        @Order(2)
        void nestedSel() throws Exception {
            queryAndCheck(SEARCH_TYPE_NESTED);
        }
  1. 執(zhí)行單元測(cè)試的結(jié)果如下圖紅框所示,和前面的聯(lián)表查詢(xún)一樣:

在這里插入圖片描述

  • 兩種方式的一對(duì)多關(guān)聯(lián)查詢(xún)都試過(guò)了,接下來(lái)看看兩者的區(qū)別;

聯(lián)表和嵌套的區(qū)別

  1. 首先是聯(lián)表查詢(xún)的日志,如下,只有一次查詢(xún):

2020-10-21 20:25:05.754  INFO 15408 --- [           main] c.b.r.controller.ControllerTest          : query path [/user/leftjoin/3]
2020-10-21 20:25:09.910  INFO 15408 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
2020-10-21 20:25:09.925 DEBUG 15408 --- [           main] c.b.r.mapper.UserMapper.leftJoinSel      : ==>  Preparing: select u.id as user_id, u.name as user_name, u.age as user_age, l.id as log_id, l.user_id as log_user_id, l.action as log_action, l.create_time as log_create_time from mybatis.user as u left join mybatis.log as l on u.id = l.user_id where u.id = ?
2020-10-21 20:25:10.066 DEBUG 15408 --- [           main] c.b.r.mapper.UserMapper.leftJoinSel      : ==> Parameters: 3(Integer)
2020-10-21 20:25:10.092 DEBUG 15408 --- [           main] c.b.r.mapper.UserMapper.leftJoinSel      : <==      Total: 5
  1. 再來(lái)看看嵌套查詢(xún)的日志,兩次:

2020-10-21 20:37:29.648  INFO 24384 --- [           main] c.b.r.controller.ControllerTest          : query path [/user/nested/3]
2020-10-21 20:37:33.867  INFO 24384 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
2020-10-21 20:37:33.880 DEBUG 24384 --- [           main] c.b.r.mapper.UserMapper.nestedSel        : ==>  Preparing: select u.id as user_id, u.name as user_name, u.age as user_age from mybatis.user as u where u.id = ?
2020-10-21 20:37:34.018 DEBUG 24384 --- [           main] c.b.r.mapper.UserMapper.nestedSel        : ==> Parameters: 3(Integer)
2020-10-21 20:37:34.041 DEBUG 24384 --- [           main] c.b.r.m.UserMapper.selectLogByUserId     : ====>  Preparing: select l.id, l.user_id, l.action, l.create_time from mybatis.log as l where l.user_id = ?
2020-10-21 20:37:34.043 DEBUG 24384 --- [           main] c.b.r.m.UserMapper.selectLogByUserId     : ====> Parameters: 3(Integer)
2020-10-21 20:37:34.046 DEBUG 24384 --- [           main] c.b.r.m.UserMapper.selectLogByUserId     : <====      Total: 5
2020-10-21 20:37:34.047 DEBUG 24384 --- [           main] c.b.r.mapper.UserMapper.nestedSel        : <==      Total: 1
  • 至此,MyBatis常用的多表關(guān)聯(lián)查詢(xún)實(shí)戰(zhàn)就完成了,希望能給您一些參考,接下來(lái)的文章,咱們繼續(xù)體驗(yàn)MyBatis帶給我們的各種特性。

你不孤單,欣宸原創(chuàng)一路相伴

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多