4

day07-MyBatis的关联映射01 - 一刀一个小西瓜

 1 year ago
source link: https://www.cnblogs.com/liyuelian/p/17166430.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

MyBatis的关联映射

Mybatis的关联映射

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

1.关联关系概述

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

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

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

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

一般来说一个对象映射一张表,因此一对一的关系就是在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表的主键-idFOREIGN 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>
@Testpublic void getPersonById() { Person person = personMapper.getPersonById(1); System.out.println("person=" + person); if (sqlSession != null) { sqlSession.close(); }}
image-20230228194043902

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表进行查询。

image-20230228204133095

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)测试

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

2.3注意事项

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

2.4练习

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


Recommend

  • 9
    • www.cnblogs.com 2 years ago
    • Cache

    day30-注解 - 一刀一个小西瓜

    Java注解 1.注解的理解 注解(Annotation)也被称为元数据(Metadata),用于修饰解释 包、类、方法、属性、构造器、局部变量等数据信息 和注释一样,注解不影响程序逻辑,但注解可以被编译或者运...

  • 2

    线程基础01 1.程序 进程 线程 程序(program):是为完成的特定任务,用某种语言编写的一组指令的集合。简单来说,就是我们写的代码。

  • 5
    • www.cnblogs.com 2 years ago
    • Cache

    day35-IO流02 - 一刀一个小西瓜

    JavaOI流02 4.常用的类 4.1文件字节流输入流-FileInputStream InputStream抽象类是所有类字节输入流的超类 InputStream常用的...

  • 6
    • www.cnblogs.com 2 years ago
    • Cache

    day36-IO流03 - 一刀一个小西瓜

    JavaIO流03 4.常用的类02 4.4节点流和处理流

  • 7
    • www.cnblogs.com 2 years ago
    • Cache

    day38-IO流05 - 一刀一个小西瓜

    JavaIO流05 4.常用的类04 4.4节点流和处理流03 4.4.8打印流-PrintStream和PrintWriter 打印流只有输出流,没有输...

  • 5

    多用户即时通讯系统05 4.编码实现04(拓展) 拓展功能: 实现离线留言,如果某个用户不在线 ,当登陆后,可以接收离线的消息 实现离线发文件,如果某个功能没有在线,当登...

  • 5

    MySQL基础知识02 4.CRUD 数据库CRUD语句:增(create)、删(delete)、改(update)、查(Retrieve) Insert 语句 (添加数据) Update 语句(更新数据) Delete 语句(删除数据)...

  • 6

    JDBC和连接池05 11.BasicDAO 先来分析一个问题 前面我们使用了Apache-DBUtils和Druid简化了JDBC开发,但仍存在以下不足: SQL语句是固定的,不能通过参数传入,通用性不好...

  • 5

    Spring管理Bean-IOC-05 3.基于注解配置bean 3.3自动装配 基本说明: 基于注解配置bean,也可以实现自动装配,使用的注解是:@Aut...

  • 3

    MyBatis缓存 mybatis – MyBatis 3 | cache

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK