广告
返回顶部
首页 > 资讯 > 后端开发 > Python >SSM框架之MyBatis3专题3:关联
  • 243
分享到

SSM框架之MyBatis3专题3:关联

框架专题SSM 2023-01-31 07:01:39 243人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

当查询内容涉及具有关联关系的多个表时,就需要使用关联关系查询。根据表与表之间的关联关系的不同,关联查询分为四种:1、一对一关联查询;2、一对多关联查询;3、多对一关联查询;4、多对多关联查询; 由于日常工作中最常见的关联关系是一对多、多

  • 当查询内容涉及具有关联关系的多个表时,就需要使用关联关系查询。根据表与表之间的关联关系的不同,关联查询分为四种:
    1、一对一关联查询;
    2、一对多关联查询;
    3、多对一关联查询;
    4、多对多关联查询;
  • 由于日常工作中最常见的关联关系是一对多、多对一与多对多,所以这里就不专门只讲解一对一关联查询了,其解决方案与多对一解决方案是相同的。

    1.1 一对多关联查询

  • 这里的一对多关联查询是指,在查询一方对象的时候,同时将其所关联的多方对象也都查询出来。
  • 下面以国家Country与部长Minister间的一对多关系进行演示。

    1.1.1 定义实体

  • 在定义实体时,若定义的是双向关联,即双方的属性中均有对方对象作为域属性出现,那么它们在定义各自的toString()方法时需要注意,只让某一方可以输出另一方即可,不要让双方的toString()方法均可输出对方。这样会形成递归调用,程序出错。

    1.1.2 定义数据库

    SSM框架之MyBatis3专题3:关联关系查询
    SSM框架之MyBatis3专题3:关联关系查询

    1.1.3 定义Dao层接口

    public interface ICountryDao {
    Country selectCountryById(int cid);
    }

    1.1.4 定义测试

    public class Mytest {
    private sqlSession session;
    private ICountryDao dao;    
    @Before
    public void setUp() {
        session = mybatisUtils.getSqlSession();
        dao = session.getMapper(ICountryDao.class);
    }   
    @After
    public void tearDown() {
        if(session != null) {
            session.close();
        }
    }   
    @Test
    public void test01() {
        Country country = dao.selectCountryById(1);
        System.out.println(country);
    }
    }

    1.1.5 定义映射文件

    1、多表连接查询方式

    <mapper namespace="com.eason.mybatis.dao.ICountryDao">
    <resultMap type="Country" id="countryMapper">
        <id column="cid" property="cid"/>
        <result column="cname" property="cname"/>
        <!-- 关联属性的映射文件 -->
        <collection property="ministers" ofType="Minister">
            <id column="mid" property="mid"/>
            <result column="mname" property="mname"/>
        </collection>
    </resultMap>
    <!-- 多表连接查询 -->
    <select id="selectCountryById" resultMap="countryMapper">
        select cid, cname, mid, mname from t_country, t_minister where cid=#{xxx} and cid=countryId
    </select>
    </mapper>
  • 注意,此时即使字段名与属性名相同,在<resultMap/>中也写出它们的映射关系。因为框架是依据这个<resultMap/>封装对象的。
  • 另外,在映射文件中使用<collection/>标签体现出两个实体对象间的关联关系。其两个属性的意义为:
  • property:指定关联属性,即Country类中的集合属性;
  • ofType:集合属性的泛型类型;

