Mybatis中的级联关系
- association 一对一
- collection 一对多
- discrimination 多对多
asscociation
我们模拟一个场景
一个学生有姓名,年龄,住址等等
Student类:
1 |
|
而这里的住址又包括省、市、区
1 |
|
多表级联查询不使用
association
标签1
2
3
4
5
6
7
8
9
10<resultMap type="Student" id="studentResult">
<id property="studentNo" column="student_no"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<!-- 直接赋值 -->
<result property="address.addressNo" column="a_no"/>
<result property="address.province" column="province"/>
<result property="address.city" column="city"/>
<result property="address.area" column="area"/>
</resultMap>查询语句
1
2
3
4
5<select id="findStudentWithAddress"
resultMap="studentResult"
parameterType="Integer">
select * from t_student t1,t_address t2 where t1.a_no=t2.address_no and t1.student_no=#{id}
</select>这种方式采用的是对象方式的级联操作不太好,因为每一次查询都要把所有的属性列在那里,并且修改的时候要全部修改,所以很不方便,尽量模块化
多表级联查询使用
association
标签1
2
3
4
5
6
7
8
9
10
11<resultMap type="Student" id="StudentResult">
<id property="studentNo" column="student_no"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<association property="address" javaType="Address">
<id property="addressNo" column="address_no"/>
<result property="province" column="province"/>
<result property="city" column="city"/>
<result property="area" column="area"/>
</association>
</resultMap>单表查询(N+1的问题)
1
2
3
4
5
6
7
8
9
10
11<resultMap type="Student" id="StudentResult">
<id property="studentNo" column="student_no"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<association property="address" javaType="Student" column="a_no"
selete="getAddress"/>
</resultMap>
<!-- 不一定在同一XML文件中 -->
<select id="getAddress" parameterType="int" resultType="Student">
select * from t_address where address_no=#{_parameter}
</select>这里的
selete
=getAddress
也可以是packageName.xxxMapper.selectID
不用sql联合查询,通过
association
的延迟加载来实现: 什么是延迟加载?如果先查询订单信息即可满足业务要求就不会去查询用户,只有当用到用户信息时再查询用户信息。 对用户信息按需去查询就是延迟加载。
比如上面,只有当调用Student
Bean中的getAddress
方法获取关联的address
数据时,才会触发数据库查询t_address
表。mybatis
默认没有开启延迟加载,需要在SqlMapConfig.xml
中setting
配置。lazyLoadingEnabled
:全局性设置懒加载。如果设为false
,则所有相关联的都会被初始化加载。 允许值有:
true
|false
。默认值:false
aggressiveLazyLoading
:当设置为true
的时候,懒加载的对象可能被任何懒属性全部加载。 否则,每个属性都按需加载。允许值有:
true
|false
。默认值:true
事实上,大多数业务场景显示的表格,都会用到多个表字段。
如果采用延迟加载,会存在N+1问题。
什么是N+1问题呢?
每一个获取Order内部的User对象,都会进行一次select查询
那么当运行过程中执行Order的getList方法时,SQL首先进行1次查询,查询结果如果有N条订单记录,那么实际在每条订单中显示过程中还要运行一次select用户的查询,共n次。
SQL总共执行了n+1次。相比第二种方法的只进行一次联合查询,这种方式无疑是低效的。
如果业务场景的表格显示字段,并没有跨表,那么可以采用延迟加载方式
collection
情景:一个班级有多个学生
班级表
1 | CREATE TABLE `class` ( |
学生表
1 | CREATE TABLE `student` ( |
班级类
1 |
|
学生类
1 |
|
班级Mapper.xml
1 | <mapper namespace="包名.ClassMapper"> |
学生Mapper.xml
1 | <mapper namespace="包名.StudentMapper"> |
使用方法和效率的比较:
全使用单表查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26<resultMap type="Student" id="StudentMap">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="job" property="job" />
<collection property="scores"
ofType="Score"
column="id"
select="queryScoresBySID"/>
</resultMap>
<resultMap type="Score" id="ScoreMap">
<id column="id" property="id" />
<result column="num" property="num" />
<association property="subject"
javaType="Subject"
column="subject"
select="querySubjectBySubId"/>
</resultMap>
<select id="queryStudents" resultMap="StudentMap" >
SELECT id,name,job FROM t_student
</select>
<select id="queryScoresBySID" resultMap="ScoreMap">
SELECT id,num,subject FROM t_score WHERE sid = #{sid}
</select>
<select id="querySubjectBySubId" resultType="Subject" >
SELECT id,name FROM t_subject where id = #{id}
</select>使用左连接进行多表查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<resultMap type="Student" id="StudentMap2">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="job" property="job" />
<collection property="scores" javaType="java.util.ArrayList" ofType="Score">
<id column="id" property="id" />
<result column="num" property="num"/>
<association property="subject" javaType="Subject">
<id column="id" property="id"/>
<result column="name" property="name"/>
</association>
</collection>
</resultMap>
<select id="queryStudents2" resultMap="StudentMap2" >
SELECT stu.id,stu.name name,stu.job,sco.id id,sco.num num,sub.id id,sub.name name
FROM t_student stu LEFT JOIN t_score sco ON stu.id = sco.sid LEFT JOIN t_subject sub ON sco.subject = sub.id
</select>
总结:
方案一:需要执行至少三次sql语句,开启三次事务才能完成本次请求。
方案二:只需要执行一次sql语句,开启一次事务就能完成本次请求
方案二比方案一的效率要高,但是在使用的时候,方案一的代码可重用性要高
如果追求代码重用性可以选择方案一
如果追求运行的性能可以选择方案二
descrimination
待续…