mybatis多表查询之多对多关系查询的实现-xml方式
2019-08-26 03:10 全me村的希望 阅读(10164) 评论(1) 编辑 收藏 举报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.测试代码
1 public class UserDaoTest { 2 private InputStream in; 3 private SqlSession session; 4 5 private UserDao userDao; 6 private SqlSessionFactory factory; 7 @Before 8 public void init()throws Exception{ 9 //获取配置文件 10 in = Resources.getResourceAsStream("SqlMapConfig.xml"); 11 //获取工厂 12 factory = new SqlSessionFactoryBuilder().build(in); 13 14 session = factory.openSession(); 15 16 userDao = session.getMapper(UserDao.class); 17 } 18 @After 19 public void destory()throws Exception{ 20 session.commit(); 21 session.close(); 22 in.close(); 23 } 24 @Test 25 public void findAllTest(){ 26 List<User> userList = userDao.findAll(); 27 for (User user: userList){ 28 System.out.println("每个用户的信息"); 29 System.out.println(user); 30 System.out.println(user.getRoles()); 31 } 32 }
6.测试结果