Mybatis 多對多


在前面的章節中,我們學習了一對多,多對一的關係,現在我們來看看 Mybatis 中的多對多應用。

mybatis3.0 新增了association和collection標籤專門用於對多個相關實體類資料進行級聯查詢,但仍不支援多個相關實體類資料的級聯儲存和級聯刪除操作。因此在進行實體類多對多對映表設計時,需要專門建立一個關聯物件類對相關實體類的關聯關係進行描述。下文將以「User」和「Group"兩個實體類之間的多對多關聯對映為例進行CRUD操作。

1、應用場景

假設專案中存在使用者和使用者組,從一個使用者讀取出它所在的使用者組,從一個使用者組也知道這個組內的所有使用者資訊。

2、先做一些準備工作

我們首先在建立一個 java 工程,工程名稱為:mybatis06-many2many(下載),還需要建立三張表,它們分別是使用者表 user,使用者組表 group 和 使用者組對映表 user_group ,一個戶使用者可以在多個使用者組中,一個使用者組中有多個使用者。專案工程結構如下:


user表的結構和資料:

CREATE TABLE `user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(64) NOT NULL DEFAULT '',
  `mobile` varchar(16) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'yiibai', '13838009988');
INSERT INTO `user` VALUES ('2', 'User-name-1', '13838009988');

使用者組 group 表的結構和資料:

CREATE TABLE `group` (
  `group_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `group_name` varchar(254) NOT NULL DEFAULT '',
  PRIMARY KEY (`group_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of group
-- ----------------------------
INSERT INTO `group` VALUES ('1', 'Group-1');
INSERT INTO `group` VALUES ('2', 'Group-2');
使用者組對映表 user_group 的結構和資料:
CREATE TABLE `user_group` (
  `user_id` int(10) unsigned NOT NULL DEFAULT '0',
  `group_id` int(10) unsigned NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user_group
-- ----------------------------
INSERT INTO `user_group` VALUES ('1', '1');
INSERT INTO `user_group` VALUES ('2', '1');
INSERT INTO `user_group` VALUES ('1', '2');

從上面應該看出,使用者ID為1同時在使用者組ID為 1 和 2 中,而使用者ID為 2 僅在一個使用者組ID為1中。

2、建立表對應的 JavaBean 物件

這個例子中,我們需要在包 com.yiibai.pojo 下建立三個類,它們分別是: User.java 、Group.java 和 UserGroup.java,讓我們一個一個地來看它們的程式碼,User.java 類的程式碼如下:

package com.yiibai.pojo;

import java.util.List;

/** 
 * @describe: User
 * @author: Yiibai 
 * @version: V1.0
 * @copyright https://www.tw511.com
 */  
public class User {
	private int id;
	private String username;
	private String mobile;
	private List<Group> groups;
	public List<Group> getGroups() {
		return groups;
	}
	public void setGroups(List<Group> groups) {
		this.groups = groups;
	}
	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 getMobile() {
		return mobile;
	}
	public void setMobile(String mobile) {
		this.mobile = mobile;
	}
	
}
Group.java 類的程式碼如下:
package com.yiibai.pojo;

import java.util.List;

/** 
 * @describe: Group
 * @author: Yiibai 
 * @version: V1.0
 * @copyright https://www.tw511.com
 */  
public class Group {
	private int groupId;
	private String groupName;
	private List<User> users;
	
	public List<User> getUsers() {
		return users;
	}
	public void setUsers(List<User> users) {
		this.users = users;
	}
	public int getGroupId() {
		return groupId;
	}
	public void setGroupId(int groupId) {
		this.groupId = groupId;
	}
	public String getGroupName() {
		return groupName;
	}
	public void setGroupName(String groupName) {
		this.groupName = groupName;
	}
	
}
UserGroup.java 類(使用者和使用者組的關係對映)的程式碼如下:
package com.yiibai.pojo;

public class UserGroup {
	private int userId;  
    private int groupId;
	public int getUserId() {
		return userId;
	}
	public void setUserId(int userId) {
		this.userId = userId;
	}
	public int getGroupId() {
		return groupId;
	}
	public void setGroupId(int groupId) {
		this.groupId = groupId;
	}
 
    
}

3、組態檔案

在這一章節中,要用到的組態檔案有四個,一個是 mybatis 的主組態檔案:src/config/Configure.xml ,另外就是上面三個Bean類對應的組態檔案,如,User.java 對應的組態檔案 User.xml,等,我們先來看看 src/config/Configure.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>
	<typeAliases>
		<typeAlias alias="User" type="com.yiibai.pojo.User" />
		<typeAlias alias="UserGroup" type="com.yiibai.pojo.UserGroup" />
		<typeAlias alias="Group" type="com.yiibai.pojo.Group" />
	</typeAliases>

	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver" />
				<property name="url" value="jdbc:mysql://127.0.0.1:3306/yiibai" />
				<property name="username" value="root" />
				<property name="password" value="" />
			</dataSource>
		</environment>
	</environments>

	<mappers>
		<!-- // power by https://www.tw511.com -->
		<mapper resource="com/yiibai/maper/UserMaper.xml" />
		<mapper resource="com/yiibai/maper/GroupMaper.xml" />
		<mapper resource="com/yiibai/maper/UserGroupMaper.xml" />
	</mappers>
</configuration>
Group.java 對應的組態檔案 src/com/yiibai/maper/Group.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.yiibai.maper.GroupMaper">

	<parameterMap type="Group" id="parameterGroupMap">
		<parameter property="groupId"/>
		<parameter property="groupName"/>
	</parameterMap>
	<insert id="insertGroup" parameterMap="parameterGroupMap">
		INSERT INTO `group` (group_name)
		VALUES(#{groupName}); 
	</insert>

	<resultMap type="Group" id="resultGroupMap_1">
		<result property="id" column="id" />
		<result property="groupName" column="group_name" />
		<collection property="users" column="group_id"
			select="com.yiibai.maper.UserGroupMaper.getUsersByGroupId" />
	</resultMap>
	<select id="getGroup" resultMap="resultGroupMap_1"
		parameterType="int">
		SELECT *
		FROM `group`
		WHERE group_id=#{id}
	</select>
</mapper>	 

User.java 對應的組態檔案 src/com/yiibai/maper/User.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.yiibai.maper.UserMaper">
	<parameterMap type="User" id="parameterUserMap">
		<parameter property="id"/>
		<parameter property="username"/>
		<parameter property="mobile"/>
	</parameterMap>
	
	<insert id="insertUser" parameterMap="parameterUserMap">	
		INSERT INTO user(username,mobile)
		VALUES(#{username},#{mobile});
	</insert>   
	
	<resultMap type="User" id="resultUser">
		<result property="id" column="group_id"/>
		<result property="name" column="name"/>
		<collection property="groups" column="id" select="com.yiibai.maper.UserGroupMaper.getGroupsByUserId"/>
	</resultMap>
	
	<select id="getUser" resultMap="resultUser" parameterType="int">
		SELECT *
		FROM user
		WHERE id=#{id}
	</select>    
</mapper>
UserGroup.java 對應的組態檔案 src/com/yiibai/maper/UserGroup.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.yiibai.maper.UserGroupMaper">
	<parameterMap type="UserGroup" id="parameterUserGroupMap">
		<parameter property="userId"/>
		<parameter property="groupId"/>
	</parameterMap>
	
	<insert id="insertUserGroup"  parameterMap="parameterUserGroupMap">
		INSERT INTO user_group(user_id, group_id)
		VALUES(#{userId},#{groupId})
	</insert>
	
	<!-- 根據一個使用者組ID,檢視這個使用者組下的所有使用者 -->
	<resultMap type="User" id="resultUserMap_2">
		<result property="id" column="id"/>
		<result property="username" column="username"/>
		<result property="mobile" column="mobile"/>
	</resultMap>
	
	<select id="getUsersByGroupId" resultMap="resultUserMap_2" parameterType="int">
		SELECT u.*, ug.group_id
		FROM user u, user_group ug
		WHERE u.id=ug.user_id AND ug.group_id=#{group_id}
	</select>
	
	<!-- 根據一個使用者ID,檢視這個使用者所對應的組-->
	<resultMap type="Group" id="resultGroupMap_2">
		<result property="groupId" column="group_id"/>
		<result property="groupName" column="group_name"/>
	</resultMap> 
	
	<select id="getGroupsByUserId" resultMap="resultGroupMap_2" parameterType="int">
		SELECT g.*, u.user_id
		FROM group g, user_group u
		WHERE g.group_id=u.group_id AND u.user_id=#{user_id}
	</select>
</mapper>	 

註:在上面的組態檔案中,使用到了 <association>和  <clollection>標籤,關聯對應的 User 類和 Group類。

4、測試程式執行

到這裡,整個工作準備得已經差不多了,我們建立一個主類來測試上面程式,在 src 下建立一個 Main.java,程式碼如下:

import java.io.Reader;
import java.text.MessageFormat;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import com.yiibai.maper.GroupMaper;
import com.yiibai.maper.UserGroupMaper;
import com.yiibai.maper.UserMaper;
import com.yiibai.pojo.Group;
import com.yiibai.pojo.User;
import com.yiibai.pojo.UserGroup;

public class Main {
	private static SqlSessionFactory sqlSessionFactory;
	private static Reader reader;

	static {
		try {
			reader = Resources.getResourceAsReader("config/Configure.xml");
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static SqlSessionFactory getSession() {
		return sqlSessionFactory;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// testAddGroup();
		// testAddUser();
		// testAddUserGroup();
		testGetGroupAndUsers();
		
	}
	
	public static void testGetGroupAndUsers() {
		UserGroup userGroup = new UserGroup();
		SqlSession session = sqlSessionFactory.openSession();
		try {
			GroupMaper groupMaper = session.getMapper(GroupMaper.class);
			Group group = groupMaper.getGroup(1);
			System.out.println("Group => " + group.getGroupName());
			List<User> users = group.getUsers();
			for (User user : users) {
				System.out.println("\t:" + user.getId() + "\t"
						+ user.getUsername());
			}
		} finally {
			session.close();
		}
	}

	public static void testAddUserGroup() {
		UserGroup userGroup = new UserGroup();
		userGroup.setGroupId(1);
		userGroup.setUserId(2);
		SqlSession session = sqlSessionFactory.openSession();
		try {
			UserGroupMaper userGroupMaper = session
					.getMapper(UserGroupMaper.class);
			userGroupMaper.insertUserGroup(userGroup);

			session.commit();
		} finally {
			session.close();
		}

	}

	public static void testAddUser() {
		// TODO Auto-generated method stub
		SqlSession session = sqlSessionFactory.openSession();
		try {
			User user = new User();
			user.setUsername("User-name-1");
			user.setMobile("13838009988");
			UserMaper userMaper = session.getMapper(UserMaper.class);
			userMaper.insertUser(user);
			session.commit();
			// System.out.println(user.getGroupId());
		} finally {
			session.close();
		}
	}

	public static void testAddGroup() {
		// TODO Auto-generated method stub
		SqlSession session = sqlSessionFactory.openSession();
		try {
			Group group = new Group();
			group.setGroupName("使用者組-1");
			GroupMaper groupMapper = session.getMapper(GroupMaper.class);
			groupMapper.insertGroup(group);
			session.commit();
			System.out.println(group.getGroupId());
		} finally {
			session.close();
		}
	}
}

執行上述程式,得出結果:

Group => Group-1
	:1	yiibai
	:2	User-name-1