1. 程式人生 > >hibernate多對多級聯查詢、新增、刪除

hibernate多對多級聯查詢、新增、刪除

以書籍與書籍類別為例:

一本書可以有多種型別,一種型別也可以對應多本書,書籍和書籍類別的關係是多對多的關係,他們的關係是在中間表裡面的。

 

多對多通過一個表找到另一個表的資料的條件為:

中間表

中間表對應本表的外來鍵

中間表對應關聯表的外來鍵

注:多對多的時候,兩個表的關係是通過中間表建立的。

 

程式碼:

-- 書本類別表
create table t_hibernate_category
(
   category_id int primary key auto_increment,
   category_name varchar(50) not null
);
 
-- 書本表
create table t_hibernate_book
(
   book_id int primary key auto_increment,
   book_name varchar(50) not null,
   price float not null
);
 
 
-- 橋接表
-- 定義三個列,其實只要兩個列
-- 一個類別對應多本書,一本書對應多個類別
create table t_hibernate_book_category
(
  bcid int primary key auto_increment,  
  bid int not null,
  cid int not null,
  foreign key(bid) references t_hibernate_book(book_id),
  foreign key(cid) references t_hibernate_category(category_id)
);
 
-- 插入書籍表資料
insert into t_hibernate_book(book_id, book_name, price) values(1,'西遊記',50);
insert into t_hibernate_book(book_id, book_name, price) values(2,'紅樓夢',50);
insert into t_hibernate_book(book_id, book_name, price) values(3,'水滸',50);
insert into t_hibernate_book(book_id, book_name, price) values(4,'三國演義',50);
 
-- 插入型別表資料
insert into t_hibernate_category(category_id, category_name) values(1,'古典');
insert into t_hibernate_category(category_id, category_name) values(2,'神話');
insert into t_hibernate_category(category_id, category_name) values(3,'歷史');
 
-- 插入中間表資料
insert into t_hibernate_book_category(bid, cid) values(1,1);
insert into t_hibernate_book_category(bid, cid) values(1,2);
insert into t_hibernate_book_category(bid, cid) values(2,1);
insert into t_hibernate_book_category(bid, cid) values(3,1);
insert into t_hibernate_book_category(bid, cid) values(3,3);
insert into t_hibernate_book_category(bid, cid) values(4,1);
insert into t_hibernate_book_category(bid, cid) values(4,3);

實體類:

Book.java

package com.hibernate.entity;
 
import java.util.HashSet;
import java.util.Set;
 
public class Book {
 
	private Integer book_id;
	private String book_name;
	private Float price;
        /*
         *  這本書所對應的型別的集合
         */
	private Set<Category> categorys=new HashSet<>();
	public Set<Category> getCategorys() {
		return categorys;
	}
	public void setCategorys(Set<Category> categorys) {
		this.categorys = categorys;
	}
	public Integer getBook_id() {
		return book_id;
	}
	public void setBook_id(Integer book_id) {
		this.book_id = book_id;
	}
	public String getBook_name() {
		return book_name;
	}
	@Override
	public String toString() {
		return "Book [book_id=" + book_id + ", book_name=" + book_name + ", price=" + price + "]";
	}
	public void setBook_name(String book_name) {
		this.book_name = book_name;
	}
	public Float getPrice() {
		return price;
	}
	public void setPrice(Float price) {
		this.price = price;
	}
}

新建一個xml檔案,配置書籍實體的對映 ,Book.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.hibernate.entity.Book" table="t_hibernate_book">
		<id name="book_id" type="java.lang.Integer" column="book_id">
			<generator class="increment"></generator>
		</id>
		<property name="book_name" type="java.lang.String" column="book_name"></property>
		<property name="price" type="java.lang.Float" column="price"></property>
		<!-- 以上為實體中的基本屬性 -->
 
                <!-- 
                        set標籤對應實體中的set集合,若實體中使用的是List集合,這裡則使用list標籤
			name: 指的是當前對映實體
			table: 對應的是中間表的表名 
		 -->
		<set name="categorys" table="t_hibernate_book_category">
		<!-- 中間表字段(與當前對映實體對應的表的主鍵相關聯的那個欄位) -->
			<key column="bid"></key>
			<!-- 
				class: ‘多’方的全限定名,這個多方也可以叫對方,也就是類別
				column: 中間表字段(與‘多’方主鍵相關聯的欄位)
			-->
			<many-to-many class="com.hibernate.entity.Category" column="cid"></many-to-many>
		</set>
	</class>
