当前位置:   article > 正文

九、MyBatis的关联映射(1)

九、mybatis的关联映射

目录

一、关联关系概述

二、一对一

1、嵌套查询

 2、嵌套结果

3、MyBatis延迟加载的配置


一、关联关系概述

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

   (1) 在关系型数据库中,多表之间存在着三种关联关系,分别为一对一、一对多和多对多,如下图所示:

 这三种关联关系的具体说明如下。

  • 一对一:在任意一方引入对方主键作为外键。
  • 一对多:在“多”的一方,添加“—”的一方的主键作为外键。
  • 多对多:产生中间关系表,引入两张表的主键作为外键,两个主键成为联合主键或使用的字段作为主键。

(2)在Java中,通过对象也可以进行关联关系描述,如图下图所示:

三种关联关系的描述如下。

  • 一对一的关系:就是在本类中定义对方类型的对象,如A类中定义B类类型的属性b,B类中定义A类类型的属性a。
  • 一对多的关系:就是一个A类类型对应多个B类类型的情况,需要在A类中以集合的方式引入B类类型的对象,在B类中定义A类类型的属性a。
  • 多对多的关系:在A类中定义B类类型的集合,在B类中定义A类类型的集合。

二、一对一

在现实生活中,一对一关联关系是十分常见的。例如,一个人只能有一个身份证,同时一个身份证也只会对应一个人。那么使用MyBatis是怎么处理图中的这种一对一关联关系的呢?<resultMap>元素中,包含了一个<association>子元素,MyBatis就是通过该元素来处理一对一关联关系的。

 在<association>元素中,通常可以配置以下属性:

  • property:指定映射到的实体类对象属性,与表字段一一对应。
  • column:指定表中对应的字段。
  • javaType:指定映射到实体对象属性的类型。
  • select:指定引入嵌套查询的子SQL语句,该属性用于关联映射中的嵌套查询。
  •  fetchType:指定在关联查询时是否启用延迟加载。fetchType 属性有 lazy 和eager两个属性值,默认值为lazy(即默认关联映射延迟加载)。

<association>元素的使用非常简单,只需要参考如下两种示例配置即可,具体如下

<!--方式一:嵌套查询-->
<association property="card" column="card_id"
        javaType=" com.itheima. po.Idcard"
        select="com.itheima.mapper.IdCardMapper.findCodeById"/>
<!--方式二:嵌套结果-->
<association property="card" javaType="com.itheima.po.IdCard">
        <id property="id" column=" card_id"/>
        <result property="code" column="code"/>

</association>

 MyBatis加载关联关系对象主要通过两种方式:嵌套查询嵌套结果

了解了MyBatis 中处理一对一关联关系的元素和方式后,接下来就以个人和身份证之间的一对一关联关系为例,进行详细讲解。查询个人及其关联的身份证信息先通过查询个人表中的主键来获个人信息,然后通过表中的外键来获取证件表中的身份证号信息。其具体实现步骤如下。

1、嵌套查询


(1)创建数据表。在mybatis 数据库中分别创建名为tb_idcard和tb_person的数据表,同时预先插入两条数据。其执行的SQL语句如下所示。

USE mybatis;
#创建一个名称为tb idcard的表
CREATE TABLE tb_idcard(
    id INT PRIMARY KEY AUTO_INCREMENT,
    CODE VARCHAR (18)
);
#插入两条数据
INSERT INTO tb_idcard(CODE) VALUES ( '152221198711020624');
INSERT INTO tb_idcard(CODE)VALUES('152201199008150317');
#创建一个名称为tb_person的表
CREATETABLE tb_person (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(32),
    age INT,
    sex VARCHAR(8),
    card id INT UNIQUE,
    FOREIGN KEY(card id) REFERENCES tb_idcard(id)
);
#插入两条数据
INSERT INTO tb_person (name,age, sex, card_id)VALUES ( 'Rose',29, '女',1);
INSERT INTO tb_person(name, age,sex,card_id)VALUES ( 'tom' ,27,'男',2);

( 2)在 Eclipse 中创建一个名为chapter09的 Web 项目,然后引入相关JAR包、log4j日志文件、MybatisUtils工具类以及mybatis-config.xml核心配置文件。项目环境搭建完成后的文件结构,如图所示。

 (3)在项目的com.itheima.po包下创建持久化类ldCard和Person,编辑后的代码,如下所示。

public class IdCard {
    //主键id自增
    private Integer id;
    //身份证号码
    private String code;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    @Override
    public String toString() {
        return "IdCard [id=" + id + ", code=" + code + "]";
    }      

}

public class Person {
    //主键id自增
    private Integer id;
    //姓名
    private String name ;
    //年龄
    private Integer age;
    //性别
    private String sex;
    //外键--个人关联的证件,关联的IdCard
    private IdCard card;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public IdCard getCard() {
        return card;
    }
    public void setCard(IdCard card) {
        this.card = card;
    }
    @Override
    public String toString() {
        return "Person [id=" + id + ", name=" + name + ", age=" + age + ", sex=" + sex + ", card=" + card + "]";
    }

}

(4 )在com.itheima.mapper包中,创建证件映射文件ldCardMapper.xml和个人映射文件PersonMapper.xml,并在两个映射文件中编写一对一关联映射查询的配置信息,如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <!--namespace名称空间实际上是当前的xml的包路径  -->
<mapper namespace="com.itheima.mapper.IdCardMapper">
    <!--根据id查询证件信息-->
    <select id="findCodeById" parameterType="Integer"
        resultType="IdCard">
        SELECT * from tb_idcard where id=#{id}
    </select>
