Hibernate多對多的關係映射,詳解(代碼+圖解)與應用 舉個栗子,搞的清楚

Hibernate多對多的關係映射

多對多代表着,兩個表之間(或者羣體之間)存在着多重關係,一個羣羣體當中對象可以與另外一個羣體之間,產生聯繫,相反另外一個羣體當中的對象也一樣

舉一個簡單的例子:
在遊戲當中有玩家和角色,玩家可以選擇多個不同的角色,同時角色也可以被多個玩家同時選擇
或者一個遊戲當中有多種不同的武器,玩家可以選擇多個不同的武器,同時武器也可以被不同的玩家選擇
在這裏插入圖片描述

在這裏插入圖片描述
或者課程與學生之間在這裏插入圖片描述

數據庫表能夠描述實體數據之間的關係,通過對象也可以進行描述,關係映射就是將關聯關係映射到數據庫當中,在對象模型當中就是一個或者多個引用,在Hibernate當中採用Java對象關係來描述數據表之間的關係
如下:
在這裏插入圖片描述
上述圖當中,爲多對多的關係,在A類當中定義了B類的set集合,在B類集合當中定義A類類型的set集合,set集合是爲了避免數據的重複

一、Hibernate多對多關係的配置

1、創建表

用戶表

CREATE TABLE `sys_user` (
  `user_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '用戶id',
  `user_code` varchar(32) NOT NULL COMMENT '用戶賬號',
  `user_name` varchar(64) NOT NULL COMMENT '用戶名稱',
  `user_password` varchar(32) NOT NULL COMMENT '用戶密碼',
  `user_state` char(1) NOT NULL COMMENT '1:正常,0:暫停',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

角色表

CREATE TABLE `sys_role` (
  `role_id` bigint(32) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(32) NOT NULL COMMENT '角色名稱',
  `role_memo` varchar(128) DEFAULT NULL COMMENT '備註',
  PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

中間表

CREATE TABLE `sys_user_role` (
  `role_id` BIGINT(32) NOT NULL COMMENT '角色id',
  `user_id` BIGINT(32) NOT NULL COMMENT '用戶id',
  PRIMARY KEY (`role_id`,`user_id`),
  KEY `FK_user_role_user_id` (`user_id`),
  CONSTRAINT `FK_user_role_role_id` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`role_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `FK_user_role_user_id` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`user_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=INNODB DEFAULT CHARSET=utf8;

2、相關配置

(1)引入相關架包
在這裏插入圖片描述
(2)核心配置文件(Hibernate.cfg.xml)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<session-factory>
		<!-- 鏈接數據庫的基本參數 -->
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.url">jdbc:mysql:///hibernate_day03</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password">root</property>
		<!-- 配置hibernate的方言的 目的是生成對應不同數據的語句 -->
		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
		<!-- 可選配置 -->
		<!-- 打印SQL -->
		<property name="hibernate.show_sql">true</property>
		<!-- 格式化sql -->
		<property name="hibernate.format_sql">true</property>
		<!-- 自動創建表 -->
		<property name="hibernate.hbm2ddl.auto">create</property>
		<!-- 配置C3P0連接池 -->
		<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
		<!--在連接池中可用的數據庫連接的最少數目 -->
		<property name="c3p0.min_size">5</property>
		<!--在連接池中所有數據庫連接的最大數目 -->
		<property name="c3p0.max_size">20</property>
		<!--設定數據庫連接的過期時間,以秒爲單位, 如果連接池中的某個數據庫連接處於空閒狀態的時間超過了timeout時間,就會從連接池中清除 -->
		<property name="c3p0.timeout">120</property>
		<!--3000秒檢查所有連接池中的空閒連接 以秒爲單位 -->
		<property name="c3p0.idle_test_period">3000</property>
		<!-- 設置事務隔離級別 -->
		<property name="hibernate.connection.isolaction">4</property>
		<!-- 配置當前線程來綁定的session -->
		<property name="hibernate.current_session_context_class">thread</property>
		<!-- 引入兩個映射文件 -->
		<mapping
			resource="com/itzheng/hibernate/domain/Customer.hbm.xml" />
		<mapping
			resource="com/itzheng/hibernate/domain/LinkMan.hbm.xml" />
	</session-factory>
</hibernate-configuration>

(3)log4j.properties

### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c\:mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### set log levels - for more verbose logging change 'info' to 'debug' ###
# error warn info debug trace
log4j.rootLogger= info, stdout

(4)工具類

package com.itzheng.hibernate.utils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
/*
Hibernate的工具類
做hibernate的小練習,對configuration的configure()方法很好奇,爲啥創建的對象還要執行這個方法呢。
Configuration cfg = new Configuration().configure();
原來configure()方法默認會在classpath下面尋找hibernate.cfg.xml文件,
如果沒有找到該文件,系統會打印如下信息並拋出HibernateException異常。
其實不使用configure()方法也可以
Configuration cfg = new Configuration();
這時hibernate會在classpath下面尋找hibernate.properties文件,
如果沒有找到該文件,系統會打印如下信息並拋出HibernateException異常。
 */
public class HibernateUtils {
	public static final Configuration cfg;
	public static final SessionFactory sf;
	static {
		cfg = new Configuration().configure();// 獲取與數據庫的鏈接的配置文件
		sf = cfg.buildSessionFactory();//開啓事務建立與數據庫之間的鏈接
	}
	public static Session openSession() {

		return sf.openSession();
	}
	public static Session getCurrentSession() {
		return sf.getCurrentSession();
	}
}

3、創建實體

(1)創建用戶的實體

package com.itzheng.hibernate.domain;
/*
 * 用戶的實體
CREATE TABLE `sys_user` (
  `user_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '用戶id',
  `user_code` varchar(32) NOT NULL COMMENT '用戶賬號',
  `user_name` varchar(64) NOT NULL COMMENT '用戶名稱',
  `user_password` varchar(32) NOT NULL COMMENT '用戶密碼',
  `user_state` char(1) NOT NULL COMMENT '1:正常,0:暫停',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
*/
public class User {
	private Long user_id;
	private String user_code;
	private String user_name;
	private String user_password;
	private String user_state;
	public Long getUser_id() {
		return user_id;
	}
	public void setUser_id(Long user_id) {
		this.user_id = user_id;
	}
	public String getUser_code() {
		return user_code;
	}
	public void setUser_code(String user_code) {
		this.user_code = user_code;
	}
	public String getUser_name() {
		return user_name;
	}
	public void setUser_name(String user_name) {
		this.user_name = user_name;
	}
	public String getUser_password() {
		return user_password;
	}
	public void setUser_password(String user_password) {
		this.user_password = user_password;
	}
	public String getUser_state() {
		return user_state;
	}
	public void setUser_state(String user_state) {
		this.user_state = user_state;
	}
}

(2)創建角色的實體

package com.itzheng.hibernate.domain;
/*
 * 角色的實體
CREATE TABLE `sys_role` (
  `role_id` bigint(32) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(32) NOT NULL COMMENT '角色名稱',
  `role_memo` varchar(128) DEFAULT NULL COMMENT '備註',
  PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
 */
public class Role {
	private Long role_id;
	private String role_name;
	private String role_memo;
	public Long getRole_id() {
		return role_id;
	}
	public void setRole_id(Long role_id) {
		this.role_id = role_id;
	}
	public String getRole_name() {
		return role_name;
	}
	public void setRole_name(String role_name) {
		this.role_name = role_name;
	}
	public String getRole_memo() {
		return role_memo;
	}
	public void setRole_memo(String role_memo) {
		this.role_memo = role_memo;
	}
}

4、設置多對多的關係(放置對方的集合)

(1)一個用戶選擇多個角色
在這裏插入圖片描述
同時生成get和set方法
(2)一個角色被多個用戶選擇
在這裏插入圖片描述
同時生成get和set方法

4、創建映射

在這裏插入圖片描述
User.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="com.itzheng.hibernate.domain.User" table="sys_user">
		<!-- 建立oid與主鍵的映射 -->
		<id name="user_id" column="user_id">
			<generator class="native" />
		</id>
		<!-- 建立普通屬性與字段的映射 -->
		<property name="user_code" column="user_code"/>
		<property name="user_name" column="user_name"/>
		<property name="user_password" column="user_password"/>
		<property name="user_state" column="user_state"/>
		<!-- 建立與角色的多對多的一個映射關係 -->
		<!--set標籤當中有
				*name 屬性:對方集合的屬性名稱
				table 屬性:多對多的關係需要使用中間表,中間表的名稱
		 -->
		<set name="roles" table="sys_user_role">
			<!-- 
				key 標籤的:
				column:當前的對象對應中間表的外鍵的名稱。
			 -->
			<key column="user_id"/>
			<!-- 
				many-to-many 標籤:
				*class :對象的類的全路徑
				*column:對方的對象對應的中間表中的外鍵名稱。			
			 -->
			<many-to-many class="com.itzheng.hibernate.domain.Role" column="role_id"/>
		</set>
	</class>
</hibernate-mapping>

Role.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
	<class name="com.itzheng.hibernate.domain.Role" table="sys_role">
		<!-- 建立IOD與主鍵的映射 -->
		 <id name="role_id" column="role_id">
		 <!-- 主鍵生成策略 -->
		 	<generator class="native"/>
		 </id>
		 <!-- 建立普通屬性與字段的映射 -->
		 <property name="role_name" column="role_name"/>
		 <property name="role_memo" column="role_memo"/>
		 <!-- 
				key 標籤的:
				column:當前的對象對應中間表的外鍵的名稱。
			 -->
		<set name="users" table="sys_user_role">
			<!-- 
				key 標籤的:
					column:當前的對象對應中間表的外鍵的名稱。
			 -->
			<key column="role_id"/>
			<!-- 
				many-to-many 標籤:
				*class :對象的類的全路徑
				*column:對方的對象對應的中間表中的外鍵名稱。			
			 -->
			<many-to-many class="com.itzheng.hibernate.domain.User" column="user_id"/>
		</set>
	</class>
</hibernate-mapping>

5、將映射添加到核心配置文件

在hibernate.cfg.xml
在這裏插入圖片描述

二、編寫測試類

直接執行下面代碼會報錯

package com.itzheng.hibernate.demo2;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.itzheng.hibernate.domain.Role;
import com.itzheng.hibernate.domain.User;
import com.itzheng.hibernate.utils.HibernateUtils;
/*
 * Hibernate的多對多的映射
 */
public class HibernateDemo2 {
	@Test
	/*
	 * 保存多條記錄:保存多個用戶和角色
	 */
	public void demo01() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		// 創建2個用戶
		User user1 = new User();
		user1.setUser_name("李白");
		User user2 = new User();
		user2.setUser_name("杜甫");
		// 創建3個角色
		Role role1 = new Role();
		role1.setRole_name("研發部");
		Role role2 = new Role();
		role2.setRole_name("市場部");
		Role role3 = new Role();
		role3.setRole_name("公關部");
		// 設置雙向的關聯關係,獲取到對應的集合將對象添加進去,用戶和角色相互對應
		user1.getRoles().add(role1);
		user1.getRoles().add(role2);
		user2.getRoles().add(role2);
		user2.getRoles().add(role3);
		role1.getUsers().add(user1);
		role2.getUsers().add(user1);
		role2.getUsers().add(user2);
		role3.getUsers().add(user2);
		// 保存操作:多對多建立了雙向的關係,必須有一方放棄了外鍵維護
		session.save(user1);
		session.save(user2);
		session.save(role1);
		session.save(role2);
		session.save(role3);
		transaction.commit();
	}
}