</hibernate-mapping>

Category.java

package com.hibernate.entity;
 
import java.util.HashSet;
import java.util.Set;
 
public class Category {
 
	private Integer category_id;
	private String category_name;
        /*
         *  該型別所對應的書籍的集合
         */
	private Set<Book> books=new HashSet<>();
	public Set<Book> getBooks() {
		return books;
	}
	public void setBooks(Set<Book> books) {
		this.books = books;
	}
	public Integer getCategory_id() {
		return category_id;
	}
	public void setCategory_id(Integer category_id) {
		this.category_id = category_id;
	}
	public String getCategory_name() {
		return category_name;
	}
	public void setCategory_name(String category_name) {
		this.category_name = category_name;
	}
	@Override
	public String toString() {
		return "Category [category_id=" + category_id + ", category_name=" + category_name + "]";
	}
}

新建一個xml檔案,配置類別實體的對映,Category.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.hibernate.entity.Category" table="t_hibernate_category">
		<id name="category_id" type="java.lang.Integer" column="category_id">
			<generator class="increment"></generator>
		</id>
		<property name="category_name" type="java.lang.String" column="category_name"></property>
		<!-- 
                        set標籤對應實體中的set集合,若實體中使用的是List集合,這裡則使用list標籤
			name: 指的是當前對映實體
			table: 對應的是中間表的表名 ,
			
			關聯關係交與對方管理(中間表的資料是否交給對方維護)
			cascade
			inverse
		 -->
		<set name="books" table="t_hibernate_book_category" cascade="save-update" inverse="true">
			<!-- 中間表字段(與當前對映實體對應的表的主鍵相關聯的那個欄位) -->
			<key column="cid"></key>
			<!-- 
				class: ‘多’方的全限定名,這個多方也可以叫對方,也就是類別
				column: 中間表字段(與‘多’方主鍵相關聯的欄位)
			-->
			<many-to-many class="com.hibernate.entity.Book" column="bid"></many-to-many>
		</set>
	</class>
</hibernate-mapping>

