MyBatis从入门到精通(十):使用association标签实现嵌套查询
最近在读刘增辉老师所著的《mybatis从入门到精通》一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸!
本篇博客主要讲解使用association标签实现嵌套查询的方法。
1. 明确需求
仍然延用上篇博客中的需求:根据用户id查询用户信息的同时获取该用户的角色信息(假设一个员工只能拥有一个角色)。
在上篇博客中,我们分别使用了3种方式来实现这个需求,但这3个需求都有一个共同点,就是我们使用了多表查询,即查询一次数据库就获取到我们想要的所有数据。
有的同学就说了,我不喜欢多表查询的方式,数据量大的时候会影响性能,这么简单的需求,我可以拆分成两步啊,第一步,先根据用户id查询出用户的信息和用户的角色id(仍然要关联表,只是由3张表关联减为了2张表关联),第二步,根据第一步查询出的角色id再去查询角色信息。
这种方式当然可以,而且使用业务代码就能实现这个逻辑,不过本篇博客我们不讲这种方式,而是通过association标签来实现。
2. 实现方式
因为我们需要根据角色id查询角色的信息,所以我们需要先在sysrolemapper.xml中添加如下查询:
<select id="selectrolebyid" resultmap="rolemap"> select * from sys_role where id = #{id} </select>
这里的rolemap就是我们在上篇博客中定义的,代码如下:
<resultmap id="rolemap" type="com.zwwhnly.mybatisaction.model.sysrole"> <id property="id" column="id"/> <result property="rolename" column="role_name"/> <result property="enabled" column="enabled"/> <result property="createby" column="create_by"/> <result property="createtime" column="create_time" jdbctype="timestamp"/> </resultmap>
然后,在接口sysusermapper中添加如下方法:
/** * 根据用户id获取用户信息和用户的角色信息,嵌套查询方式 * * @param id * @return */ sysuserextend selectuserandrolebyidselect(long id);
接着,在对应的sysusermapper.xml中添加如下代码:
<resultmap id="userrolemapselect" type="com.zwwhnly.mybatisaction.model.sysuserextend" extends="sysusermap"> <association property="sysrole" select="com.zwwhnly.mybatisaction.mapper.sysrolemapper.selectrolebyid" column="{id=role_id}"/> </resultmap>
<select id="selectuserandrolebyidselect" resultmap="userrolemapselect"> select u.id, u.user_name, u.user_password, u.user_email, u.user_info, u.head_img, u.create_time, ur.role_id from sys_user u inner join sys_user_role ur on u.id = ur.user_id where u.id = #{id} </select>
可以发现,我们给association标签添加了select="com.zwwhnly.mybatisaction.mapper.sysrolemapper.selectrolebyid"
,引用的就是我们在sysrolemapper.xml中定义的查询。
还添加了column="{id=role_id}"
,这里的id就是com.zwwhnly.mybatisaction.mapper.sysrolemapper.selectrolebyid需要的参数名称,role_id是参数值,名称要和上面的查询中的最后一列保持一致。
注意事项:如果是多个参数的话,可以使用column="{id=role_id,name=role_name}"
这样的格式。
3. 单元测试
在sysusermappertest类中添加测试方法如下:
@test public void testselectuserandrolebyidselect() { sqlsession sqlsession = getsqlsession(); try { sysusermapper sysusermapper = sqlsession.getmapper(sysusermapper.class); sysuserextend sysuserextend = sysusermapper.selectuserandrolebyidselect(1001l); assert.assertnotnull(sysuserextend); assert.assertnotnull(sysuserextend.getsysrole()); } finally { sqlsession.close(); } }
运行测试代码,测试通过,输出日志如下:
debug [main] - ==> preparing: select u.id, u.user_name, u.user_password, u.user_email, u.create_time, ur.role_id from sys_user u inner join sys_user_role ur on u.id = ur.user_id where u.id = ?
debug [main] - ==> parameters: 1001(long)
trace [main] - <== columns: id, user_name, user_password, user_email, create_time, role_id
trace [main] - <== row: 1001, test, 123456, test@mybatis.tk, 2019-06-27 18:21:07.0, 2
debug [main] - ====> preparing: select * from sys_role where id = ?
debug [main] - ====> parameters: 2(long)
trace [main] - <==== columns: id, role_name, enabled, create_by, create_time
trace [main] - <==== row: 2, 普通用户, 1, 1, 2019-06-27 18:21:12.0
debug [main] - <==== total: 1
debug [main] - <== total: 1
从日志可以看出,分别执行了2次查询,查询了两次数据库。
4. 延迟加载
有的同学可能会说,返回的角色信息我不一定用啊,每次都查询一次数据库,好浪费性能啊,能不能在我使用到角色信息即获取sysrole属性时再去查询数据呢?答案当然是能,那么如何实现呢?
实现延迟加载需要使用association标签的fetchtype属性,该属性有lazy和eager两个值,分别代表延迟加载和积极加载。
所以上面的配置就要修改成:
<association property="sysrole" fetchtype="lazy" select="com.zwwhnly.mybatisaction.mapper.sysrolemapper.selectrolebyid" column="{id=role_id}"/>
为了能看到效果,我们在测试方法中添加一行输出语句:
system.out.println("调用sysuserextend.getsysrole()"); assert.assertnotnull(sysuserextend.getsysrole());
再次运行测试方法,发现输出日志和预期的不一样,在获取sysrole属性前还是查询了2次数据库,这是为什么呢?
这是因为mybatis的全局配置中,有一个aggressivelazyloading参数,如果这个参数的值为ture,会使带有延迟加载属性的对象完整加载,如果为false,则会按需加载,这个参数默认值为ture(3.4.5版本开始默认值改为false),而截止目前,我们使用的版本为3.3.1。
<dependency> <groupid>org.mybatis</groupid> <artifactid>mybatis</artifactid> <version>3.3.1</version> </dependency>
所以我们要在mybatis-config.xml中添加如下配置:
<settings> <!--其他配置--> <setting name="aggressivelazyloading" value="false"/> </settings>
再次运行测试方法,发现输出日志和预期的一样了:
debug [main] - ==> preparing: select u.id, u.user_name, u.user_password, u.user_email, u.create_time, ur.role_id from sys_user u inner join sys_user_role ur on u.id = ur.user_id where u.id = ?
debug [main] - ==> parameters: 1001(long)
trace [main] - <== columns: id, user_name, user_password, user_email, create_time, role_id
trace [main] - <== row: 1001, test, 123456, test@mybatis.tk, 2019-06-27 18:21:07.0, 2
debug [main] - <== total: 1
调用sysuserextend.getsysrole()
debug [main] - ==> preparing: select * from sys_role where id = ?
debug [main] - ==> parameters: 2(long)
trace [main] - <== columns: id, role_name, enabled, create_by, create_time
trace [main] - <== row: 2, 普通用户, 1, 1, 2019-06-27 18:21:12.0
debug [main] - <== total: 1
有的同学可能又会说,你现在把全局的aggressivelazyloading改为了false,我能不能在触发某个方法时将所有的数据都加载进来呢?答案当然是能(不然怎么往下写,哈哈),那么如何实现呢?
mybatis提供了参数lazyloadtriggermethods,这个参数的含义是,当调用配置中的方法时,加载全部的延迟加载数据,默认值为“equals,clone,hashcode,tostring”。
简单修改下测试方法的代码:
system.out.println("调用sysuserextend.equals(null)"); sysuserextend.equals(null); system.out.println("调用sysuserextend.getsysrole()"); assert.assertnotnull(sysuserextend.getsysrole());
再次运行测试方法,输出的部分日志如下:
调用sysuserextend.equals(null)
debug [main] - ==> preparing: select * from sys_role where id = ?
debug [main] - ==> parameters: 2(long)
trace [main] - <== columns: id, role_name, enabled, create_by, create_time
trace [main] - <== row: 2, 普通用户, 1, 1, 2019-06-27 18:21:12.0
debug [main] - <== total: 1
调用sysuserextend.getsysrole()
从日志可以看出,调用equals方法后,就触发了延迟加载属性的查询。
5. 总结
使用association标签实现嵌套查询,用到的属性总结如下:
1)select:另一个映射查询的id,mybatis会额外执行这个查询获取嵌套对象的结果。
2)column:将主查询中列的结果作为嵌套查询的参数,配置方式如column="{prop1=col1,prop2=col2}",prop1和prop2将作为嵌套查询的参数。
3)fetchtype:数据加载方式,可选值为lazy和eager,分别为延迟加载和积极加载。
4)如果要使用延迟加载,除了将fetchtype设置为lazy,还需要注意全局配置aggressivelazyloading的值应该为false。这个参数在3.4.5版本之前默认值为ture,从3.4.5版本开始默认值改为false。
5)mybatis提供的lazyloadtriggermethods参数,支持在触发某方法时直接触发延迟加载属性的查询,如equals()方法。
6. 源码及参考
源码地址:,欢迎下载。
刘增辉《mybatis从入门到精通》
上一篇: php函数与传递参数实例分析