報主鍵重混異常
在這裏插入圖片描述
需要被動方放棄外鍵維護權,這裏是在角色的一方放棄外鍵維護權
在Role.hbm.xml當中的set集合當中
在這裏插入圖片描述
設置完後執行
成功執行
在這裏插入圖片描述

三、Hibernate多對多的一些操作

1、多對多的情況下在不設置級聯的情況下,只保存一邊會報錯

(1)值保存用戶(測試代碼)

@Test
	/*
	 * 多對多的一些操作 只是保存一邊是否可以,答案在不設置級聯的情況下會報,瞬時對象異常
	 */
	public void demo02() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		// 創建2個用戶
		User user1 = new User();
		user1.setUser_name("李白");
		// 創建3個角色
		Role role1 = new Role();
		role1.setRole_name("研發部");
		Role role2 = new Role();
		role2.setRole_name("市場部");
		Role role3 = new Role();
		role3.setRole_name("公關部");
		// 設置雙向的關聯關係,獲取到對應的集合將對象添加進去,用戶和角色相互對應
		user1.getRoles().add(role1);
		role1.getUsers().add(user1);
		// 只保存角色
		session.save(user1);
		transaction.commit();
	}

(2)只保存角色,因爲Role放棄了外鍵維護權,保存角色的時候,不影用戶,所以不會報錯(但是因爲在覈心配置文件當中是create從新建表,所以用戶表會被置空)

