级联关系
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()));
}
}