zoukankan      html  css  js  c++  java
  • MyBatis的关联映射

    学习目标

    ● 了解数据表之间以及对象之间的三种关联关系

    ● 熟悉关联关系中的嵌套查询和嵌套结果

    ● 掌握一对一、一对多和多对多关联映射的使用

    1.1关联关系概述

    在关系型数据库中,多表之间存在着三种关联关系,分别为一对一、一对多和多对多

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

    · 一对一:在任意一方引入对方主键作为外键。

    · 一对多:在“多”的一方,添加“一”的一方的主键作为外键。

    · 多对多:产生中间关系表,引入两张表的主键作为外键,两个主键成为联合主键或使用新的字段作为主键。

    1.2一对一

    在现实生活中,一对一关联关系是十分常见的。例如,一个人只能有一个身份证,同时一个身份证也只会对应一个人,它们之间的关系模型图

    那么使用MyBatis是怎么处理这种一对一关联关系的呢?

    在<resultMap>元素中,包含了一个<association>子元素,

    MyBatis就是通过该元素来处理一对一关联关系的。在<association>元素中,

    通常可以配置以下属性。

    · property:指定映射到的实体类对象属性,与表字段一一对应。

    · column:指定表中对应的字段。

    · javaType:指定映射到实体对象属性的类型。

    · select:指定引入嵌套查询的子SQL语句,该属性用于关联映射中的嵌套查询。

    · fetchType:指定在关联查询时是否启用延迟加载。fetchType属性有lazy和eager两个属性值,默认值为lazy(即默认关联映射延迟加载)。

    为了更好的说明一对一的关系我们先创建两个数据表作为演示使用

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

    #创建一个名称为tb_idcard的表
    create table tb_idcard(
        id int primary key auto_increment,
        code varchar(18)
    );
    
    #插入两条数据
    insert into tb_idcard (code) values ("1234567890")
    insert into tb_idcard (code) values ("0987654321")
    
    #创建一个名称为tb_person的表
    create table 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)在项目的com.itheima.po包下创建持久化类IdCard和Person,编辑后的代码,如文件

    /*
    * 证件持久化类
    */
    public class IdCard {
    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 {
      private Integer id;
      private String name;
      private Integer age;
      private String sex;
      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 + "]";
    }
    }

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

    IdCardMapper.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">
    <mapper namespace="com.itheima.mapper.IdCardMapper">
    <!-- 根据id查询证件信息 -->
    <select id="findCodeById" parameterType="Integer" resultType="com.itheima.po.IdCard">
    SELECT * from tb_idcard where id=#{id}
    </select>
    </mapper>

    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">
    <mapper namespace="com.itheima.mapper.PersonMapper">
    <!-- 嵌套查询:通过执行另外一条SQL映射语句来返回预期的特殊类型 -->
    <select id="findPersonById" parameterType="Integer"
    resultMap="IdCardWithPersonResult">
    SELECT * from tb_person where id=#{id}
    </select>
    <resultMap type="com.itheima.po.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="com.itheima.po.IdCard"
    select="com.itheima.mapper.IdCardMapper.findCodeById" />
    </resultMap>
    </mapper>

    (4)在核心配置文件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">
    <configuration>
    <!-- 1.配置环境,默认的环境id为mysql -->
     <environments default="mysql">
        <!-- 1.2配置id为mysql的数据库环境 -->
        <environment id="mysql">
           <!-- 使用JDBC的事务管理 -->
           <transactionManager type="JDBC"/>
           <!-- 数据库连接池 -->
           <dataSource type="POOLED">
              <!-- 数据库驱动 -->
               <property name="driver" value="com.mysql.jdbc.Driver"></property>
              <!-- 连接数据库 -->
              <property name="url" value="jdbc:mysql://localhost:3306/test"></property>
              <!-- 连接数据库的用户名 -->
              <property name="username" value="root"></property>
              <!-- 连接数据库的密码 -->
             <property name="password" value="root"></property>
           </dataSource>
        </environment>
     </environments>
     <!-- 2.配置Mapper的位置 -->
     <mappers>
       
       <mapper resource="com/itheima/mapper/PersonMapper.xml"></mapper>
       <mapper resource="com/itheima/mapper/IdCardMapper.xml"></mapper>
     </mappers>
    </configuration>

    (5)在com.itheima.test包中,创建测试类MybatisAssociatedTest,并在类中编写测试方法findPersonByIdTest(),如文件

    /*
     * 嵌套查询
     */
        public void findPersonByIdTest() throws Exception {
          //1.通过工具类生成SqlSession对象
          SqlSession sqlSession=MybatisUtils.getSession();
          Person person=new Person();
          //2.使用MyBatis嵌套查询的方式查询id为的人的信息
          try {
              person=sqlSession.selectOne("com.itheima.mapper"
                         +".PersonMapper.findPersonById",1);
          } catch (Exception e) {
            // TODO: handle exception
             e.printStackTrace();
          }
          //3.输出查询结果信息
          System.out.println(person.toString());
          //4.关闭SqlSession
          sqlSession.close();
        }

    使用MyBatis嵌套查询的方式查询出了个人及其关联的身份证信息,这就是MyBatis中的一对一关联查询

    虽然使用嵌套查询的方式比较简单,但是从图中可以看出,MyBatis嵌套查询的方式要执行多条SQL语句,

    这对于大型数据集合和列表展示不是很好,因为这样可能会导致成百上千条关联的SQL语句被执行,从而极大地消耗数据库性能并且会降低查询效率。

    这并不是开发人员所期望的。为此,我们可以使用MyBatis提供的嵌套结果方式,来进行关联查询。在PersonMapper.xml中,

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

    <?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">
    <mapper namespace="com.itheima.mapper.PersonMapper">
        <!-- 嵌套查询:通过执行另外一条SQL映射语句来返回预期的特殊类型 -->
        <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="com.itheima.po.Person" id="IdCardWithPersonResult2">
             <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" javaType="com.itheima.po.IdCard">
                <id property="id" column="card_id"></id>
                <result property="code" column="code"/>
             </association>
         </resultMap>
    </mapper>

     多学一招:

    MyBatis延迟加载的配置在使用MyBatis嵌套查询方式进行MyBatis关联查询映射时,

    使用MyBatis的延迟加载在一定程度上可以降低运行消耗并提高查询效率。

    MyBatis默认没有开启延迟加载,需要在核心配置文件mybatis-config.xml中的<settings>元素内进行配置,具体配置方式如下。

    <!-- 打开延迟加载的开关 -->
     <setting name="lazyLoadingEnabled" value="true"/>
     <!-- 将积极加载改为消息加载,急按需加载 -->
     <setting name="aggressiveLazyLoading" value="false"/>
     </settings>

    1.3一队多

    与一对一的关联关系相比,开发人员接触更多的关联关系是一对多(或多对一)。例如一个用户可以有多个订单,同时多个订单归一个用户所有。

    那么使用MyBatis是怎么处理这种一对多关联关系的呢?在本书第7章所讲解的<resultMap>元素中,

    包含了一个<collection>子元素,MyBatis就是通过该元素来处理一对多关联关系的。

    <collection>子元素的属性大部分与<association>元素相同,但其还包含一个特殊属性——ofType。

    ofType属性与javaType属性对应,它用于指定实体对象中集合类属性所包含的元素类型。

    在了解了MyBatis处理一对多关联关系的元素和方式后,接下来以用户和订单之间的这种一对多关联关系为例,详细讲解如何在MyBatis中处理一对多关联关系,具体步骤如下。

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

    #创建一个名称为tb_user的表
    create table tb_user(
      id int(32) primary key auto_increment,
      username varchar(32),
      address varchar(256)
    );
    
    #插入3条数据
    insert into tb_user values ('1','詹姆斯','克利夫兰');
    insert into tb_user values ('2','科比','洛杉矶');
    insert into tb_user values ('3','保罗','洛杉矶');
    
    #创建一个名称叫做tb_orders的表
    create table tb_orders(
      id int(32) primary key auto_increment,
      number varchar(32) not null,
      user_id int(32) not null,
      foreign key(user_id) references tb_user(id)
    );
    
    #插入3条数据
    insert into tb_orders values ('1','1000011','1');
    insert into tb_orders values ('2','1000012','2');
    insert into tb_orders values ('3','1000013','3');

    (2)在com.itheima.po包中,创建持久化类Orders和User,并在两个类中定义相关属性和方法

    import java.util.List;
    
    /*
     * 用户持久化类
     */
    public class User {
      private Integer id;//用户编号
      private String username;//用户姓名
      private String address;//用户地址
      private List<Orders> ordersList;//用户关联的订单
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public List<Orders> getOrdersList() {
        return ordersList;
    }
    public void setOrdersList(List<Orders> ordersList) {
        this.ordersList = ordersList;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", address=" + address + ", ordersList=" + ordersList + "]";
    }
    }
    /*
     * 订单持久化类
     */
    public class Orders {
       private Integer id;//订单id
       private String number;//订单编号
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number;
    }
    @Override
    public String toString() {
        return "Orders [id=" + id + ", number=" + number + "]";
    }
       
    }

    UserMapper.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">
    <mapper namespace="com.itheima.mapper.UserMapper">
    <!-- 一对多:查看某一用户及其关联的订单信息 -->
    <!-- 注意:当关联查询出的列名相同,则需要使用别名区分 -->
    <select id="findUserWithOrders" parameterType="Integer"
       resultMap="UserWithOrdersResult">
      select u.*,o.id as orders_id,o.number
      from tb_user u,tb_orders o
      where u.id=o.user_id
      and u.id=#{id}
    </select>
    <resultMap type="com.itheima.po.User" id="UserWithOrdersResult">
       <id property="id" column="id"></id>
       <result property="username" column="username"/>
       <result property="address" column="address"/>
       <!-- 一对多关联映射:collection
       ofType表示属性集合中元素的类型,List<Orders>属性即Orders类 -->
       <collection property="ordersList" ofType="com.itheima.po.Orders">
         <id property="id" column="orders_id"></id>
         <result property="number" column="number"/>
       </collection>
    </resultMap>
    </mapper>

     测试类:

     /*
        * 一对多    
        */
        public void findUserTest() {
            //1.通过工具类生成SqlSession对象
            SqlSession sqlSession=MybatisUtils.getSession();
            //2.查询id为1的用户信息
            User user=sqlSession.selectOne("com.itheima.mapper"
                    +".UserMapper.findUserWithOrders",1);
            //3.输出信息
            System.out.println(user.toString());
            //4.关闭sqlSession
            sqlSession.close();
        }
    一纸高中万里风,寒窗读破华堂空。 莫道长安花看尽,由来枝叶几相同?
  • 相关阅读:
    cookie和session区别
    jsp编写页面时常见错误提示
    web处理jsp文件的三个阶段
    JSP页面元素
    B/S与C/S区别
    url组成部分
    css选择器
    ol,ul,dl,table标签的基本语法
    剑指offer-把二叉树打印成多行
    剑指offer-从上往下打印二叉树
  • 原文地址:https://www.cnblogs.com/byczyz/p/12174366.html
Copyright © 2011-2022 走看看