</mapper>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <!--namespace名称空间实际上是当前的xml的包路径  -->
<mapper namespace="com.itheima.mapper.PersonMapper">
    <!--嵌套查询-->
    <select id="findPersonById" parameterType="Integer"
        resultMap="IdCardWithPersonResult">
        select * from tb_person where id = #{id}
    </select>
    
    <resultMap type="person" id="IdCardWithPersonResult">
        <!-- 主键 -->
        <id property= "id" column= "id" />
        <result property= "name" column= "name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>

        <!--一对一:association使用select属性引入另外一条SQL语句-->
        <association property="card" column="card_id" javaType="IdCard"                 select="com.itheima.mapper.IdCardMapper.findCodeById">

        </association>
    </resultMap>

</mapper>

 ( 5)在核心配置文件 mybatis-config.xml 中,引入Mapper映射文件并定义别名

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-config.dtd">
 <!-- 上面是mybatis的核心约束 -->
<configuration><!-- configuration根 -->

    <!--引入数据库连接配置文件-->
    <properties resource="db.properties"></properties>

    <!--使用扫描包的形式定义别名-->
    <typeAliases>
        <package name="com.qingcheng.ssm.po"/>
    </typeAliases>

    <environments default="mysql"><!--配置环境,现在是mysql  -->
        <environment id="mysql">
        <!-- 使用JDBC的事务管理 -->
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${driver}" />
                <property name="url" value="${url}" />
                <property name="username" value="${username}" />
                <property name="password" value="${password}" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
          <mapper resource="com/qingcheng/ssm/mapper/IdCardMapper.xml" />
          <mapper resource="com/qingcheng/ssm/mapper/PersonMapper.xml" />
    </mappers>

</configuration>

(6)在 com.itheima.test包中,创建测试类MybatisAssociatedTest,并在类中编写测试方法findPersonByldTest()

@Test
    public void findPersonByIdTest() {
        SqlSession sqlSession = MybatisUtils.getSession();
        Person person = sqlSession.selectOne("com.itheima.mapper.PersonMapper.findPersonById", 1);
        System.out.println(person);
        sqlSession.close();
    }

使用JUnit4执行findPersonByldTest()方法后,控制台的输出结果如图

 从图可以看出,使用MyBatis嵌套查询的方式查询出了个人及其关联的身份证信息,这就是MyBatis 中的一对一关联查询。
虽然使用嵌套查询的方式比较简单,但是从图中可以看出,MyBatis嵌套查询的方式要执行多条SQL语句,这对于大型数据集合和列表展示不是很好,因为这样可能会导致成百上千条关联的SQL语句被执行,从而极大地消耗数据库性能并且会降低查询效率。这并不是开发人员所期望的。为此,我们可以使用MyBatis提供的嵌套结果方式,来进行关联查询。

 2、嵌套结果

在PersonMapper.xml中,使用MyBatis嵌套结果的方式进行个人及其关联的证件信息查询,所添加的代码如下所示。

<!-- 嵌套结果 -->
    <select id="findPersonById2" parameterType="Integer"
        resultMap="IdCardWithPersonResult2">
        select p.*,idcard.code

        from tb_person p,tb_idcard idcard
        where p.card_id=idcard.id

        and p.id = #{id}
    </select>
    
    <resultMap type="Person" id="IdCardWithPersonResult2">
        <!-- 主键 -->
        <id property= "id" column= "id" />
        <result property= "name" column= "name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <association property="card" javaType="IdCard" >
            <id property= "id" column= "card_id" />
            <result property= "code" column= "code"/>
        </association>

    </resultMap>

从上述代码中可以看出,MyBatis 嵌套结果的方式只编写了一条复杂的多表关联的SQL语句,并且在<association>元素中继续使用相关子元素进行数据库表字段和实体类属性的一―映射。
在测试类MybatisAssociatedTest中,编写测试方法 findPersonByldTest2(),其代码如下所示。

@Test
    public void findPersonByIdTest2() {
        SqlSession sqlSession = MybatisUtils.getSession();
        Person person = sqlSession.selectOne("com.itheima.mapper.PersonMapper.findPersonById2", 1);
        System.out.println(person);
        sqlSession.close();
    }

使用JUnit4执行findPersonByldTest2()方法后,控制台的输出结果如图

从图可以看出,使用MyBatis嵌套结果的方式只执行了一条SQL语句,并且同样查询出了个人及其关联的身份证的信息。

3、MyBatis延迟加载的配置

使用MyBatis的延迟加载在一定程度上可以降低运行消耗并提高查询效率。MyBatis默认没有开启延迟加载,需要在核心配置文件中的<settings>元素内进行配置,具体配置方式如下:

 <settings>

            <!--打开延迟加载的开关-->
            <setting name="lazyLoadingEnabled" value="true" />

            <!--将积极加载改为消息加载,即按需加载--> 
            <setting name="aggressiveLazyLoading" value="false"/>  
  </settings>

  在映射文件中,<association>元素和<collection>元素中都已默认配置了延迟加载属性,即默认属性fetchType="lazy"(属性fetchType="eager"表示立即加载),所以在配置文件中开启延迟加载后,无需在映射文件中再做配置。


 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/625694
推荐阅读
相关标签
  

闽ICP备14008679号