9.8 多对多级联
我们在第 4 章里面讨论了一对一、一对多的级联以及鉴别器的使用,在现实中还会有多对多的级联。比如用户和角色,一个用户可以拥有多个角色,同样一个角色也可以拥有多个用户。这样就是多对多的关系,有可能我需要查询的一个用户有多少个角色,也有可能查询一个角色下有哪些用户。一般而言,处理多对多的关系采用双向关联的形式。这就意味着你中有我,我中有你。我们一般的做法是把它们拆分为两部分,使角色类和用户类形成一对多的格局,同时用户和角色也是一对多的格局,通过这两个一对多来实现多对多的功能。在很多的情况下,我们有时候只需要用户的信息而不需要角色的信息,所以这个时候我们设置的策略为角色可以延迟加载。同样,对角色而言,用户也需要延迟加载,以保证没有必要的性能丢失。
关于这个模型请参看附录 A 部分,对数据库模型的描述。
首先,我们需要构建 POJO,这里我们在角色类(Role)里面需要构建一个 List 对象,它的泛型为用户类;而在用户类中我们也需要一个 List 属性,其泛型为角色类,于是我们得到了如代码清单 9-48 所示的两个类。
代码清单 9-48:角色类和用户类
/** * 角色类 */ public class Role implements Serializable { private static final long serialVersionUID = -728748601095040554L; private Long id; private String roleName; private String note; //使用 List 保存关联对象 private List<User> userList; ......setter and getter...... } /** * 用户类 */ public class User implements Serializable { private static final long serialVersionUID = -5261054170742785591L; private Long id; private String userName; private String cnname; private SexEnum sex; private String mobile; private String email; private String note; //使用 List 保存关联对象 private List<Role> roleList; ......setter and getter....... }
这样,我们取出角色的时候,关联的用户就可以保存在其属性 userList 中;同样,我们取出用户的时候,其角色也可以保存在属性 roleList 中。这里我们拆解为两个一对多的关联,所以在关联的时候需要用到的是 resultMap 中的 collection 元素,我们需要在映射文件中配置关联关系,方法如代码清单 9-49 所示。
代码清单 9-49:配置关联关系
<!--###################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"> <mapper namespace="com.learn.chapter9.mapper.UserMapper"> <resultMap type="com.learn.chapter9.pojo.User" id="userMapper"> <id property="id" column="id" /> <result property="userName" column="user_name" /> <result property="cnname" column="cnname" /> <result property="sex" column="sex" javaType="com.learn.chapter9.enums.SexEnum" jdbcType="INTEGER" typeHandler="com.learn.chapter9.typehandler.SexTypeHandler"/> <result property="mobile" column="mobile" /> <result property="email" column="email" /> <result property="note" column="note" /> <collection property="roleList" column="id" fetchType="lazy" select="com.learn.chapter9.mapper.RoleMapper.findRoleByUserId"/> </resultMap> <select id="getUser" parameterType="long" resultMap="userMapper"> select id, user_name, cnname, sex, mobile, email, note from t_user where id = #{id} </select> <select id="findUserByRoleId" parameterType="long" resultMap= "userMapper"> select a.id, a.user_name, a.cnname, a.sex, a.mobile, a.email, a.note from t_user a, t_user_role b where a.id = b.role_id and b.user_id = #{userId} </select> </mapper> <!--###################RoleMapper.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"> <mapper namespace="com.learn.chapter9.mapper.RoleMapper"> <resultMap type="com.learn.chapter9.pojo.Role" id="roleMapper"> <id property="id" column="id" /> <result property="roleName" column="role_name" /> <result property="note" column="note"/> <collection property="userList" column="id" fetchType="lazy" select="com.learn.chapter9.mapper.UserMapper.findUserByRoleId" /> </resultMap> <select id ="getRole" resultMap="roleMapper"> select id, role_name, note from t_role where id = #{id} </select> <select id="findRoleByUserId" resultMap="roleMapper"> select a.id, a.role_name, a.note from t_role a, t_user_role b where a.id = b.user_id and b.user_id = #{userId} </select> </mapper>
这个配置和平时的配置大致是一样的,只是在加粗的代码上做了一些处理,它们都用了 select 元素,用全限制名的方式指向了对应的 SQL,这样 MyBatis 就知道用对应的 SQL 把数据取回来,然后保存到你的 POJO 中。这里我们用了 column,它是制定传递的参数,而我们把 fetchType 设置为 lazy,这样配置是为了达到延迟加载的作用。当我们取出角色而不访问其关联用户的时候,MyBatis 便不会发送对应的 SQL 取回我们并不关心的数据,只有当我们访问其关联的数据的时候,它才会发送 SQL 取回我们感兴趣的数据。让我们运行一下代码清单 9-50。
代码清单 9-50:测试多对多关联
sqlSession = SqlSessionFactoryUtil.openSqlSession(); RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class); //取出角色,此时并不发送 SQL 取回用户,因为设置了延迟加载 Role role = roleMapper.getRole(1L); //访问用户,此时才会发送 SQL,取回对应的用户信息 List<User> userList = role.getUserList(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //取出角色,因延迟加载,所以不会发送 SQL 取回角色信息 User user = userMapper.getUser(1L);
运行此代码,我们可以得到下面的结果。
DEBUG 2016-04-07 00:01:04,518 org.apache.ibatis.logging.jdbc. BaseJdbcLogger: ==> Preparing: select id, role_name, note from t_role where id = ? DEBUG 2016-04-07 00:01:04,542 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Long) DEBUG 2016-04-07 00:01:04,745 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 1 DEBUG 2016-04-07 00:01:04,747 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select a.id, a.user_name, a.cnname, a.sex, a.mobile, a.email, a.note from t_user a, t_user_role b where a.id = b.role_id and b.user_id = ? DEBUG 2016-04-07 00:01:04,748 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Long) DEBUG 2016-04-07 00:01:04,831 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 1 DEBUG 2016-04-07 00:01:04,834 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Preparing: select id, user_name, cnname, sex, mobile, email, note from t_user where id = ? DEBUG 2016-04-07 00:01:04,834 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Long) DEBUG 2016-04-07 00:01:04,837 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <== Total: 1
我们打印出来的 SQL 一共是三句,其中通过用户访问角色的 SQL 并没有打印出来,因为我们没有通过用户去访问角色,只有我们访问了角色才会运行,这就是延迟加载。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论