MyBatis的关联映射

Mybatis的关联映射

实际的开发中,对数据库的操作常常会涉及到多张表,这在面向对象中就涉及到了对象与对象之间的关联关系。针对多表之间的操作,MyBatis提供了关联映射,通过关联映射就可以很好的处理对象与对象之间的关联关系。

1.关联关系概述

在关系型数据库中,多表之间存在着三种关系,分别是一对一,一对多,多对多。

  • 一对一:在任意一个表中引入另外一个表的主键作为外键。

  • 一对多:在多个表中都引入了某一个表的主键作为外键

  • 多对多:需要用一张中间表表示多对多的关系,这张中间表引入两张表的主键作为外键。

一般来说一个对象映射一张表,因此一对一的关系就是在A类中定义B类属性,一对多的关系就是在A类中定义List< B> 的属性,多对多就是分别在A、B类中定义对方的List 属性。

2.一对一

一对一关系是一个基本的映射关系,比如Person(人)--IDCard(身份证),我们可以通过如下两种方式实现:

  1. 通过配置XxxMapper.xml实现1对1 [配置方式]
  2. 通过注解的方式实现1对1 [注解方式]

2.1配置方式

2.1.1环境搭建

配置映射文件来实现一对一的映射关系,实现级联查询,要求通过person可以获取到对应的idencard信息

关于级联查询:若表A中有一个外键引用了表B的主键,A表就是子表,B表就是父表。当查询表A的数据时,通过表A的外键将表B的记录也查找出来,这就是级联查询。相应的还有级联删除,当删除B表的记录时,会先将A表中关联的记录删掉

(1)person表和 idencard表

-- 创建 idencard 表
-- 记录身份证
CREATE TABLE `idencard`(
`id` INT PRIMARY KEY AUTO_INCREMENT,
`card_sn` VARCHAR(32) NOT NULL DEFAULT ''
)CHARSET utf8;
INSERT INTO `idencard` VALUES(1,'123456789098765'); -- 创建person表
CREATE TABLE `person`(
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(32) NOT NULL DEFAULT '',
`card_id` INT, -- 对应idencard表的主键-id
FOREIGN KEY (`card_id`) REFERENCES idencard (`id`)-- card_id作为外键
)CHARSET utf8;
INSERT INTO `person` VALUES(1,'张三',1);

(2)实体类 IdenCard 和 Person

package com.li.entity;

/**
* @author 李
* @version 1.0
*/
public class IdenCard {
private Integer id;
private String card_sn;
//省略setter,getter,toString方法
}
package com.li.entity;

/**
* @author 李
* @version 1.0
*/
public class Person {
private Integer id;
private String name;
private IdenCard card;
//省略setter,getter,toString方法
}

(3)PersonMapper 接口

package com.li.mapper;

import com.li.entity.Person;

/**
* @author 李
* @version 1.0
*/
public interface PersonMapper {
//通过Person的id获取到Person,包括这个Person关联的IdenCard对象(级联操作)
public Person getPersonById(Integer id);
}

2.1.2方式1:多表联查

mybatis – MyBatis 3 | XML 映射器

PersonMapper.xml映射文件实现级联查询,实现方法是使用多表联查,返回的数据通过resultMap结果映射

<mapper namespace="com.li.mapper.PersonMapper">
<!--1.接口声明:public Person getPersonById(Integer id);
2.通过Person的id获取到Person,包括这个Person关联的IdenCard对象(级联操作)
3.返回类型如果配置成resultType="Person",不能实现级联查询,
在返回的person对象中IdenCard属性对象为 null
4.因此需要使用自定义resultMap,在resultMap中指定级联关系-->
<select id="getPersonById" parameterType="Integer" resultMap="PersonResultMap">
SELECT * FROM `person`,`idencard` WHERE `person`.`id`= #{id} AND
`person`.`card_id` = `idencard`.`id`;
</select> <!--association – 一个复杂类型的关联;许多结果将包装成这种类型嵌套结果映射 – 关联可以是
resultMap 元素,或是对其它结果映射的引用
1.property="card" 表示 Person对象的card属性
2.javaType="IdenCard" 表示card属性的类型-->
<resultMap id="PersonResultMap" type="Person">
<!--id标签–一个ID结果(就是主键);标记出作为主键的结果可以帮助提高整体性能-->
<!--这里的property表示Person类的属性名,column表示对应表的字段-->
<id property="id" column="id"/>
<result property="name" column="name"/>
<association property="card" javaType="IdenCard">
<!--这里的property表示IdenCard类的属性名,column表示表的字段名-->
<result property="id" column="id"/>
<result property="card_sn" column="card_sn"/>
</association>
</resultMap>
</mapper>

测试:

@Test
public void getPersonById() {
Person person = personMapper.getPersonById(1);
System.out.println("person=" + person);
if (sqlSession != null) {
sqlSession.close();
}
}

2.2.3方式2:分解为多次单表操作(推荐使用)