2、多表单独查询方式

  • 多表连接查询方式是将多张表进行拼接,连为一张表后进行查询。其查询的本质是一张表。而多表单独查询方式是多张表各自查询各自的相关内容,需要多张表的联合数据了,则将主表的查询结果联合其他表的查询结果,封装为一个对象。
  • 当然,这多个查询是可以跨越多个映射文件的。即是可以跨越多个namespace的。在使用其他namespace的查询时,添加上其所在的namespace即可。
    <mapper namespace="com.eason.mybatis.dao.ICountryDao">
    <select id="selectMinisterByCountry" resultType="Minister">
    select mid, mname from t_minister where countryId=#{ooo}
    </select>
    <resultMap type="Country" id="countryMapper">
        <id column="cid" property="cid"/>
        <result column="cname" property="cname"/>
        <!-- 关联属性的映射文件 -->
        <!-- 集合的数据来自于指定的select查询 -->
        <collection property="ministers" ofType="Minister" select="selectMinisterByCountry" column="cid"></collection>
    </resultMap>
    <!-- 多表连接查询 -->
    <select id="selectCountryById" resultMap="countryMapper">
        select cid, cname from t_country where cid=#{xxx}
    </select>
    </mapper>
  • 关联属性<collection/>的数据来自于一个查询<selectMinisterByCountry/>。而该查询<selectMinisterByCountry/>的动态参数countryId=#{ooo}的值来自于查询<selectCountryById/>的查询结果字段cid。

    1.2 多对一关联查询

  • 这里的多对一关联查询是指,在查询多方对象的时候,同时将其所关联的一方对象也查询出来。
  • 由于在查询多方对象时也是一个一个查询,所以多对一关联查询,其实就是一对一关联查询。即一对一关联查询的实现方式与多对一的实现方式是相同的。
  • 下面以部长Minister与国家Country间的多对一关联进行演示。

    1.2.1 定义实体

    public class Minister {
    private Integer mid;
    private String mname;
    private Country country;
    //setter and getter
    //toString
    }
    public class Country {
    private Integer cid;
    private String cname;
    //setter and getter()
    //toString
    }

    1.2.2 定义数据库

    SSM框架之MyBatis3专题3:关联关系查询
    SSM框架之MyBatis3专题3:关联关系查询

    1.2.3 定义Dao层接口

    public interface ICountryDao {
    Minister selectMinisterById(int mid);
    }

    1.2.4 定义测试类

    @Test
    public void test02() {
        Minister minister = dao.selectMinisterById(2);
        System.out.println(minister);
    }

    1.2.5 定义映射文件

    1、多表连接查询方式:

    <mapper namespace="com.eason.mybatis.dao.ICountryDao">
    <resultMap type="Minister" id="ministerMapper">
        <id column="mid" property="mid"/>
        <result column="mname" property="mname"/>
        <association property="country" javaType="Country">
            <id column="cid" property="cid"/>
            <result column="cname" property="cname"/>
        </association>
    </resultMap>
    <select id="selectMinisterById" resultMap="ministerMapper">
        select mid, mname, cid, cname from t_minister,t_country where mid=#{xxx} and countryId = cid 
    </select>
    </mapper>
  • 注意:在映射文件中使用<association/>标签体现出两个实体对象间的关联关系。
  • property:指定关联属性,即Minister类中的country属性。
  • javaType:关联属性的类型。
    2、多表单独查询方式
    <mapper namespace="com.eason.mybatis.dao.ICountryDao">
    <select id="selectCountryById" resultType="Country">
        select * from t_country where cid=#{ooo} 
    </select>
    <resultMap type="Minister" id="ministerMapper">
        <id column="mid" property="mid"/>
        <result column="mname" property="mname"/>
        <association property="country" javaType="Country" select="selectCountryById" column="countryId"></association>
    </resultMap>
    <select id="selectMinisterById" resultMap="ministerMapper">
        select mid, mname, countryId from t_minister where mid=#{xxx}
    </select>
    </mapper>

    1.3 自关联查询

  • 所谓自关联是指,自己即充当一方,又充当多方,是1:n或者n:1的变型。例如,对于新闻栏目NewsColumn,可以充当一方,即父栏目,也可以充当多方,即子栏目。而反映到DB表中,只有一张表,这张表中具有一个外键,用于表示该栏目的父栏目。一级栏目没有父栏目,所以可以将其外键值是为0,而子栏目具有外键值。
  • 为了便于理解,将自关联分为两种情况来讲解,一种是当做1:n讲解,即当前类作为一方,其包含多方的集合域属性。一种是当做n:1讲解,即当前类作为多方,其包含一方的域属性。
  • 下面以新闻栏目为例进行讲解。由于Column是DBMS中的关键字,为了避免误解,将新闻栏目实体类定义为NewsLabel。

    1.3.1 自关联的DB表

    SSM框架之MyBatis3专题3:关联关系查询

    1.3.2 以一对多方式处理

  • 以一对多方式处理,即一方可以看到多方。该处理方式的应用场景比较多,例如在页面上点击父栏目,显示出其子栏目。再如,将鼠标定位在窗口中的某菜单项上会显示其所有子菜单项等。
    1、查询指定栏目的所有子孙栏目:
  • 根据指定的id,仅查询出其所有子栏目。当然,包括其所有辈分的孙子栏目。即,给出的查询id实际为父栏目id。
  • 定义实体类:
    public class NewsLabel {
    private Integer id;
    private String name;
    //关联属性,指定子栏目,即多方
    private Set<NewsLabel> children;
    //getter and setter
    //toString()
    }
  • 定义Dao接口:
    public interface INewsLabelDao {
    List<NewsLabel> selectChidrenByParentId(int pid);
    }
  • 定义mapper映射:这里通过select语句的递归调用实现查询所有下级栏目的功能。查询结果的集合数据<collection/>来自于递归调用的selectChidrenByParentId查询。与第一次进行该查询不同的是,第一次的pid动态参数值来自于调用方法传递来的实参,而<collection/>中查询语句的pid动态参数数值来自于上一次的查询结果的id值。
    <mapper namespace="com.eason.mybatis.dao.INewsLabelDao">
    <!-- 形成递归,因为查询结果再次调用了selectChidrenByParentId查询 -->
    <resultMap type="NewsLabel" id="newsLabelMapper">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <collection property="children" ofType="NewsLabel" 
                    select="selectChidrenByParentId"
                    column="id"></collection>
    </resultMap>
    <!-- 根据pid查询其子栏目 -->
    <select id="selectChidrenByParentId" resultMap="newsLabelMapper">
        select id, name from t_newslabel where pid = #{xxx}
    </select>
    </mapper>
  • 定义测试类:
    public class Mytest {
    private SqlSession session;
    private INewsLabelDao dao;
    @Before
    public void setUp() {
        session = MyBatisUtils.getSqlSession();
        dao = session.getMapper(INewsLabelDao.class);
    }
    @After
    public void tearDown() {
        if(session != null) {
            session.close();
        }
    }   
    @Test
    public void test02() {
        List<NewsLabel> children = dao.selectChidrenByParentId(1);
        for(NewsLabel newsLabel : children) {
            System.out.println(newsLabel);
        }
    }
    }

    2、查询指定栏目以及所有子孙栏目

  • 这里的查询结果,即要包含指定id的当前栏目,还要包含其所有辈分的孙子栏目。即给出的id实际为当前要查询的栏目的id。
  • 修改Dao接口:
    public interface INewsLabelDao {
    NewsLabel selectNewsLabelById(int id);
    }
  • 修改mapper映射:

    <mapper namespace="com.eason.mybatis.dao.INewsLabelDao">
    <select id="selectNewsLabelByParentId" resultMap="newsLabelMapper">
        select id, name from t_newslabel where pid = #{ooo}
    </select>
    <resultMap type="NewsLabel" id="newsLabelMapper">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <collection property="children" ofType="NewsLabel" 
                    select="selectNewsLabelByParentId"
                    column="id"></collection>
    </resultMap>
    
    <select id="selectNewsLabelById" resultMap="newsLabelMapper">
        select id, name from t_newslabel where id = #{xxx}
    </select>
    </mapper>
  • 修改测试类:
    @Test
    public void test02() {
        NewsLabel newsLabel = dao.selectNewsLabelById(1);
        System.out.println(newsLabel);
    }

    1.3.3 以多对一方式处理

  • 以多对一方式处理,即多方可以看到一方。该处理方式的应用功能场景,例如在网页上显示当前页面的站内位置。
  • 定义实体类:

    public class NewsLabel {
    private Integer id;
    private String name;
    private NewsLabel parent;
    
    //setter and getter()
    //toString
    }
  • 定义mapper映射:
    <mapper namespace="com.eason.mybatis.dao.INewsLabelDao">
    <resultMap type="NewsLabel" id="newslabelMapper">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <association property="parent"
                    javaType="NewsLabel"
                    select="selectParentByParentId"
                    column="pid">
        </association>
    </resultMap>
    <select id="selectParentByParentId" resultMap="newslabelMapper">
        select id,name,pid from t_newslabel where id=#{xxx}
    </select>
    </mapper>
  • 定义测试类:
    @Test
    public void test03() {
        NewsLabel newsLabel = dao.selectParentByParentId(3);
        System.out.println(newsLabel);
    }

    1.4 多对多关联查询

  • 什么是多对多关联关系?一个学生可以选择多门课程,而一门课程可以由多个学生选。这就是典型的多对多关联关系。所以,所谓多对多关系,其实是由两个互反的一对多关系组成。一般情况下,多对多关系都会通过一个中间表来建立,例如选课表。

    1.4.1 定义实体

  • 在定义双向关联(双方均可看到对方的关联关系)的实体的toString()方法时,只会让一方的toString()方法中可以输出对方,不要让双方均可输出对方。否则将会出现的递归现象,程序会报错。
    public class Student {
    private Integer sid;
    private String sname;
    private Set<Course> courses;
    //setter and getter()
    //toString()
    }
    public class Course {
    private Integer cid;
    private String cname;
    private Set<Student> students;
    //setter and getter()
    //toString()
    }

    1.4.2 定义数据库表

    SSM框架之MyBatis3专题3:关联关系查询
    SSM框架之MyBatis3专题3:关联关系查询

    1.4.3 定义Dao层接口

    public interface IStudentDao {
    Student selectStudentById(int id);
    }

    1.4.4 定义mapper映射

  • 多对多关联关系也是通过映射文件<resultMap/>的<collection/>体现的。但是,需要注意的是SQL语句中是对三张表的连接查询。
    <mapper namespace="com.eason.mybatis.dao.IStudentDao">
    <resultMap type="Student" id="studentMapper">
        <id column="sid" property="sid"/>
        <result column="sname" property="sname"/>
        <collection property="courses" ofType="Course">
            <id column="cid" property="cid"/>
            <result column="cname" property="cname"/>
        </collection>
    </resultMap>
    <select id="selectStudentById" resultMap="studentMapper">
        select sid, sname, cid, cname 
        from t_student, t_middle, t_course 
        where sid = studentId and cid = courseId and sid = #{xxx}
    </select>
    </mapper>

    1.4.5 定义测试类

    @Test
    public void test05() {
        Student student = dao.selectStudentById(2);
        System.out.println(student);
    }
  • MyBatis中的延迟加载,也称之为懒加载,是指在进行关联查询时,按照设置规则推迟对关联对象的select查询。延迟加载可以有效的减少数据库压力。
  • 需要注意的是,MyBatis的延迟加载只是对关联对象的查询有延迟设置,对于主加载对象都是直接执行查询语句的。

    2.1 关联对象加载时机

  • MyBatis根据对关联对象查询的select语句的执行时期,分为三种类型:直接加载、侵入式延迟加载和深度延迟加载。
    1、直接加载:执行完对主加载对象的select语句,马上执行对关联对象的select查询。
    2、侵入式延迟:执行完对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情时,就会马上执行关联对象的select查询。即对关联对象的查询执行,侵入到了主加载对象的详情访问中。也可以这样理解:将关联对象的详情侵入到了主加载对象的详情中,即将关联对象的详情作为主加载对象的详情的一部分出现。
    3、深度延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的select查询。
  • 需要注意的是,延迟加载的应用要求,关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能够是多表连接所进行的select查询。因为,多表连接查询,其实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。
  • MyBatis中对于延迟加载设置,可以应用到一对一、一对多、多对多的所有关联关系查询中。
  • 下面以一对多关联关系查询为例,讲解MyBatis中的延迟加载应用。

    2.2 直接加载

  • 修改主配置文件:在主配置文件的<properties/>与<typeAliases/>标签之间,添加<setting/>标签,用于完成全局参数设置。
        <!-- 注册属性文件 -->
        <properties resource="jdbc.properties"></properties>
        <!-- 全局参数设置 -->
        <settings>
            <setting name="lazyLoadingEnabled" value="false"/>
        </settings>
        <!-- 注册类的别名 -->
        <typeAliases>
            <package name="com.eason.mybatis.beans"/>
        </typeAliases>
  • 在MyBatis帮助文档中Ctrl+F查询关键字“lazy”,则可查询出延迟加载的相关参数名称以及取值。
    SSM框架之MyBatis3专题3:关联关系查询
  • 全局属性lazyLoadingEnabled的值只要设置为false,那么,对于关联对象的查询,将采用直接加载。即在查询过主加载对象后,会马上查询关联对象。(对于标签的书写位置,是由约束文件进行规定好的,不能随便写。在<configuration/>标签上点击F2,可查看的顺序以及数量要求。)
  • 标签数量上要求说明用到符号为:
    1、?:表示子标签可以没有,若有的话,最多只能有一个,即小于等于1;
    2、*:表示子标签可以没有,可以有多个,即大于等于0;
    3、+:表示子标签最少要有一个,即大于等于1;
    4、没有符号:表示有且只能够有一个,即等于1;

    2.3 深度延迟加载

  • 修改主配置文件的<settrings/>,将延迟加载开关lazyLoadingEnabled开启(设置为true),将侵入式延迟加载开关aggressiveLazyLoading关闭(设置为false)。
    <!-- 全局参数设置 -->
    <settings>
        <!-- 延迟加载总开关 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 侵入式延迟加载开关 -->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

    2.4 侵入式延迟加载

  • 修改主配置文件的<settings/>,将延迟加载开关lazyLoadingEnabled开启(设置为true),将侵入式延迟加载开关aggressiveLazyLoading也开启(设置为true,默认为true)。
    <!-- 全局参数设置 -->
    <settings>
        <!-- 延迟加载总开关 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 侵入式延迟加载开关 -->
        <setting name="aggressiveLazyLoading" value="true"/>
    </settings>
  • 需要注意的是,该延迟策略也是一种延迟加载,需要在延迟加载开关lazyLoadingEnabled开启时才会其作用。若lazyLoadingEnabled为false,则aggressiveLazyLoading无论取何值,均不会起作用。

--结束END--

本文标题: SSM框架之MyBatis3专题3:关联

本文链接: https://www.lsjlt.com/news/191128.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • SSM框架之MyBatis3专题3:关联
    当查询内容涉及具有关联关系的多个表时,就需要使用关联关系查询。根据表与表之间的关联关系的不同,关联查询分为四种:1、一对一关联查询;2、一对多关联查询;3、多对一关联查询;4、多对多关联查询; 由于日常工作中最常见的关联关系是一对多、多...
    99+
    2023-01-31
    框架 专题 SSM
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作