@Test
	/*
	 * 多對多的一些操作 只是保存一邊是否可以,答案在不設置級聯的情況下會報,瞬時對象異常
	 */
	public void demo02() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		// 創建2個用戶
		User user1 = new User();
		user1.setUser_name("李白");
		// 創建3個角色
		Role role1 = new Role();
		role1.setRole_name("研發部");
		Role role2 = new Role();
		role2.setRole_name("市場部");
		Role role3 = new Role();
		role3.setRole_name("公關部");
		// 設置雙向的關聯關係,獲取到對應的集合將對象添加進去,用戶和角色相互對應
		user1.getRoles().add(role1);
		role1.getUsers().add(user1);
		// 只保存用戶
		//session.save(user1);
		//只保存角色
		session.save(role1);
		transaction.commit();
	}

2、多對多的級聯保存或者更新

(1) 保存用戶,級聯保存角色。在用戶的映射文件當中配置。
在User.hbm.xml中的set當中配置cascade=“save-update”
在這裏插入圖片描述
這是隻保存User的時候

@Test
	/*
	 * 多對多的一個級聯保存
	 * 保存用戶,級聯保存角色。在用戶的映射文件當中配置。
	 */
	public void demo03() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		// 創建2個用戶
		User user1 = new User();
		user1.setUser_name("李白");
		// 創建3個角色
		Role role1 = new Role();
		role1.setRole_name("研發部");
		Role role2 = new Role();
		role2.setRole_name("市場部");
		Role role3 = new Role();
		role3.setRole_name("公關部");
		// 設置雙向的關聯關係,獲取到對應的集合將對象添加進去,用戶和角色相互對應
		user1.getRoles().add(role1);
		role1.getUsers().add(user1);
		// 只保存用戶
		session.save(user1);
		// 只保存角色
		//session.save(role1);
		transaction.commit();
	}

