在這篇博客中,我來介紹下mybatis中的多對多查詢的案例,在mybatis中,如何使用ResultMap來實現多對多的查詢?
案例:一個user可以有很多role,一個role可以有很多entitlement.
一,數據庫表的準備
<span style="font-size:18px;">DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(20) COLLATE utf8_bin NOT NULL,
`sex` varchar(20) COLLATE utf8_bin NOT NULL,
`birthday` date NOT NULL,
`address` varchar(20) COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('5', '步驚雲', '男', '2016-03-12', '安徽');
INSERT INTO `user` VALUES ('6', '聶風', '男', '2016-03-01', '上海');
INSERT INTO `user` VALUES ('7', '楊康', '男', '2016-03-03', '杭州');</span>
<span style="font-size:18px;">-- ----------------------------
-- Table structure for `role`
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`rolename` varchar(20) COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES ('1', '管理員');
INSERT INTO `role` VALUES ('2', '用戶模塊管理員');
INSERT INTO `role` VALUES ('3', '國家模塊管理員');</span>
<span style="font-size:18px;">DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`user_id` int(11) NOT NULL,
`role_id` int(11) NOT NULL,
KEY `user_foreign_key` (`user_id`),
KEY `role_foreign_key` (`role_id`),
CONSTRAINT `role_foreign_key` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`),
CONSTRAINT `user_foreign_key` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES ('5', '1');
INSERT INTO `user_role` VALUES ('5', '2');
INSERT INTO `user_role` VALUES ('6', '2');
INSERT INTO `user_role` VALUES ('7', '1');
INSERT INTO `user_role` VALUES ('7', '2');
INSERT INTO `user_role` VALUES ('7', '3');</span>
<span style="font-size:18px;">DROP TABLE IF EXISTS `entitlement`;
CREATE TABLE `entitlement` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`entitlementname` varchar(20) COLLATE utf8_bin NOT NULL,
`role_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `role_entitlement_foreign_key` (`role_id`),
CONSTRAINT `role_entitlement_foreign_key` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- ----------------------------
-- Records of entitlement
-- ----------------------------
INSERT INTO `entitlement` VALUES ('1', '添加', '1');
INSERT INTO `entitlement` VALUES ('2', '修改', '1');
INSERT INTO `entitlement` VALUES ('3', '添加', '2');
INSERT INTO `entitlement` VALUES ('4', '修改', '2');
INSERT INTO `entitlement` VALUES ('5', '刪除', '2');
INSERT INTO `entitlement` VALUES ('6', '添加', '3');</span>
二,與數據庫表對應的entity的類結構
package com.npf.entity;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
public class User implements Serializable {
private static final long serialVersionUID = 8341195547209659206L;
private int id;
private String username;
private String sex;
private Date birthday;
private String address;
private List<Role> roles;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", sex=" + sex
+ ", birthday=" + birthday + ", address=" + address
+ ", roles=" + roles + "]";
}
}
package com.npf.entity;
import java.io.Serializable;
import java.util.List;
/**
*
* @author Jack
*
*/
public class Role implements Serializable{
private static final long serialVersionUID = -3687052298547500972L;
private Integer id;
private String rolename;
private List<Entitlement> entitlements;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getRolename() {
return rolename;
}
public void setRolename(String rolename) {
this.rolename = rolename;
}
public List<Entitlement> getEntitlements() {
return entitlements;
}
public void setEntitlements(List<Entitlement> entitlements) {
this.entitlements = entitlements;
}
@Override
public String toString() {
return "Role [id=" + id + ", rolename=" + rolename + ", entitlements="
+ entitlements + "]";
}
}
package com.npf.entity;
import java.io.Serializable;
/**
*
* @author Jack
*
*/
public class Entitlement implements Serializable{
private static final long serialVersionUID = -1630305760044848905L;
private Integer id;
private String entitlementname;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getEntitlementname() {
return entitlementname;
}
public void setEntitlementname(String entitlementname) {
this.entitlementname = entitlementname;
}
@Override
public String toString() {
return "Entitlement [id=" + id + ", entitlementname=" + entitlementname
+ "]";
}
}
三,mapper接口和mapper.xml配置文件(注意這兩個文件需要放在同一個包下面)
package com.npf.user.mapper;
import java.util.List;
import java.util.Map;
import com.npf.entity.User;
import com.npf.entity.vo.UserQueryVO;
public interface UserMapper {
public List<User> findUserRoleEntitlement() throws Exception;
}
<?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.npf.user.mapper.UserMapper">
<resultMap type="com.npf.entity.User" id="findUserRoleEntitlementResultMap">
<id column="user_id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<result column="address" property="address"/>
<collection property="roles" ofType="com.npf.entity.Role">
<id column="role_id" property="id"/>
<result column="rolename" property="rolename"/>
<collection property="entitlements" ofType="com.npf.entity.Entitlement">
<id column="entitlement_id" property="id"/>
<result column="entitlementname" property="entitlementname"/>
</collection>
</collection>
</resultMap>
<select id="findUserRoleEntitlement" resultMap="findUserRoleEntitlementResultMap">
select
`user`.id as user_id,
`user`.username as username,
`user`.sex as sex,
`user`.birthday as birthday,
`user`.address as address,
user_role.role_id,
role.rolename as rolename,
entitlement.entitlementname,
entitlement.id as entitlement_id
from user inner JOIN user_role ON
`user`.id = user_role.user_id
inner JOIN role on role.id = user_role.role_id
inner JOIN entitlement ON entitlement.role_id = role.id;
</select>
</mapper>
四,mybatis的核心配置文件
SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties"></properties>
<!-- 和spring整合後 environments配置將廢除-->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事務管理,事務控制由mybatis-->
<transactionManager type="JDBC" />
<!-- 數據庫連接池,由mybatis管理-->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<!-- 加載映射文件 -->
<mappers>
<package name="com.npf.user.mapper"/>
</mappers>
</configuration>
五,獲取SqlSessionFactory的工具類
package com.npf.utils;
import java.io.IOException;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public final class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory;
private static final String resource = "SqlMapConfig.xml";
private MyBatisUtils(){}
public static SqlSessionFactory getSqlSessionFactory(){
if(sqlSessionFactory==null){
synchronized (MyBatisUtils.class) {
if(sqlSessionFactory==null){
try {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream(resource));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
return sqlSessionFactory;
}
}
六,測試
package com.npf.test;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Before;
import org.junit.Test;
import com.npf.entity.User;
import com.npf.user.mapper.UserMapper;
import com.npf.utils.MyBatisUtils;
public class UserMapperTest {
private SqlSessionFactory sessionFactory;
@Before
public void setup(){
sessionFactory = MyBatisUtils.getSqlSessionFactory();
}
@Test
public void findUserRoleEntitlementTest() throws Exception{
SqlSession session = sessionFactory.openSession();
UserMapper usermapper = session.getMapper(UserMapper.class);
List<User> users = usermapper.findUserRoleEntitlement();
for(User user : users){
System.out.println(user);
}
}
}
七,運行結果
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 939437404.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@37fead5c]
DEBUG [main] - ==> Preparing: select `user`.id as user_id, `user`.username as username, `user`.sex as sex, `user`.birthday as birthday, `user`.address as address, user_role.role_id, role.rolename as rolename, entitlement.entitlementname, entitlement.id as entitlement_id from user inner JOIN user_role ON `user`.id = user_role.user_id inner JOIN role on role.id = user_role.role_id inner JOIN entitlement ON entitlement.role_id = role.id;
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 14
User [id=5, username=步驚雲, sex=男, birthday=Sat Mar 12 00:00:00 CST 2016, address=安徽, roles=[Role [id=1, rolename=管理員, entitlements=[Entitlement [id=1, entitlementname=添加], Entitlement [id=2, entitlementname=修改]]], Role [id=2, rolename=用戶模塊管理員, entitlements=[Entitlement [id=3, entitlementname=添加], Entitlement [id=4, entitlementname=修改], Entitlement [id=5, entitlementname=刪除]]]]]
User [id=6, username=聶風, sex=男, birthday=Tue Mar 01 00:00:00 CST 2016, address=上海, roles=[Role [id=2, rolename=用戶模塊管理員, entitlements=[Entitlement [id=3, entitlementname=添加], Entitlement [id=4, entitlementname=修改], Entitlement [id=5, entitlementname=刪除]]]]]
User [id=7, username=楊康, sex=男, birthday=Thu Mar 03 00:00:00 CST 2016, address=杭州, roles=[Role [id=1, rolename=管理員, entitlements=[Entitlement [id=1, entitlementname=添加], Entitlement [id=2, entitlementname=修改]]], Role [id=2, rolename=用戶模塊管理員, entitlements=[Entitlement [id=3, entitlementname=添加], Entitlement [id=4, entitlementname=修改], Entitlement [id=5, entitlementname=刪除]]], Role [id=3, rolename=國家模塊管理員, entitlements=[Entitlement [id=6, entitlementname=添加]]]]]
八,總結
1. 使用resultMap是針對那些對查詢結果映射有特殊要求的功能,,比如特殊要求映射成list中包括 多個list。
2. resultType:作用:將查詢結果按照sql列名pojo屬性名一致性映射到pojo中。
場合:常見一些明細記錄的展示,比如用戶購買商品明細,將關聯查詢信息全部展示在頁面時,此時可直接使用 resultType將每一條記錄映射到pojo中,在前端頁面遍歷list(list中是pojo)即可。
resultMap:使用association和collection完成一對一和一對多高級映射(對結果有特殊的映射要求)。
association:作用:將關聯查詢信息映射到一個pojo對象中。
場合:爲了方便查詢關聯信息可以使用association將關聯訂單信息映射爲用戶對象的pojo屬性中,比如:查詢訂單 及關聯用戶信息。使用resultType無法將查詢結果映射到pojo對象的pojo屬性中,根據對結果集查詢遍歷的需 要選擇使用resultType還是resultMap。
collection:作用:將關聯查詢信息映射到一個list集合中。
場合:爲了方便查詢遍歷關聯信息可以使用collection將關聯信息映射到list集合中,比如:查詢用戶權限範圍模塊 及模塊下的菜單,可使用collection將模塊映射到模塊list中,將菜單列表映射到模塊對象的菜單list屬性中,這樣的 作的目的也是方便對查詢結果集進行遍歷查詢。如果使用resultType無法將查詢結果映射到list集合中。