前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SSM框架之MyBatis3专题3:关联

SSM框架之MyBatis3专题3:关联

作者头像
py3study
发布2020-01-06 10:37:10
7140
发布2020-01-06 10:37:10
举报
文章被收录于专栏:python3python3

1 关联查询

  • 当查询内容涉及具有关联关系的多个表时,就需要使用关联关系查询。根据表与表之间的关联关系的不同,关联查询分为四种: 1、一对一关联查询; 2、一对多关联查询; 3、多对一关联查询; 4、多对多关联查询;
  • 由于日常工作中最常见的关联关系是一对多、多对一与多对多,所以这里就不专门只讲解一对一关联查询了,其解决方案与多对一解决方案是相同的。 1.1 一对多关联查询
  • 这里的一对多关联查询是指,在查询一方对象的时候,同时将其所关联的多方对象也都查询出来。
  • 下面以国家Country与部长Minister间的一对多关系进行演示。 1.1.1 定义实体
  • 在定义实体时,若定义的是双向关联,即双方的属性中均有对方对象作为域属性出现,那么它们在定义各自的toString()方法时需要注意,只让某一方可以输出另一方即可,不要让双方的toString()方法均可输出对方。这样会形成递归调用,程序出错。 1.1.2 定义数据库表
SSM框架之MyBatis3专题3:关联关系查询
SSM框架之MyBatis3专题3:关联关系查询
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:关联关系查询
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:关联关系查询
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:关联关系查询
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); } 2 延迟加载
  • 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:关联关系查询
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无论取何值,均不会起作用。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-09-20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 关联查询
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档