Mybatis 对于多对多关系下的查询提供了集合 (collection) 的概念来解决, collection 属性是 resultMap 高级结果映射的子集, 首先, 在本例中我们使用的是集合元素来解决多对多的查询. 然后你会注意到有一个新的 "ofType" 属性. 这个属性非常重要, 它用来将 JavaBean(或字段)属性的类型和集合存储的类型区分开来. 在集合中 ofType 指的是集合中元素的类型.
首先介绍一下 Demo 情况:
实体类: User(用户)类和 Role(角色)类, 类中的属性在后面代码中贴出
关系: 一个用户可以有多个角色, 一个角色可以赋予多个用户中
数据库表结构: 用户表, 角色表, 中间表(用于存储用户和角色的关系)
本例中实现查询的目标: 查询用户时同时获取用户所拥有的角色的信息(当查询角色时同时获取角色所属用户的信息的情况和下面的例子原理一样, 主要是修改 select 中的 sql 语句)
1. 用户实体类以及角色实体类
- public class User implements Serializable{
- private Integer id;
- private String username;
- private Date birthday;
- private String sex;
- private String address;
- private List<Role> roles;
get 和 set 方法省略 ......
- }
- public class Role implements Serializable{
- private Integer roleId;
- private String roleName;
- private String roleDesc;
- private List<User> users;
get 和 set 方法省略 ......
}
这里主要是增加用户所拥有的角色的 List 属性和角色所属用户的 List 属性, 后面做 resultMap 结果映射的时候使用.
2. 数据库表结构
- DROP TABLE IF EXISTS user;
- CREATE TABLE user (
- id INT(11) NOT NULL auto_increment,
- username VARCHAR(32) NOT NULL COMMENT '用户名称',
- birthday datetime default NULL COMMENT '生日',
- sex char(1) default NULL COMMENT '性别',
- address varchar(256) default NULL COMMENT '地址',
- PRIMARY KEY (id)
- )ENGINE=InnoDB default CHARSET=utf8
- INSERT INTO `user` VALUES ('41', '老王', '2018-02-27 17:47:08', '男', '石家庄');
- INSERT INTO `user` VALUES ('45', '老李', '2018-02-27 17:47:08', '男', '石家庄');
- INSERT INTO `user` VALUES ('46', '老郭', '2018-02-27 17:47:08', '男', '石家庄');
- INSERT INTO `user` VALUES ('47', 'mde', '2019-06-26 15:04:25', '女', '河南');
- INSERT INTO `user` VALUES ('48', 'nan', '2019-08-01 15:04:54', '女', '合肥');
- DROP TABLE IF EXISTS role;
- CREATE TABLE role(
- ID int(11) NOT NULL COMMENT '编号',
- ROLE_NAME VARCHAR(30) DEFAULT NULL COMMENT '角色名称',
- ROLE_DESC VARCHAR(60) DEFAULT NULL COMMENT '角色描述',
- PRIMARY KEY (ID)
- )ENGINE=INNODB DEFAULT CHARSET=utf8
- INSERT INTO role (ID,ROLE_NAME,ROLE_DESC) VALUES (1,'院长','管理整个学院'),(2,'总裁','管理整个公司'),(3,'校长','管理整个学校');
- DROP TABLE IF EXISTS user_role;
- CREATE TABLE user_role(
- UID int(11) NOT NULL COMMENT '用户编号',
- RID INT(11) NOT NULL COMMENT '角色编号',
- PRIMARY KEY (UID,RID),
- CONSTRAINT FK_Reference_10 FOREIGN KEY(RID) REFERENCES role(ID),
- CONSTRAINT FK_Reference_9 FOREIGN KEY(UID) REFERENCES user(id)
- )ENGINE=INNODB DEFAULT CHARSET=utf8;
- INSERT INTO user_role(UID,RID) VALUES(41,1),(45,1),(41,2);
这里主要是增加了中间表.
3. 在 UserDao 接口中声明查询所有用户的方法 findAll();
- /**
- * 查询所有的用户同时查询出所拥有的角色的信息
- *
- * @return
- */
- List<User> findAll();
4. 在 UserDao.xml 中配置 findAll()方法
- <!DOCTYPE mapper
- PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="com.example.dao.UserDao">
- <resultMap id="userMap" type="com.example.domain.User">
- <id property="id" column="id"/>
- <result property="username" column="username"/>
- <result property="birthday" column="birthday"/>
- <result property="sex" column="sex"/>
- <result property="address" column="address"/>
- <collection property="roles" ofType="com.example.domain.Role" resultMap="roleMap"/>
- </resultMap>
- <resultMap id="roleMap" type="com.example.domain.Role">
- <id property="roleId" column="rid"/>
- <result property="roleName" column="ROLE_NAME"/>
- <result property="roleDesc" column="ROLE_DESC"/>
- </resultMap>
- <select id="findAll" resultMap="userMap">
- SELECT u.*,r.ID as rid,r.ROLE_DESC,r.ROLE_NAME FROM user u
- LEFT OUTER JOIN user_role ur on u.id = ur.UID
- LEFT OUTER JOIN role r on ur.RID = r.ID
- </select>
- </mapper>
实现多对多关系查询的主要工作都放在了这里, 首先通过 resultMap 声明用户类的结果映射, id 以及 result 等标签就是 User 类中的基本属性, User 类中的角色属性 roles 通过 collection 集合标签来映射到结果集中,<collection property="roles" ofType="com.example.domain.Role" resultMap="roleMap"/>,property 对应 User 类中声明的 roles 属性, ofType 用于标识集合中元素的类型, resultMap 用于引用其他的结果映射来说明集合中元素的属性, 在这里为 roleMap. 如果 roleMap 不在其他地方使用, 也可以直接将角色的属性直接配置在 collection 属性的子集里, 如以下形式也可以使用.
- <resultMap id="userMap" type="com.example.domain.User">
- <id property="id" column="id"/>
- <result property="username" column="username"/>
- <result property="birthday" column="birthday"/>
- <result property="sex" column="sex"/>
- <result property="address" column="address"/>
- <!--<collection property="roles" ofType="com.example.domain.Role" resultMap="roleMap"/>-->
- <collection property="roles" ofType="com.example.domain.Role">
- <!-- 这里的 rid 是 role 表中的 id, 在 select 语句中为了防止 id 字段在两个表中都出现导致的重复, 所以给 role 的字段 id 起了别名 注意要与 select 中的别名保持一致 -->
- <id property="roleId" column="rid"/>
- <result property="roleName" column="ROLE_NAME"/>
- <result property="roleDesc" column="ROLE_DESC"/>
- </collection>
- </resultMap>
select 中的 SQL 查询语句解释:
<select id="findAll" resultMap="userMap"> SELECT u.*,r.ID as rid,r.ROLE_DESC,r.ROLE_NAME FROM user u LEFT OUTER JOIN user_role ur on u.id = ur.UID LEFT OUTER JOIN role r on ur.RID = r.ID </select>
u.*: 查询 USER 表中所有的属性
r.ID as rid: 对于 role 表中的 id 起一个别名 rid
user u LEFT OUTER JOIN user_role ur on u.id = ur.UID: 前面的表左连接后面的表, 并且连接条件是 User 表中的 id 与 User_role 表中的 uid 相等
5. 测试代码
public class UserDaoTest { private InputStream in; private SqlSession session; private UserDao userDao; private SqlSessionFactory factory; @Before public void init()throws Exception{ // 获取配置文件 in = Resources.getResourceAsStream("SqlMapConfig.xml"); // 获取工厂 factory = new SqlSessionFactoryBuilder().build(in); session = factory.openSession(); userDao = session.getMapper(UserDao.class); } @After public void destory()throws Exception{ session.commit(); session.close(); in.close(); } @Test public void findAllTest(){ List<User> userList = userDao.findAll(); for (User user: userList){ System.out.println("每个用户的信息"); System.out.println(user); System.out.println(user.getRoles()); } } View Code
6. 测试结果
来源: https://www.cnblogs.com/hopeofthevillage/p/11410221.html