將書籍與類別的實體對映檔案配置到hibernate的核心配置檔案中:
(核心配置檔案也是新建的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="connection.username">root</property>
		<property name="connection.password">123</property>	
		<property name="connection.url">jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=GMT</property>
		<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
		<!-- 配置sql語句生成的規則,配置資料庫方言 -->
		<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
		<!-- 配置本地事務 -->
		<property name="hibernate.current_session_context_class">thread</property>
		<!-- 配置開發除錯所用的配置show_sql,format_sql -->
		<property name="show_sql">true</property>
		<property name="format_sql">true</property>
		<!-- 配置對映檔案 -->
		<mapping resource="com/hibernate/entity/Book.hbm.xml"/>
		<mapping resource="com/hibernate/entity/Category.hbm.xml"/>
	</session-factory>
</hibernate-configuration>

測試程式碼:

注:HibernateUtils,java 工具類在我之前寫的部落格中有程式碼,這裡就不貼了:

hibernate中一對多雙向關聯的記錄

1.查詢

//需求:通過ID查詢書籍的資訊,同時查詢書籍對應的類別的資訊。
@Test
public void testGet() {
            //1.獲得session會話
		Session session = HibernateUtils.getSession();
            //2.開啟事務
		Transaction beginTransaction = session.beginTransaction();
            //3.資料庫操作
		Book b=new Book();
		b.setBook_id(1);
		Book book = session.get(Book.class,b.getBook_id());
                System.out.println(book);
		book.getCategorys().stream().forEach(System.out::println);
            //4.關閉事務
		beginTransaction.commit();
            //5.關閉會話
		session.close();
}

輸出結果:

 2.新增
由於我們之前在類別的對映檔案Category.hbm.xml中設定了  cascade="save-update" inverse="true" ,設定這幾個屬性則表示新增與修改時,中間表的資料是否由對方維護,如果inverse設定為true,則表示由對方維護(即由書籍維護中間表資料)。所以我們這裡的中間表資料就由書籍來維護。

//需求:通過新增書籍,同時在中間表中新增該書籍與其對應的類別。
@Test
public void testSave() {
	Session session = HibernateUtils.getSession();
	Transaction beginTransaction = session.beginTransaction();
 
      //例項化一個Book物件
        Book b=new Book();
      //初始化book物件
	b.setBook_name("鬥破蒼穹");
	b.setPrice(10f);
      //為Book物件設定它的書籍類別
	Category c=new Category();
	c.setCategory_id(1);		
        Category c2=new Category();
	c2.setCategory_id(2);
	b.getCategorys().add(c);
	b.getCategorys().add(c2);
      //執行新增操作
	Integer save = (Integer) session.save(b);
 
	beginTransaction.commit();
	session.close();
}

由於我們設定了中間表資料由書籍維護,在資料庫中重新整理一下資料會發現,書籍表中鬥破蒼穹新增成功了,中間表也將對應的書籍id以及型別id添加了進去,所以增加是成功的;不過這裡還要舉一個反面的例子:新增類別,且為類別中的Set集合加入書籍

需求:通過新增類別,同時在中間表中新增該類別與其對應的書籍。


@Test
	public void testSaveC() {
                Session session = HibernateUtils.getSession();
    		Transaction beginTransaction = session.beginTransaction()
              //例項化一個型別物件
		Category c=new Category();
              //初始化型別物件
		c.setCategory_name("穿越");
             //為Category物件設定它的書籍
		Book book=new Book();
		book.setBook_id(6);
		book.setBook_name("a");
		book.setPrice(1f);
		c.getBooks().add(book);
 
                Integer save = (Integer) session.save(c);
 
		beginTransaction.commit();
		session.close();
 
	}

 

注:由於中間表的資料是由書籍來維護的,所以新增型別時, 如果指定了書籍 ,中間表不僅不會新增資料,反而還會刪除掉中間表中的資料。  故增加類別時,只做單純的增加類別就行了,別指定書籍。

 

3.刪除

//需求:根據書籍id刪除書籍,同時刪除中間表中該書籍所對應的資料
@Test
public void testDelete() {
	Session session = HibernateUtils.getSession();
	Transaction beginTransaction = session.beginTransaction();
      //執行刪除操作
        Book book=new Book();
	book.setBook_id(5);
        session.delete(book);	
 
	beginTransaction.commit();
	session.close();
}

 刪除與新增有著異曲同工之處,由於之前在類別的實體對映檔案中配置了,中間表的資料由書籍去維護。

So,對Book進行刪除時,中間表中該書籍對應的資料也會被刪除掉,需求能夠達成!

如果對類別進行刪除,中間表有該類別的話是刪除不了的(由於中間表關聯了主外來鍵),所以刪除被控方(類別)會相對麻煩一點:


@Test
	public void testDeleteC() {
                Session session = HibernateUtils.getSession();
		Transaction beginTransaction = session.beginTransaction();
	    //要刪除的類別id
                Category c=new Category();
		c.setCategory_id(4);
 
                /**
	         * 被控方的刪除:
	         * 	1、先要獲取到被控方的資料
	         * 	2、利用被控方獲取到主控方來解除關聯關係
	         * 	3、最後將被控方刪除
	         */
	    	Category category = session.get(Category.class, c.getCategory_id());
		for (Book b : category.getBooks()) {
			b.getCategorys().remove(category);
		}
		session.delete(category);
 
		beginTransaction.commit();
		session.close();
	}

注:刪除被控方資料時,如果中間表中關聯了要刪除的那條資料,則中間表中關聯的資料也會被一同刪除。

溫馨提示:級聯刪除慎用