Mybatis | Mybatis的“关联映射“

在这里插入图片描述

作者简介 :一只大皮卡丘,计算机专业学生,正在努力学习、努力敲代码中! 让我们一起继续努力学习!

该文章参考学习教材为:
《Java EE企业级应用开发教程 (Spring + Spring MVC +MyBatis)》 黑马程序员 / 编著
文章以课本知识点 + 代码为主线,结合自己看书学习过程中的理解和感悟 ,最终成就了该文章

文章用于本人学习使用 , 同时希望能帮助大家。
欢迎大家点赞👍 收藏⭐ 关注💖哦!!!

(侵权可联系我,进行删除,如果雷同,纯属巧合)


Mybatis的 “关联映射” :

通过前面的知识点我们熟悉MyBatis的基本知识,并能够使用MyBatis以及面向对象的方式进行 “数据库操作”,但这些操作只是针对 单表 实现的。在实际的开发中,对数据库的操作常常会涉及多张表,这在面向对象中就涉及了 对象与对象之间关联关系。针对多表之间的操作,MyBatis提供了关联映射,通过关联映射就可以很好地处理对象与对象之间关联关系.

“关联关系” 概述 :

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

    在这里插入图片描述
    (数据库中表) 三种关联关系具体说明如下 : (对应“数据库表”中的 “表”之间的“主外键关系

    关联关系描述
    一对一任意一方引入对方主键作为 外键
    比如说 : 个人表
    -身份证表
    ,是“一对一”的关系,按照规律 : 此时可在将“身份证表”的主键放在“个人表”中当外键,以此实现,根据个人信息查询身份证号。(这是 数据库方面关联关系
    Java对象间关系 可以是 : 在个人表中添加一个 “类类型” ( 身份证对象 )的属性,该属性是一个对象类型,存储身份证信息,以此完成和对应“Java对象间的一对一关系。
    (即 添加类类型属性对象 可以是和 :添加外键的数据库表相对应的)
    一对多一方,添加一方主键作为外键
    比如说 :
    用户表 (一)订单表(多) ,此时在数据库层面是 :将“用户表” ()的“主键”作为“订单表” ()的“外键”。
    Java对象层面是 :在 对应的对象中添加一个 “对象集合”类型属性
    多对多产生 中间关系表引入两张表的主键作为 (中间表)的 外键两个主键成为“ 联合主键”或使用新的字段作为主键
  • 通过数 据库中的表可以描述数据之间的关系,同样,在Java中,通过对象也可以进行关系描述 ( Java对象描述数据表之间的关系 ) :

    在这里插入图片描述

    (Java对象中) 三种关联关系具体说明如下 : (对应“Java对象”中的 “表”之间的“主外键关系

    三种关联关系描述
    一对一关系就是在 本类中定义对方类型的对象。如A类中定义B类类型属性口B类中定义A类类型属性a
    互相本类中定义对方类型对象,)
    ps :
    其实只要在添加了”外键的 数据库表对应的对象”中添加一个”类类型属性即可,“不一定”要两个都添加
    一对多 的关系就是 “一个”A类类型对应“多个”B类类型的情况,需要在A类中以集合的方式引入B类类型对象,同时也要在 在B类中定义A类类型的属性对象
    ps :

    其实只要在 对象中添加一个”对象集合“ 类型属性即可,“不一定”要两个都添加.
    多对多 的关系A类中定义B类类型集合,在 B类中定义A类类型集合

1.一对一

“一对一” 关联关系 ( 嵌套查询 / 嵌套结果 (常用) )
  • 现实生活中,一对一关联 关系是十分常见的。例如,一个人只能有一个身份证,同时一个身份证也只会对应一个人

  • MyBatis怎么处理图的这种一对一关联关系的呢?

    文章: Mybatis | Mybatis的核心配置 中的 “配置文件中的主要元素” 中的 <resultMap>元素 包含了一个 <association>子元素,MyBatis 就是通过该元素<association>来处理一对一关联关系 的。

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

    属性描述
    property指定 映射实体类中的属性,与表字段一一对应
    column指定 对应字段
    javaType指定 映射实体对象“属性”类型
    简而言之 : 现在参与映射的 “实体类” 的类型一对一关系中,JavaType的值为 : 外键所在的表对应对象的类名。)
    如:
    一个“个人表”( Person对象 ),一个“身份证表” ( IDCard对象 ),“身份证”表的“主键”作为“个人表”的外键。这样就能通过个人id来查询个人信息的同时还能查询 “对应的”身份证号码。 这种是 “一对一”关联关系,要通过 <resultMap>元素中的子元素 : <association>元素来表示这种一对一关系。此时 javaType属性的为 :IDCard
    select
    ps
    属性在 “嵌套查询” 中使用
    指定 引入 “嵌套查询”子SQL语句,该属性用于 关联映射中的 嵌套查询
    ps :
    select属性作用本质 : 通过sql语句获得一个对应的“对象信息”,将其映射某个对象的“类类型”属性上,以此来完成“一对一关联关系
    fetchType指定在 关联查询是否启用延迟加载fetchType属性lazyeager两个属性值默认值lazy (即默认关联映射延迟加载)。

    例子如

      <!--  使用<association>元素实现"关联关系" : "一对一关系" (是在<resultMap>元素中使用的,不能单独使用) -->
     <!--  方式一: 嵌套查询 -->
      <association property="card" column="card_id" javaType="com.myh.mapper.IdCard"
                         select="com.myh.mapper.IdCardMapper.findCodeById"/>
    
     <!--  方式二: 嵌套结果(更常用) -->
     <association property="card" column="card_id" javaType="com.myh.mapper.IdCard">
                <id property="id" column="card_id"/>
                <result property="code" column="code"/>
            </association>
    

    MyBatis映射文件中加载关联关系对象主要通过两种方式 : 嵌套查询嵌套结果 ( 更常用 )。
    嵌套查询 是指通过执行 另外一条SQL映射语句 来返回预期复杂类型
    嵌套结果是使用 嵌套结果映射 来处理重复联合结果子集 (使用一条复杂SQL语句来进行多表查询)。

    ps :
    通过 嵌套结果 ( 更常用 )的方式来进行处理“一对一”关联关系是使用一条复杂SQL语句来进行多表查询,这样就能达到目的,此时在 <association>元素中要设置外键”对应的数据库表 和 其对应的“对象” 之间的映射关系,以此来完成数据映射
    (其中实际的最重要操作是 : ①在 <association>元素中设置好“外键”对应的数据库表 和 对象属性之间的“映射关系” ②在“映射文件”中通过一条复杂的SQL语句进行 “多表查询”。)

“一对一” 关联关系 “实现方式” (例子)
  • 下面的内容 :将以 个人身份证 之间的 一对一关联关系 进行详细例子讲解 :
    查询个人及其关联的身份证信息是先通过查询个人表中主键来获个人信息,然后通过表中的外键,来获取证件表中的身份证号信息
    (有两个表 :①个人表 : 其中存放个人信息 ②身份证表 : 其中存放身份证信息。 通过个人表中个人对应身份证表中的主键 ,该主键存放在个人表中则作为 ”外键“ ,最后通过该外键实现“个人”对应的“身份证信息”)

  • 一对一”关联关系的 <association>元素实现方式两种①嵌套查询 ②嵌套结果

“嵌套查询”
  • 嵌套查询 :
    通过 <association>元素 + <association>元素select属性 + 两条sql语句 来实现 一对一”关联关系例子如下

  • 导入 Mybatis框架依赖 (引入Mybatis的核心包 和 Mybatis的依赖包) 和 MySQL驱动JAR: [Mybatis网址
    获得Mybatis核心包 和 Mybatis的依赖包

    MySQL驱动JAR

  • 创建tb_idcardtb_person表
    在这里插入图片描述

    在这里插入图片描述

  • IDCard.javaPerson.java

    public class IDCard { //持久化类
    
        private Integer id; //该属性为 tb_idcard表中的“主键”,在"tb_person"表中做“外键”
        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;
    
        /*
           个人关联的“身份证号”,此处将IDCard这个对象,作为属性引入到该表中。
           数据库中则通过“tb_person”表中的“外键”来查询到个人对应的“tb_idcard”表中的“身份证号”
         */
        private  IDCard card; //"对象类型",要获得一个“IDCard”类型的数据然后"映射"到该属性中
    
        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 +
                    '}';
        }
    }
    
  • db.propertiesmybatis-config.xml

    db.properties

    jdbc.driver = com.mysql.cj.jdbc.Driver
    jdbc.url = jdbc:mysql://localhost:3306/mybatis?useSSL=false
    jdbc.username = root
    jdbc.password = root
    

    mybatis-config.xml :

    <?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>
    
        <!--  通过“配置”中的<Properties元素>来引入.propertis文件,可在映射文件中使用该文件中的属性等  -->
        <properties resource="com/myh/Mybatis的关联映射/properties/db.properties"/>
    
        <!--  使用扫描包的形式来定义“别名” : 替代类的"全限定名",引入更加方便快捷  -->
        <typeAliases>
            <package name="com.myh.Mybatis的关联映射.po"/>
        </typeAliases>
    
        <!--
           Mybatis的配置文件: mybatis-config.xml : 为全局配置文件,配置了“运行环境的信息”,
           主要内容是 : 数据库的连接 (其属于Mybatis操作步骤的第一步 : 读取Mybatis-config.xml配置文件的"先行要准备好的内容")
          -->
        <!--  1.配置环境,默认环境id为mysql  -->
        <environments default="mysql">
            <environment id="mysql">
                <transactionManager type="JDBC"/>
                <!--  用于指定MyBatis获取数据库连接的方式。“POOLED”代表的是连接池。 -->
                <dataSource type="POOLED">
                    <property name="driver" value="${jdbc.driver}"/>
                    <property name="url" value="${jdbc.url}"/>
                    <property name="username" value="${jdbc.username}"/>
                    <property name="password" value="${jdbc.password}"/>
                </dataSource>
            </environment>
        </environments>
    
    
        <!--  配置Mapper文件的位置,让"配置文件"读取“映射文件”  -->
        <mappers>
            <mapper resource="com/myh/Mybatis的关联映射/mapper/IdCardMapper.xml"/>
            <mapper resource="com/myh/Mybatis的关联映射/mapper/PersonMapper.xml"/>
             <mapper resource="com/myh/Mybatis的关联映射/mapper/PersonMapper (嵌套结果).xml"/>
        </mappers>
    </configuration>
    
  • IdCardMapper.xmlPersonMapper.java (映射文件):

    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">
    
    <!--  namespace的命名空间 -->
    <mapper namespace="IdCardMapper">
        <!--  根据id查询证件的信息  -->
        <select id="findCodeById" parameterType="Integer" resultType="com.myh.Mybatis的关联映射.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">
    
    <!--  namespace的命名空间 -->
    <mapper namespace="PersonMapper">
    
        <!--  "嵌套查询" 的方式来实现“一对一”映射关系  -->
    
         <resultMap id="IdCardWithPersonResult" type="com.myh.Mybatis的关联映射.po.Person">
             <id property="id" column="id"/>
             <result property="name" column="name"/>
             <result property="age" column="age"/>
             <result property="sex" column="sex"/>
            <!--  使用<resultMap>元素的子元素:<association>元素来表示Person对象中存在的 “一对一”关联关系 -->
             <!--  此处获得一个IdCard类型对应的“对象”属性数据,然后映射到该"类类型"属性中 -->
             <association property="card" column="card_id" javaType="com.myh.Mybatis的关联映射.po.IDCard"
                 select="IdCardMapper.findCodeById"/>
         </resultMap>
    
        <!--  嵌套查询: 通过执行另外一条SQL映射语句来返回预期的"特殊类型"  -->
        <!--
        实际的需求的前提说明 :
        Person表中除了本来对应的属性外,还添加了“IdCard”对象这个“类类型”属性,这就是”一对一“关联关系,同时也要求在Sql语句中除了基本的查询外,
        还要通过Person的id来查询到对应“身份证”信息。(因为这已经设置好了主外键,只要使用<resultMap>的子元素: <association>元素就能解决这个问题)
        -->
        <!--
        实际的需求 :
        通过Person的id除了基本的Person类的信息外,还要活得IdCard类的信息,然后映射到类的属性中,可用<association> : "一对一"关联关系来实现该需求
        -->
        <select id="findPersonById" parameterType="Integer" resultMap="IdCardWithPersonResult">
            select * from tb_person where id=#{id}
        </select>
    
    
    </mapper>
    
  • MybatisAssociatedTest.java (测试类) :

    //Mybatis关联查询映射测试类
    public class MybatisAssociatedTest {
    
        /**
         * “嵌套查询”方式实现“一对一”关联关系
         */
        @Test //单元测试
        public void findPersonByIdTest1() throws IOException {
            //1.读取mybatis框架的配置文件
            String resource = "com/myh/Mybatis的关联映射/mybatis-config.xml";
            //通过“输入流”读取mybatis配置文件
            /*
               在该mybatis-config.xml配置文件中,已配置了“数据源”信息 和配置了"映射文件 : XxxMapper.xml”的位置,
               可实施加载“映射文件”
             */
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //2.根据配置文件“构建会话工厂 : SqlSessionFactory ”
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            //3.通过SqlSessionFactory(会话工厂)创建SqlSession("会话"对象)
            SqlSession sqlSession = sqlSessionFactory.openSession();
            //使用Mybatis嵌套查询的方式查询id为1的人的信息
            Person person = sqlSession.selectOne("PersonMapper.findPersonById", 1);
            System.out.println("(一对一关联关系)查询出来的信息为: "+person);
    
            //4.关闭SqlSession
           sqlSession.close();
        }
    
    }
    

    注意点:

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

       <settings>
            <!--  打开"延迟加载"的开关 -->
            <setting name="lazyLoadingEnable" value="true"/>
           
            <!--  将“积极加载”改为“延迟加载”,即按需加载   -->
            <setting name="aggressiveLazyLoading" value="false"/>
        </settings>
    

    映射文件中,MyBatis 关联映射<asociation>元素<collection>元素中都已默认配置延迟加载属性,即默认属性fetchType=“lazy” (属性fetchType="eager"表示立即加载),所以在配置文件开启延迟加载后无须在映射文件中再做配置

“嵌套结果” (常用)
  • 嵌套结果 :
    通过 <association>元素 + <association>元素<id>元素、<result>元素等 + 一条sql语句 来实现 一对一”关联关系

  • 嵌套结果与 “嵌套查询”相比只需要使用一条SQL语句即可很好地解决了 “嵌套查询”存在的多条sql查询消耗数据库性能降低查询效率“的问题

  • 例子如下

    首先所有的文件代码和 “嵌套查询保持一致。因为“嵌套结果” 和 “嵌套查询”的大部分代码是相似的,只有“映射文件 ”( PersonMapper (嵌套结果).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的命名空间 -->
    <mapper namespace="PersonMapper(嵌套结果)">
    
        <!--  "嵌套结果" 的方式来实现“一对一”映射关系  -->
        <!--  通过以下代码就能通过一条复杂的SQL语句来进行“多表查询”,然后通过之前的配置完成“数据映射”,不需要使用“嵌套查询”的要用两条SQL语句来实现  -->
         <resultMap id="IdCardWithPersonResult" type="com.myh.Mybatis的关联映射.po.Person">
             <id property="id" column="id"/>
             <result property="name" column="name"/>
             <result property="age" column="age"/>
             <result property="sex" column="sex"/>
            <!--  使用<resultMap>元素的子元素:<association>元素来表示Person对象中存在的 “一对一”关联关系 -->
             <!-- 配置“外键”对应的“数据库表” 和 其对应的“对象”之间的映射关系 -->
             <association property="card" column="card_id" javaType="com.myh.Mybatis的关联映射.po.IDCard">
                 <id property="id" column="id"/>
                 <result property="code" column="CODE"/>
             </association>
         </resultMap>
    
    
        <!--  嵌套结果: 使用“嵌套结果”映射来处理“重复”的“联合结果”的子集  -->
        <!--
        实际的需求的前提说明 :
        Person表中除了本来对应的属性外,还添加了“IdCard”对象这个“类类型”属性,这就是”一对一“关联关系,同时也要求在Sql语句中除了基本的查询外,
        还要通过Person的id来查询到对应“身份证”信息。(因为这已经设置好了主外键,只要使用<resultMap>的子元素: <association>元素就能解决这个问题)
        -->
        <!--
        实际的需求 :
        通过Person的id除了基本的Person类的信息外,还要活得IdCard类的信息,然后映射到类的属性中,可用<association> : "一对一"关联关系来实现该需求
        -->
        <!--   通过一条复杂的sql语句来进行”多表查询“  -->
        <select id="findPersonById" parameterType="Integer" resultMap="IdCardWithPersonResult">
            select p.*,idcard.CODE
            from tb_person p,tb_idcard idcard where p.card_id = idcard.id
            and p.id = #{id}
        </select>
    
    </mapper>
    

    MybatisAssociatedTest.java (测试类) :

    //Mybatis关联查询映射测试类
    public class MybatisAssociatedTest {
    
        /**
         * “嵌套查询”方式实现“一对一”关联关系
         */
        @Test //单元测试
        public void findPersonByIdTest1() throws IOException {
            //1.读取mybatis框架的配置文件
            String resource = "com/myh/Mybatis的关联映射/mybatis-config.xml";
            //通过“输入流”读取mybatis配置文件
            /*
               在该mybatis-config.xml配置文件中,已配置了“数据源”信息 和配置了"映射文件 : XxxMapper.xml”的位置,
               可实施加载“映射文件”
             */
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //2.根据配置文件“构建会话工厂 : SqlSessionFactory ”
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            //3.通过SqlSessionFactory(会话工厂)创建SqlSession("会话"对象)
            SqlSession sqlSession = sqlSessionFactory.openSession();
            //使用Mybatis嵌套查询的方式查询id为1的人的信息
            Person person = sqlSession.selectOne("PersonMapper.findPersonById", 1);
            System.out.println("(一对一关联关系)查询出来的信息为: "+person);
    
            //4.关闭SqlSession
           sqlSession.close();
        }
    
    
        /**
         * “嵌套结果”方式实现“一对一”关联关系
         */
        @Test //单元测试
        public void findPersonByIdTest2() throws IOException {
            //1.读取mybatis框架的配置文件
            String resource = "com/myh/Mybatis的关联映射/mybatis-config.xml";
            //通过“输入流”读取mybatis配置文件
            /*
               在该mybatis-config.xml配置文件中,已配置了“数据源”信息 和配置了"映射文件 : XxxMapper.xml”的位置,
               可实施加载“映射文件”
             */
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //2.根据配置文件“构建会话工厂 : SqlSessionFactory ”
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            //3.通过SqlSessionFactory(会话工厂)创建SqlSession("会话"对象)
            SqlSession sqlSession = sqlSessionFactory.openSession();
            //使用Mybatis嵌套查询的方式查询id为1的人的信息
            Person person = sqlSession.selectOne("PersonMapper(嵌套结果).findPersonById", 1);
            System.out.println("(一对一关联关系)查询出来的信息为(通过嵌套结果的方式): "+person);
            //4.关闭SqlSession
            sqlSession.close();
        }
    
    }
    

    使用“嵌套结果”的方式 只需执行一条SQL语句就解决 “一对一”关联关系情况。开发中,“嵌套结果”的方式更常用

2.一对多

“一对多” 关联关系 ( 嵌套查询 / 嵌套结果 (常用) )
  • 一对一关联关系相比,我们 接触更多的关联关系一对多(或多对一)。例如 : 一个用户可以有多个订单,同时多个订单一个用户所有

    Mybatis通过 <collection>元素来处理“一对多关联关系
    <collection>元素的属性大部分 <association>元素相同,但其还包含一个 特殊属性ofTypeofType属性javaType属性对应,它用于指定实体对象中集合类属性所包含元素类型 (即指明集合元素类型)。

  • “一对多”关联关系实现方式同样两种①嵌套查询 ②嵌套结果 ( 常用 )
    (一对多的 ) 两种方式<collection>元素 中的 代码区别 为 :

      <!--  使用<collection>元素实现"关联关系" : "一对多关系" (是在<resultMap>元素中使用的,不能单独使用) -->
     <!--  方式一: 嵌套查询 -->
      <association property="card" column="card_id" javaType="com.myh.mapper.IdCard"
                         select="com.myh.mapper.IdCardMapper.findCodeById"/>
    
     <!--  方式二: 嵌套结果(更常用) -->
     <association property="card" column="card_id" javaType="com.myh.mapper.IdCard">
                <id property="id" column="card_id"/>
                <result property="code" column="code"/>
            </association>
    
“一对多” 关联关系 “实现方式” (例子)
  • 下面的内容 :将以 用户订单之间的“一对多” 关联关系进行详细例子讲解 :
    (有两个表 :①用户表
    ) : 其中存放用户信息 ②订单表 () : 其中存放订单信息。 数据库表层是 : 将在 “多” : 订单中添加添加“外键” (将“用户表”中主键作为 “订单表” 中“外键”)

  • 一对多”关联关系的 <collection>元素实现方式两种①嵌套查询 ②嵌套结果
    ps :

    例子讲解只讲常用的 : “嵌套结果”的方式实现 “一对多 关联关系”

“嵌套结果” (常用)
  • 嵌套结果 :
    通过 <collection>元素 + <collection>元素ofType属性、id元素、result元素 + 一条sql语句 来实现 一对多”关联关系例子如下

    第一步、创建tb_usertb_orders表
    在这里插入图片描述

    在这里插入图片描述

  • 创建数据库 中表对应的PO类** (持久化对象)

  • db.propertiesmybatis-config.xml

    db.properties

    jdbc.driver = com.mysql.cj.jdbc.Driver
    jdbc.url = jdbc:mysql://localhost:3306/mybatis?useSSL=false
    jdbc.username = root
    jdbc.password = root
    

    mybatis-config.xml :

    <?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>
    
        <!--  通过“配置”中的<Properties元素>来引入.propertis文件,可在映射文件中使用该文件中的属性等  -->
        <properties resource="db.properties"/>
    
        <!--
           Mybatis的配置文件: mybatis-config.xml : 为全局配置文件,配置了“运行环境的信息”,
           主要内容是 : 数据库的连接 (其属于Mybatis操作步骤的第一步 : 读取Mybatis-config.xml配置文件的"先行要准备好的内容")
          -->
        <!--  1.配置环境,默认环境id为mysql  -->
        <environments default="mysql">
            <environment id="mysql">
                <transactionManager type="JDBC"/>
                <!--  用于指定MyBatis获取数据库连接的方式。“POOLED”代表的是连接池。 -->
                <dataSource type="POOLED">
                    <property name="driver" value="${jdbc.driver}"/>
                    <property name="url" value="${jdbc.url}"/>
                    <property name="username" value="${jdbc.username}"/>
                    <property name="password" value="${jdbc.password}"/>
                </dataSource>
            </environment>
        </environments>
    
    
        <!--  配置Mapper文件的位置,让"配置文件"读取“映射文件”  -->
        <mappers>
            <mapper resource="com/myh/Mybatis的关联映射/一对多/mapper/userMapper.xml"/>
        </mappers>
    
    </configuration>
    
  • 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">
    
    <!--  namespace的命名空间 -->
    <mapper namespace="UserMapper">
    
        <resultMap id="resultMap" type="com.myh.Mybatis的关联映射.一对多.po.User">
            <id property="id" column="id"/>
            <id property="username" column="username"/>
            <id property="address" column="address"/>
    
            <!--  用“collection”元素“配置一对多”关联关系 (注意同样是“一对多”在①数据库表关系层面 和 ②Java对象属性关系层面的表现是不一样的)  -->
            <!--  ofType : 指定"集合"中的"元素"的类型   -->
            <!--  id 和 result元素 : 将集合中的元素中的属性 和 其对应的数据库中“字段”数据进行映射      -->
            <collection property="ordersList" ofType="com.myh.Mybatis的关联映射.一对多.po.Orders">
                <id property="id" column="orders_id"/>
                <result property="number" column="number"/>
            </collection>
        </resultMap>
    
    
        <!--
        一对多 :  查看某一"用户"及其关联的“订单信息”
         注意: 当关联查询出的列名相同,需使用别名进行区分开
        -->
        <!-- 使用的实现“一对多”关联关系的方式中的“嵌套结果” : 用复杂语句进行“多表查询”   -->
        <select id="findUserWithOrders" parameterType="Integer" resultMap="resultMap" >
               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>
    
    </mapper>
    
  • MybatisCollectionTest.java (测试类)

    public class MybatisCollectionTest { //"一对多"关联关系,测试类
    
        /**
         * “嵌套结果”方式实现“一对多”关联关系
         */
        @Test //单元测试
        public void findUserTest_Collection() throws IOException {
            //1.读取mybatis框架的配置文件
            String resource = "com/myh/Mybatis的关联映射/一对多/mybatis-config.xml";
            //通过“输入流”读取mybatis配置文件
            /*
               在该mybatis-config.xml配置文件中,已配置了“数据源”信息 和配置了"映射文件 : XxxMapper.xml”的位置,
               可实施加载“映射文件”
             */
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //2.根据配置文件“构建会话工厂 : SqlSessionFactory ”
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            //3.通过SqlSessionFactory(会话工厂)创建SqlSession("会话"对象)
            SqlSession sqlSession = sqlSessionFactory.openSession();
            User user = sqlSession.selectOne("UserMapper.findUserWithOrders", 1);
            System.out.println("通过(嵌套结果)方式处理(一对多)关联关系地从数据库中获得User数据为: "+user);
            //4.关闭SqlSession
            sqlSession.close();
        }
    }
    

3.多对多

“多对多” 关联关系
  • 实际项目开发中,多对多关联关系 也是非常常见的。以 订单商品 为例,一个订单可以包含多种商品
    一种商品又可以属于多个订单订单商品就属于多对多关联关系

  • 数据库中,多对多关联关系 通常使用一个 中间表 来维护,中间表中的订单id作为外键参照 订单表id商品id作为外键参照 商品表id
    (即将“订单表 和“”商品表主键在 “中间表中当“外键””)

  • 下面的“多对多”例子中的出发思考点是 : 订单—产品。(思考的 :查询id为xxx的订单的”产品信息“)

    ps : 映射文件 中整体的执行流程 : 虽然都是多对多,但实际开发中一般客户的思考方向是 :从“订单”出发然后到“产品”的 (如 : 我这个订单选了5个产品)。所以此时映射文件的执行流程 : 是从OrderMapper.xmlproductMapper.xml

“多对多” 关联关系的 “例子”
  • 下面的“多对多”例子中的出发思考点是 : 订单产品。(思考的 :查询idxxx订单的”产品信息“)

  • 多对多”关联关系的 <collection>元素实现方式两种①嵌套查询 ②嵌套结果

“嵌套查询”
  • 嵌套查询 :
    通过 <collection>元素 + <collection>元素select属性 + 两条sql语句 来实现 多对多”关联关系例子如下

  • 创建tb_producttb_orderstb_ordersitem这三个表
    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

  • 创建tb_product表tb_orders表对应的 “持久化类

  • OrdersMapper.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的命名空间 -->
    <mapper namespace="OrdersMapper">
        
        <select id="findOrdersWithProduct" parameterType="Integer" resultMap="resultMap">
            select * from tb_orders where id = #{id}
        </select>
    
        <!--  collection中的select属性中的sql语句的开发目标是 : 根据订单id来查询对应的“产品”,查询到后"映射“存储的List集合中  -->
        <resultMap id="resultMap" type="com.myh.Mybatis的关联映射.多对多.po.Orders">
            <id property="id" column="id"/>
            <id property="number" column="number"/>
           <collection property="productList" column="id" ofType="com.myh.Mybatis的关联映射.多对多.po.Product"
                select="ProductMapper.findProductById">
           </collection>
        </resultMap>
    
    </mapper>
    

    ProductMapper.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的命名空间 -->
    <mapper namespace="ProductMapper">
    
       <!-- 根据订单id来在“中间表”中查询对应的“产品”-->
        <!--将此处获得的“产品”的组成的List集合映射到 OrdersMapper.xml中的 collection元素中的 productList上 -->
        <select id="findProductById" parameterType="Integer" resultType="com.myh.Mybatis的关联映射.多对多.po.Product">
            select * from tb_product where id in (select product_id from tb_ordersitem where orders_id = #{id})
        </select>
    
    </mapper>
    

    测试类代码

      /**
         * “嵌套查询”方式实现“多对多”关联关系
         */
        @Test //单元测试
        public void findUserTest_Collection() throws IOException {
            //1.读取mybatis框架的配置文件
            String resource = "com/myh/Mybatis的关联映射/多对多/mybatis-config.xml";
            //通过“输入流”读取mybatis配置文件
            /*
               在该mybatis-config.xml配置文件中,已配置了“数据源”信息 和配置了"映射文件 : XxxMapper.xml”的位置,
               可实施加载“映射文件”
             */
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //2.根据配置文件“构建会话工厂 : SqlSessionFactory ”
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            //3.通过SqlSessionFactory(会话工厂)创建SqlSession("会话"对象)
            SqlSession sqlSession = sqlSessionFactory.openSession();
            //查询id为1的订单中的商品信息
            Orders orders = sqlSession.selectOne("OrdersMapper.findOrdersWithProduct", 1);
            System.out.println(orders);
            //4.关闭SqlSession
            sqlSession.close();
        }
    
“嵌套结果” (常用)
  • 嵌套结果 :
    通过 <collection>元素 + <collection>元素ofType属性、id子元素、result子元素等 + 一条sql语句 来实现 多对多”关联关系例子如下

    大部分代码和 “嵌套查询”的代码是相同的,最大的差别是在 映射文件中,其中的代码有根本性的改变,导致从“嵌套查询” 变成了 “嵌套结果” 。

    OrdersMapper2.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的命名空间 -->
    <!-- 多对多中的“嵌套结果”-->
    <mapper namespace="OrdersMapper2">
    
        <!--  复杂的sql语句查询 + “结果映射” 处理 “多对多”关系  -->
        <select id="findOrdersWithProduct2" parameterType="Integer" resultMap="resultMap222">
           select o.*,p.id as pid,p.name,p.price
            from tb_orders o ,tb_product p,tb_ordersitem oi
             where oi.orders_id = o.id
             and oi.product_id = p.id
             and o.id = #{id}
        </select>
    
        <!-- 多对多中的“嵌套结果”-->
        <!--  collection中的select属性中的sql语句的开发目标是 : 根据订单id来查询对应的“产品”,查询到后"映射“存储的List集合中  -->
        <resultMap id="resultMap222" type="com.myh.Mybatis的关联映射.多对多.po.Orders">
            <id property="id" column="id"/>
            <id property="number" column="number"/>
           <collection property="productList"  ofType="com.myh.Mybatis的关联映射.多对多.po.Product">
               <id property="id" column="pid"/>
               <result property="name" column="name"/>
               <result property="price" column="price"/>
           </collection>
        </resultMap>
    
    </mapper>
    

    上述代码中,只定义了一条查询SQL,通过该SQL就能查询出订单及其关联商品信息

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值