第一种方式使用了多表联查的形式实现级联查询,但是如果涉及的表过多,sql语句可读性就会变差。第二种方式的核心思想是将多表联查分解成单表操作,这样更简洁,易于维护,而且可以复用你写好的方法,推荐使用

(1)创建IdenCardMapper接口

public interface IdenCardMapper {
//根据id获取到身份证序列号
public IdenCard getIdenCardById(Integer id);
}

(2)在IdenCardMapper的映射文件中实现该方法

<mapper namespace="com.li.mapper.IdenCardMapper">
<!--配置实现public IdenCard getIdenCardById(Integer id);-->
<select id="getIdenCardById" parameterType="Integer" resultType="IdenCard">
SELECT * FROM `idencard` WHERE `id` = #{id}
</select>
</mapper>

(3)PersonMapper接口

//通过Person的id获取到Person,包括这个Person关联的IdenCard对象(方式2)
public Person getPersonById2(Integer id);

(4)实现PersonMapper接口的映射文件

   1. 先通过 SELECT * FROM person WHERE id =#{id} 返回 person 信息
2. 以第一个操作返回的 card_id 字段数据,作为条件再次查询,得到对应的 IdenCard 数据

如果第一个操作使用了别名,那么返回的时候的字段也是别名,因此第二个操作也要使用别名才能匹配到

<!--通过Person的id获取到Person,包括这个Person关联的IdenCard对象(方式2)
接口方法:public Person getPersonById2(Integer id);-->
<resultMap id="PersonResultMap2" type="Person">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!--第二种方式的核心思想是将多表联查操作分解成单表操作,
这样更简洁,易于维护,复用性更强,推荐使用-->
<!--1.property="card"表示Person对象的card属性
2.column="card_id"是SELECT * FROM person WHERE id = #{id}语句返回的card_id字段名/别名
3.返回的字段card_id信息/数据会作为getIdenCardById()的入参来执行方法-->
<association property="card" column="card_id"
select="com.li.mapper.IdenCardMapper.getIdenCardById"/>
</resultMap>
<select id="getPersonById2" parameterType="Integer" resultMap="PersonResultMap2">
SELECT * FROM person WHERE id = #{id};
</select>

测试结果:

可以看到底层执行了两次sql查询操作。首先对person表进行查询,查询结果(card_id)作为第二张表的查询条件(id),再对idencard表进行查询。

2.2注解方式

通过注解的方式来实现一对一的映射关系,实现级联查询,通过person可以获取到对应的idencard的信息。这里只进行方式二的演示。

在实际开发中还是推荐使用配置方式

(1)注解实现方法

IdenCardMapperAnnotation 接口:

package com.li.mapper;

import com.li.entity.IdenCard;
import org.apache.ibatis.annotations.Select; /**
* @author 李
* @version 1.0
* 使用注解的方式实现一对一的映射
*/
public interface IdenCardMapperAnnotation {
//根据id获取到身份证序列号
@Select(value = "SELECT * FROM `idencard` WHERE `id` = #{id}")
public IdenCard getIdenCardById(Integer id);
}

PersonMapperAnnotation 接口:

package com.li.mapper;

import com.li.entity.Person;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select; /**
* @author 李
* @version 1.0
*/
public interface PersonMapperAnnotation {
//通过Person的id获取到Person,包括这个Person关联的IdenCard对象
//注解的形式就是对前面xml配置方式的体现
@Select(value = "SELECT * FROM person WHERE id = #{id}")//如果这里返回的字段使用了别名,则@result的card_id也要使用该别名
@Results({//配置返回数据的映射
@Result(id = true, property = "id", column = "id"),
@Result(property = "name", column = "name"),
@Result(property = "card", column = "card_id",
one = @One(select = "com.li.mapper.IdenCardMapper.getIdenCardById"))
})
public Person getPersonById(Integer id);
}

(2)测试

@Test
public void getIdenCardById() {
Person person = personMapperAnnotation.getPersonById(1);
System.out.println("person=" + person);
if (sqlSession != null) {
sqlSession.close();
}
}

2.3注意事项

一张表是否设置了外键,对MyBatis进行对象级联映射没有影响,外键只是对表本身数据的约束

2.4练习

前面我们讲解的是查询Person可以级联查询到IdenCard,如果要求通过查询IdenCard,也可以级联查询到Person,应该如何解决?