成功執行並插入了數據(一號用戶選擇了一號角色)
在這裏插入圖片描述
多對多的一個級聯保存
(2) 保存角色去,級聯保存用戶。在角色的映射文件當中配置。
在Role.hbm.xml當中
在這裏插入圖片描述

@Test
	/*
	 * 多對多的一個級聯保存
	 * 保存角色去,級聯保存用戶。在角色的映射文件當中配置。
	 */
	public void demo04() {

		Session session = HibernateUtils.getCurrentSession();
		Transaction transaction = session.beginTransaction();

		// 創建2個用戶

		User user1 = new User();
		user1.setUser_name("李白");

		// 創建3個角色

		Role role1 = new Role();
		role1.setRole_name("研發部");

		Role role2 = new Role();
		role2.setRole_name("市場部");

		Role role3 = new Role();
		role3.setRole_name("公關部");

		// 設置雙向的關聯關係,獲取到對應的集合將對象添加進去,用戶和角色相互對應
		user1.getRoles().add(role1);
		role1.getUsers().add(user1);
		// 只保存用戶
		//session.save(role1);
		// 只保存角色
		session.save(role1);
		transaction.commit();
	}

3、多對多的級聯刪除(基本不用)

中間表
在這裏插入圖片描述
(1)測試級聯刪除的功能,當中刪除用戶的時候級聯刪除角色
在User.hbm.xml當中配置
在這裏插入圖片描述
更改核心配置文件當中的hibernate.cfg.xml
在這裏插入圖片描述
執行下列代碼

	@Test
	/*
	 * 多對多的一個級聯刪除 刪除用戶級聯刪除角色, 
	 * 在User.hbm.xml中的set上配置cascade="delete"
	 */
	public void demo05() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		// 先查詢1號用戶
		User user = session.get(User.class, 1l);
		//刪除對應的用戶 
		session.delete(user);
		transaction.commit();
	}

