009,Mybatis级联关系

级联关系

V哥官网:http://www.vgxit.com
本文对应视频教程:http://www.vgxit.com/course/22


1,概述:

级联关系,我们之前在学习Mysql的时候就已经学到过了。就是我们提出的一对一关联,一对多关联,多对多关联。

在Mybatis中的级联分为三种:

  • 鉴别器(discriminator):它是一个根据某些条件决定采用具体实现级联的方案,有员工体检报告,男性和女性的体检表肯定是不一样的
  • 一对一(association):比如我们人和身份证就是一种一对一的级联,员工和员工工牌也是一对一的级联
  • 一对多(collection):比如班级和学生就是一种一对多的级联

在Mybatis中没有多对多级联,因为多对多级联比较复杂,在Mybatis中采用两个1对多级联进行替换。


2,课前准备:

为了更好的讲解Mybatis中的级联,首先我们做好课前准备,我们定义一组模型:

接下来我们就来创建对应的表:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for employee
-- ----------------------------
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee`  (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键Id',
  `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '雇员姓名',
  `gender` tinyint(1) NOT NULL COMMENT '性别,1表示男,2表示女',
  `birthday` date NOT NULL COMMENT '对应的出生日期',
  `mobile` char(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户手机号',
  `email` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '对应的邮箱',
  `position` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户职位',
  `remarks` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '备注',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '员工表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for employee_card
-- ----------------------------
DROP TABLE IF EXISTS `employee_card`;
CREATE TABLE `employee_card`  (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增Id',
  `employee_id` int(11) NOT NULL COMMENT '对应的员工表',
  `card_no` char(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '对应的员工卡号',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '员工工牌表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for fmale_health_form
-- ----------------------------
DROP TABLE IF EXISTS `fmale_health_form`;
CREATE TABLE `fmale_health_form`  (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增Id',
  `employee_id` int(11) NOT NULL COMMENT '对应的员工编号id',
  `heart` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '心脏检测信息',
  `liver` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '肝脏检测信息',
  `lung` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '肺部检测信息',
  `uterus` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '子宫信息',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '女性体检表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for male_health_form
-- ----------------------------
DROP TABLE IF EXISTS `male_health_form`;
CREATE TABLE `male_health_form`  (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增Id',
  `employee_id` int(11) NOT NULL COMMENT '对应的员工编号id',
  `heart` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '心脏检测信息',
  `liver` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '肝脏检测信息',
  `lung` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '肺部检测信息',
  `prostate` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '前列腺信息',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '男性体检表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for task
-- ----------------------------
DROP TABLE IF EXISTS `task`;
CREATE TABLE `task`  (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增Id',
  `employee_id` int(11) NOT NULL COMMENT '对应的员工id',
  `title` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '对应的标题',
  `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '对应的任务内容',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '任务表' ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

然后构建对应的PO,Mapper,以及初始化数据代码:

package com.vgxit.learn.vgmybatis.ktdm.test;

import com.vgxit.learn.vgmybatis.ktdm.mapper.*;
import com.vgxit.learn.vgmybatis.ktdm.po.*;
import com.vgxit.learn.vgmybatis.ktdm.tool.MybatisTool;
import org.apache.ibatis.session.SqlSession;

import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

public class InitDataTest {
    public static void main(String[] args) throws IOException {
        try (SqlSession sqlSession = MybatisTool.getSqlSession()){
            EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
            MaleHealthFormMapper maleHealthFormMapper = sqlSession.getMapper(MaleHealthFormMapper.class);
            FmaleHealthFormMapper fmaleHealthFormMapper = sqlSession.getMapper(FmaleHealthFormMapper.class);
            EmployeeCardMapper employeeCardMapper = sqlSession.getMapper(EmployeeCardMapper.class);
            TaskMapper taskMapper = sqlSession.getMapper(TaskMapper.class);
            List<Employee> employees = Arrays.asList(
                    new Employee(null, "周杰伦", (short) 1, new Date(1983, 11, 1), "13611111111", "mail.jay@163.com",
                            "总经理", "流行歌手"),
                    new Employee(null, "蔡依林", (short) 2, new Date(1985, 11, 1), "13622222222", "mail.jolin@163.com",
                            "首席财务管理", "流行歌手"),
                    new Employee(null, "李一桐", (short) 2, new Date(1991, 11, 1), "13333333333", "mail.tongtong@163" +
                            ".com", "首席技术官", "演员")
            );
            int index = 1;
            for (Employee employee : employees) {
                //添加员工
                employeeMapper.add(employee);
                if (employee.getGender() == (short) 1) {
                    MaleHealthForm maleHealthForm = new MaleHealthForm(null, employee.getId(), "很好", "很好", "还可以", "要注意");
                    maleHealthFormMapper.add(maleHealthForm);
                } else {
                    FmaleHealthForm fmaleHealthForm = new FmaleHealthForm(null, employee.getId(), "很好", "很好", "还可以", "赶快结婚生孩子了");
                    fmaleHealthFormMapper.add(fmaleHealthForm);
                }
                employeeCardMapper.add(new EmployeeCard(null, employee.getId(), "EMVGXIT000" + (index++)));
                for (int i = 0; i < 10; i++) {
                    Task task = new Task(null, employee.getId(), "任务标题" + index + ":" + i, "任务内容" + index + ":" + i);
                    taskMapper.add(task);
                }
            }
            sqlSession.commit();
        }
    }
}


3,一对一

一对一关联,我们就用员工和工牌来举例。

我们可以通过员工来获取到卡号,也可以通过卡号来获取到员工:

首先在员工PO中加上一个字段是卡号的PO:

/**
     * 员工卡号
     */
    private EmployeeCard employeeCard;

然后,我们在EmployeeCardMapper中加一个通过员工Id来获取工卡的方法:

/**
     * 通过员工Id获取对应的工卡
     * @param employeeId 员工的Id
     * @return
     */
    EmployeeCard getByEmployeeId(@Param("employeeId") int employeeId);

在xml中编写对应的代码:

<select id="getByEmployeeId" resultType="EmployeeCard" parameterType="int">
        select * from emplyee_card where employee_id=#{employeeId}
    </select>

然后我们接下来就要用到Mybatis里面的ResultMap了。我们在EmployeeMap里面来定义对应的ResultMap。这个ResultMap就是用来定义我们返回的数据类型的。

<resultMap id="employeeResultMap" type="Employee">
        <id column="id" property="id"/>
        <!--一个员工对应一个卡号-->
        <association property="employeeCard" column="id" select="com.vgxit.learn.vgmybatis.ktdm.mapper.EmployeeCardMapper.getByEmployeeId"/>
    </resultMap>

接下来我们定义一个通过id获取员工的方法,在EmployeeMapper中定义一个方法叫做getById:

/**
     * 通过id来获取对应的员工
     * @param id
     * @return
     */
    Employee getById(int id);
<select id="getById" resultMap="employeeResultMap" parameterType="int">
        select * from employee where id=#{id}
    </select>

对应的测试运行代码如下:

private static void testOne2One() throws IOException {
        try (SqlSession sqlSession = MybatisTool.getSqlSession()){
            EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
            Employee employee = employeeMapper.getById(1);
            System.out.println(employee.getEmployeeCard());
        }
    }

运行结果:

上面我们可以看到我们通过员工的getEmployeeCard方法可以获取到对应的卡号。获取对应卡号的方式是再次调用EmployeeCardMapper里面的一个方法,再次发送一套sql来获取。而这条Sql是在ResultMap里面的association里面获取的。那么接下来如果我们要用卡号来获取对应的员工,怎么办?

同样我们在EmployeeCard这个PO里面我们加上一个Employee属性。

/**
     * 对应的员工
     */
    private Employee employee;

然后再EmployeeCardMapper.xml中建立一个ResultMap:

<resultMap id="employeeCardResultMap" type="EmployeeCard">
        <id property="id" column="id"/>
        <association property="employee" column="employee_id" select="com.vgxit.learn.vgmybatis.ktdm.mapper.EmployeeMapper.getById"/>
    </resultMap>

接下来建立获取工卡,和工卡关联员工的方法:

/**
     * 通过Id来获取对应的卡号
     * @param id
     * @return
     */
    EmployeeCard getById(int id);
<select id="getById" resultMap="employeeCardResultMap" parameterType="int">
        select * from employee_card where id=#{id}
    </select>

测试代码:

private static void testOne2One() throws IOException {
        try (SqlSession sqlSession = MybatisTool.getSqlSession()){
            EmployeeCardMapper employeeCardMapper = sqlSession.getMapper(EmployeeCardMapper.class);
            EmployeeCard employeeCard = employeeCardMapper.getById(1);
            System.out.println(employeeCard.getEmployee());
        }
    }

4,一对多

比如我们上面的员工和任务,一个员工有多个任务,一个任务属于某一个员工。

案例一:通过员工来获取对应的任务

1,首先我们在员工PO里面创建一个属性List<Task> tasks。

private List<Task> tasks;

2,定义对应的查询员工的方法和查询任务的方法

List<Task> findByEmployeeId(@Param("employeeId") int employeeId);
<select id="findByEmployeeId" resultType="Task" parameterType="int">
        select * from task where employee_id=#{employeeId}
    </select>

3,定义获取对应的员工的ReslutMap

<resultMap id="employeeResultMap" type="Employee">
        <id column="id" property="id"/>
        <!--一个员工对应一个卡号,association表示我关联了一个其他的-->
        <association property="employeeCard" column="id" select="com.vgxit.learn.vgmybatis.ktdm.mapper.EmployeeCardMapper.getByEmployeeId"/>
        <!--一个员工对应多个任务,collection表示我关联了多个其他的-->
        <collection property="tasks" column="id" select="com.vgxit.learn.vgmybatis.ktdm.mapper.TaskMapper.findByEmployeeId"/>
    </resultMap>

案例二:通过任务获取对应的员工(多对一)

1,在Task的PO里面添加一个属性Employee

private Employee employee;

2,TaskMapper.xml定义对应的ResultMap

<resultMap id="taskResultMap" type="Task">
        <id property="id" column="id"/>
        <association property="employee" column="employee_id" select="com.vgxit.learn.vgmybatis.ktdm.mapper.EmployeeMapper.getById"/>
    </resultMap>

3,获取所有task的sql

List<Task> all();
<select id="all" resultMap="taskResultMap">
        select * from task
    </select>

5,延迟加载

上面我们使用的级联的方式,其实是有很大问题的。

private static void testOne2One() throws IOException {
        try (SqlSession sqlSession = MybatisTool.getSqlSession()){
            EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
            Employee employee = employeeMapper.getById(1);
            System.out.println(employee.getName());
        }
    }

运行结果如下:

我们在这里,只是需要获取到员工的名字,我们就加载员工的信息就好了啊。但是mybatis自动给我们查询了对应的卡号,和对应的任务。这个时候就多发了两条sql去查询数据库。但是其实上卡号信息和任务信息对我们来说完全没用。这个问题怎么解决?

这个时候,我们就需要使用mybatis的延迟加载技术。这个就是相当于,我们需要使用到对应的级联数据的时候,我们才发sql去数据库查询加载。那什么时候属于我们需要使用呢?mybatis通过我们有没有调用级联属性的get方法来判断。

我们要实现可以通过在mybatis-config.xml中配置两个元素来解决:

  • lazyLoadingEnabled:延迟加载的全局开关。只有当开启的时候,所有关联的对象都会使用延迟加载。可用值是true/false。默认是false
  • aggressiveLazyLoading:当启用的时候,对于任意延迟属性的调用会使用带有延迟加载属性的对象完整加载。可用值有true/false。在3.4.1版本之前默认是true,之后是false。

案例:

1,编写通过Id获取任务的代码:

Task getById(int id);
<resultMap id="taskResultMap" type="Task">
        <id property="id" column="id"/>
        <association property="employee" column="employee_id" select="com.vgxit.learn.vgmybatis.ktdm.mapper.EmployeeMapper.getById"/>
    </resultMap>
<select id="getById" resultMap="taskResultMap" parameterType="int">
        select * from task where id=#{id}
    </select>

2,具体测试代码:

public static void testLazy() throws IOException {
        try (SqlSession sqlSession = MybatisTool.getSqlSession()) {
            TaskMapper taskMapper = sqlSession.getMapper(TaskMapper.class);
            Task task = taskMapper.getById(1);
            System.out.println(task.getTitle());
        }
    }

3,我们查看对应的Sql:

是加载了很多无用信息

4,设置lazyLoadingEnabled开启延迟加载:

<setting name="lazyLoadingEnabled" value="true"/>

然后再次运行:

我们看到就只获取到了任务,其他无用信息一概没有获取到。

5,如果是我们获取对应的employee的name呢?

加载员工的时候,没有把员工对应的其他信息加载出来,是比较好的

如果我们设置了aggressiveLazyLoading为true呢?

<setting name="aggressiveLazyLoading" value="true"/>

获取员工的时候又把没有必要的信息加载出来了。这里老师建议大家最好就不要去aggressiveLazyLoading,直接让其用默认值就好了。


6,多对多关联:

在Mybatis中,没有对多关联。但是现实生活中,又存在多对多关联的情况,比如我们发文章,一篇文章可以有多个标签,而一个标签又可以有多篇文章。

接下来,我们用文章和标签做例子,定义对应的表。

CREATE TABLE `article` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
  `title` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '标题',
  `content` text COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '对应的内容',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='文章表';

CREATE TABLE `tag` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增Id',
  `name` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '标签名',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE `tag_article` (
  `tag_id` int(10) unsigned NOT NULL COMMENT '对应的标签Id',
  `article_id` int(11) NOT NULL COMMENT '对应的文章Id'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

然后接下来我们初始化数据:

INSERT INTO `tag` (`name`) VALUES ('美女');
INSERT INTO `tag` (`name`) VALUES ('帅哥');
INSERT INTO `tag` (`name`) VALUES ('小姐姐');
INSERT INTO `tag` (`name`) VALUES ('小哥哥');
INSERT INTO `article` (`title`, `content`) VALUES ('文章1', '1111');
INSERT INTO `article` (`title`, `content`) VALUES ('文章2', '2222');
INSERT INTO `article` (`title`, `content`) VALUES ('文章3', '333');
INSERT INTO `tag_article` (`tag_id`, `article_id`) VALUES (1, 1);
INSERT INTO `tag_article` (`tag_id`, `article_id`) VALUES (1, 2);
INSERT INTO `tag_article` (`tag_id`, `article_id`) VALUES (2, 2);
INSERT INTO `tag_article` (`tag_id`, `article_id`) VALUES (2, 3);
INSERT INTO `tag_article` (`tag_id`, `article_id`) VALUES (3, 3);
INSERT INTO `tag_article` (`tag_id`, `article_id`) VALUES (3, 1);
INSERT INTO `tag_article` (`tag_id`, `article_id`) VALUES (4, 3);
INSERT INTO `tag_article` (`tag_id`, `article_id`) VALUES (4, 3);

然后创建Article对应的PO:

package com.vgxit.learn.vgmybatis.ktdm.po;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

/**
 * 文章对应的PO
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Article {
    private Integer id;
    private String title;
    private String content;
    private List<Tag> tags;
}

再创建标签对应的PO:

package com.vgxit.learn.vgmybatis.ktdm.po;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;

/**
 * 标签对应的PO
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Tag {
    private Integer id;
    private String name;
    private List<Article> articles;
}

然后构建对应的ArticleMapper:

package com.vgxit.learn.vgmybatis.ktdm.mapper;

import com.vgxit.learn.vgmybatis.ktdm.po.Article;
import org.apache.ibatis.annotations.Param;

/**
 * 文章对应的Mapper
 */
public interface ArticleMapper {
    /**
     * 通过Id获取一篇文章
     * @param id
     * @return
     */
    Article getById(@Param("id") int id);
}

<!DOCTYPE mapper
        PUBLIC "-//rnybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vgxit.learn.vgmybatis.ktdm.mapper.ArticleMapper">
    <resultMap id="articleResultMap" type="Article">
        <id column="id" property="id"/>
        <collection property="tags" column="id" select="com.vgxit.learn.vgmybatis.ktdm.mapper.TagMapper.findByArticleId"/>
    </resultMap>
    <select id="getById" resultMap="articleResultMap">
        select * from article where id=#{id}
    </select>
</mapper>

然后构建对应的Tag的Mapper:

package com.vgxit.learn.vgmybatis.ktdm.mapper;

import com.vgxit.learn.vgmybatis.ktdm.po.Tag;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface TagMapper {
    List<Tag> findByArticleId(@Param("articleId") int articleId);
}

<!DOCTYPE mapper
        PUBLIC "-//rnybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vgxit.learn.vgmybatis.ktdm.mapper.TagMapper">
    <select id="findByArticleId" resultType="com.vgxit.learn.vgmybatis.ktdm.po.Tag">
        select t.* from tag t left join tag_article ta on t.id=ta.tag_id where ta.article_id=#{articleId}
    </select>
</mapper>

对应的测试代码:

private static void testMany2Many() throws IOException {
        try (SqlSession sqlSession = MybatisTool.getSqlSession()) {
            ArticleMapper articleMapper = sqlSession.getMapper(ArticleMapper.class);
            Article article = articleMapper.getById(1);
            article.getTags().forEach(tag -> System.out.println(tag.getName()));
        }
    }

7,基于连表查询的级联:

我们上面的这种级联在开发的时候还是尽量的避免使用比较好,因为上面的这种级联方式存在很大的性能问题。比如我们有一个需求,我们要查询所有的标签和这些标签对应的文章。

1,首先在TagMapper里面定义对应的查询方法:

List<Tag> all();
<resultMap id="tagResultMap" type="Tag">
        <id column="id" property="id"/>
        <collection property="articles" column="id" select="com.vgxit.learn.vgmybatis.ktdm.mapper.ArticleMapper.getByTagId"/>
    </resultMap>
    <select id="all" resultMap="tagResultMap">
        select * from tag
    </select>

2,在ArticleMapper里面定义通过标签Id查询文章的方法:

List<Article> getByTagId(@Param("tagId") int tagId);
<select id="getByTagId" resultType="com.vgxit.learn.vgmybatis.ktdm.po.Article">
        select a.* from article a left join tag_article ta on ta.article_id=a.id where ta.tag_id=#{tagId}
    </select>

3,具体调用:

private static void testMany2Many() throws IOException {
        try (SqlSession sqlSession = MybatisTool.getSqlSession()) {
            TagMapper tagMapper = sqlSession.getMapper(TagMapper.class);
            List<Tag> tags = tagMapper.all();
            tags.forEach(tag -> tag.getArticles().forEach(article -> System.out.println(article.getTitle() + "------" + article.getContent())));
        }
    }

4,运行结果:

我们可以看到,这里,获取tag对应的文章的时候,mybatis是循环的发送sql去查询对应的文章。这里我们的tag只有4条数据。但是在真实的开发中,我们的tag可能有几百条数据。这个时候我们是不是就发送了几百条去查询对应的文章呢?这个时候效率是非常低的。我们在开发的时候,绝对的,一定要杜绝在循环里面去发送sql。

老师告诉一下大家,我们这个时候就可以使用连接查询的方式来解决级联的问题。

我们再定义查询标签的方法:

List<Tag> allWithArticle();

然后,我们定义对应的ResultMap和Sql:

<resultMap id="tagResultMap" type="Tag">
        <id column="t_id" property="id"/>
        <result column="t_name" property="name"/>
        <!--如果是一对一关联这个时候我们使用的association标签,我们指定类型用javaType。如果是一对多关联的时候,我们使用collection,这个时候指定类型用ofType-->
        <collection property="articles" ofType="Article">
            <id column="a_id" property="id"/>
            <result column="a_title" property="title"/>
            <result column="a_content" property="content"/>
        </collection>
    </resultMap>
<select id="allWithArticle" resultMap="tagResultMap">
        select
            t.id as t_id,
            t.name as t_name,
            a.id as a_id,
            a.title as a_title,
            a.content as a_content
        from
            tag t
        left join
            tag_article ta on ta.tag_id = t.id
        left join
            article a on a.id=ta.article_id
    </select>

然后,接下来,我们用新方法测试:

我们可以看到只发了一条sql就查询出来了所有的数据。


8,基于Annotation的级联:

我们通过上面的学习可以发现一个问题,就是级联的实现其实重点就是定义ResultMap。我们用一个例子来讲解Annotation实现级联就好了。我们这个例子通过标签来获取对应的文章。

1,通过注解定义Mapper

package com.vgxit.learn.vgmybatis.ktdm.mapper;

import com.vgxit.learn.vgmybatis.ktdm.po.Tag;
import org.apache.ibatis.annotations.*;
import java.util.List;

public interface TagAnnMapper {
    @Select("select * from tag")
    //注解方式定义ResultMap就是用的Results
    @Results(id = "tagAnnResultMap", value = {
            @Result(id = true, property = "id", column = "id"),
            @Result(property = "articles", column = "id", many = @Many(
                    select = "com.vgxit.learn.vgmybatis.ktdm.mapper.ArticleMapper.findByTagId"
            ))
    })
    List<Tag> all();

    @Select("select * from tag where id=#{id}")
    @ResultMap("tagAnnResultMap")
    Tag getById(int id);
}

2,调用:

private static void testAnnRelation() throws IOException {
        try (SqlSession sqlSession = MybatisTool.getSqlSession()) {
            TagAnnMapper tagAnnMapper = sqlSession.getMapper(TagAnnMapper.class);
//            List<Tag> tags = tagAnnMapper.all();
//            tags.forEach(tag -> tag.getArticles().forEach(article -> System.out.println(article.getTitle() + "------" + article.getContent())));
            Tag tag = tagAnnMapper.getById(1);
            tag.getArticles().forEach(article -> System.out.println(article.getTitle()));
        }
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容