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.測試結果