級聯刪除
在這裏插入圖片描述
(2)刪除角色級聯刪除用戶
在Role.hbm.xml中的set上配置cascade=“delete”
在這裏插入圖片描述
執行下面代碼

@Test
	/*
	 * 多對多的一個級聯刪除 
	 * 刪除角色級聯刪除用戶
	 * 在Role.hbm.xml中的set上配置cascade="delete"
	 */
	public void demo06() {

		Session session = HibernateUtils.getCurrentSession();
		Transaction transaction = session.beginTransaction();

		// 先查詢2號角色
		Role role = session.get(Role.class, 2l);
		//刪除對應的角色,級聯刪除用戶 
		session.delete(role);

		transaction.commit();
	}

在這裏插入圖片描述

4、多對多的其他操作

(1)給用戶選擇角色

a、更改核心配置文件
在這裏插入圖片描述
b、運行下面代碼創建用戶表和角色表

@Test
	/*
	 * 保存多條記錄:保存多個用戶和角色
	 */
	public void demo01() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		// 創建2個用戶
		User user1 = new User();
		user1.setUser_name("李白");
		User user2 = new User();
		user2.setUser_name("杜甫");
		// 創建3個角色
		Role role1 = new Role();
		role1.setRole_name("研發部");
		Role role2 = new Role();
		role2.setRole_name("市場部");
		Role role3 = new Role();
		role3.setRole_name("公關部");
		// 設置雙向的關聯關係,獲取到對應的集合將對象添加進去,用戶和角色相互對應
		user1.getRoles().add(role1);
		user1.getRoles().add(role2);
		user2.getRoles().add(role2);
		user2.getRoles().add(role3);
		role1.getUsers().add(user1);
		role2.getUsers().add(user1);
		role2.getUsers().add(user2);
		role3.getUsers().add(user2);
		// 保存操作:多對多建立了雙向的關係,必須有一方放棄了外鍵維護
		session.save(user1);
		session.save(user2);
		session.save(role1);
		session.save(role2);
		session.save(role3);
		transaction.commit();
	}

c、給一號用戶選擇二號角色
角色放棄關聯外鍵的維護由
在這裏插入圖片描述
創建好表之後,更改核心配置文件當中
在這裏插入圖片描述
一號用戶選擇到二號角色
user.getRoles().add(role);//將二號角色添加到1號用戶的集合當中,就相黨與用戶獲取到角色

@Test
	/*
	 * 給用戶選擇角色
	 */
	public void demo07() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		// 給1號用戶多選二號角色
		//查詢1號用戶
		User user = session.get(User.class, 1l);
		// 查詢2號角色
		Role role = session.get(Role.class, 2l);
		//一號用戶選擇到二號角色
		user.getRoles().add(role);//將二號角色添加到1號用戶的集合當中,就相黨與用戶獲取到角色
		transaction.commit();
	}
(2)給用戶改選角色

給2號用戶將原有的二號角色改爲3號角色
查詢2號用戶

@Test
	/*
	 * 給用戶改選角色
	 */
	public void demo08() {

		Session session = HibernateUtils.getCurrentSession();
		Transaction transaction = session.beginTransaction();

		// 給2號用戶將原有的二號角色改爲3號角色
		// 查詢2號用戶
		User user = session.get(User.class, 2l);
		Role role2 = session.get(Role.class, 2l);
		Role role3 = session.get(Role.class, 3l);

		user.getRoles().remove(role2);// 將集合當中原有的二號角色刪除
		user.getRoles().add(role3);//然後將3號用戶添加到對應的集合當中

		transaction.commit();
	}
(3)給用戶刪除角色

刪除角色
給二號用戶刪除1號角色

@Test
	/*
	 * 刪除角色
	 * 給二號用戶刪除1號角色
	 */
	public void demo09() {

		Session session = HibernateUtils.getCurrentSession();
		Transaction transaction = session.beginTransaction();

		// 給2號用戶刪除1號角色
		// 查詢2號用戶
		User user = session.get(User.class, 2l);
		Role role1 = session.get(Role.class, 1l);

		user.getRoles().remove(role1);// 將集合當中原有的1號角色刪除

		transaction.commit();
	}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章