day07-MyBatis的关联映射01的更多相关文章

  1. 04—mybatis的关联映射

    mybatis的关联映射一对一一对多多对多 一.一对一(一个人只能有一个身份证号) 1.创建表创建表tb_card CREATE TABLE `tb_card` ( `id` int(11) NOT ...

  2. mybatis 一对一关联映射实例

    在实际项目开发中,经常存在一对一的关系,如一个人对应一张身份证信息,这就是一对一的关系.下面是一个简单的实例: 1.建表过程我就省略了,主要是一张Person表,一张IDCard表,其相关属性见步骤2 ...

  3. mybatis之关联映射

    ###mybatis使用之一对一关联映射 1)分析并画ER图.(特别是一对一.一对多.多对多的情况) 2)启动终端数据库,并建库建表,在表中插入值和字段,并查看结果.(后期把navicat用上) 3) ...

  4. Mybatis的关联映射案例

    主要是对之前学习的关联映射做一个案例,自己动手实践一下,可以理解的更好一点. 开发环境 开发工具:idea Java环境: jdk1.8.0_121 数据库:SQLServer 项目结构,里面包含了三 ...

  5. Mybatis的关联映射

    实际的开发中,对数据库的操作常常会涉及到多张表,这在面向对象中就涉及到了对象与对象之间的关联关系.针对多表之间的操作,MyBatis提供了关联映射, 通过关联映射就可以很好的处理对象与对象之间的关联关 ...

  6. MyBatis的关联映射和动态SQL

    CREATE TABLE tb_card ( id INT PRIMARY KEY AUTO_INCREMENT, CODE ) ); '); CREATE TABLE tb_person ( id ...

  7. 【Hibernate框架】关联映射(一对一关联映射)

    一.整理思路: 之前,小编总结过Mybatis的关联映射,接下来,再来总结一下hibernate的相关的关联映射,直接上图: 这张图,就是小编整理总结整个Hibernate的关联映射的一个大致思路. ...

  8. Mybatis(四)关联映射

    一. Mybatis关联映射 1 讲数据库中关联关系,主键表与外键表 一对多,多对一,主键表表示一 与外键表表示多 学生与学校的关系,工人与工厂,人员与部门的关系,.... 多        一    ...

  9. SSM框架开发web项目系列(三) MyBatis之resultMap及关联映射

    前言 在上篇MyBatis基础篇中我们独立使用MyBatis构建了一个简单的数据库访问程序,可以实现单表的基本增删改查等操作,通过该实例我们可以初步了解MyBatis操作数据库需要的一些组成部分(配置 ...

  10. MyBatis学习(七)MyBatis关联映射之多对多映射

    对于数据库中的多对多关系建议使用一个中间表来维护关系. 1.创建四张表,分别为用户表,商品表,订单表,中间表. DROP TABLE IF EXISTS `t_user`; CREATE TABLE ...

随机推荐

  1. java (String)强制转换与toString()方法

    1. Object.toString()介绍 Object中是自带有toString()方法的,也就是说java中的所有类的对象都是可以转换为字符串的. 首先,先看看Object.toString() ...

  2. Effective C++试读笔记

    Part1 习惯C++ 1. 视C++为一个语言联邦 C++非常的屌,除了开发效率和编译效率不高,其他的都非常屌 C++ 可以视为一系列的语言联邦构成的紧密结合体,分为以下四个部分 C 2.C wit ...

  3. 启动springboot项目报错Unable to start embedded Tomcat

    1.问题描述 最近在学习springcloud的时候,在父工程下新建一个model后,引入dashboard相关依赖后启动报错 2.产生原因 产生原因有可能就是pom.xml中下载的jar包版本冲突 ...

  4. [能源化工] TE田纳西-伊斯曼过程数据集

    TE田纳西-伊斯曼过程数据集简介 TE数据集是现在故障诊断中的应用较多的一种数据集.主要介绍论文上都有. 具体介绍见:http://depts.washington.edu/control/LARRY ...

  5. [R语言] WGCNA入门教程

    文章目录 wgcna入门-雌性小鼠肝脏表达数据的网络分析:寻找与体重有关的模块 1 数据输入和清洗 1.1 加载基因表达数据 1.2 数据清洗 1.3 加载临床特征数据 2 建设表达网络与模块检测 2 ...

  6. python进阶之路13 二分法 三元表达式 各种生成式 匿名函数

    算法简介及二分法 1.什么是算法 算法就是解决问题的有效方法 不是所有的算法都很高效也有不合格的算法 2.算法应用场景 推荐算法(抖音视频推送 淘宝商品推送) 成像算法(AI相关)...... 几乎涵 ...

  7. Educational Codeforces Round 33 (Rated for Div. 2) 虚拟赛体验

    前言 就只做出了 \(A,B,C,D\) 是不是很弱? A.Chess For Three A,B,C 三人下棋,A和B先下,每次下完棋之后由现在观战的人(例如第一局就由C)代替下输的人. 每次输入一 ...

  8. 03-Verilog语法

    Verilog语法 1 Register 组合逻辑-->寄存器-->组合逻辑-->寄存器 Register是一个变量,用于存储值,并不代表一个真正的硬件DFF. reg A,C; / ...

  9. 使用Zolom内存解析运行python脚本(不落地)

    在目标机器运行python工具 好多工具都是python写的,如果目标机器是linux的话自带python环境可以很方便的运行这些工具,但是windows下是不自带python环境的,所以一种办法是直 ...

  10. 【随笔记】Tina 系统的 ADB、声卡、网卡、串口多路共存

    全志 Tina 系统的 USB Gadget 配置方法,随笔记录,实测为 R311 平台,其它平台应该通用.  一.配置内核 二.编译驱动并加载 # UAC insmod usb_f_uac1.ko ...