1. 程式人生 > >Hibernate:多對多刪除

Hibernate:多對多刪除

案例:

書籍類(Book.java)和書籍類別(Category.java)

表結構:

t_hibernate_book表:

t_hibernate_category表:

t_hibernate_book_category表(中間表):

先建立多對多聯絡:

book類:

package com.zking.five.entity;

import java.util.HashSet;
import java.util.Set;

public class Book {
	private Integer bookId;
	private String bookName;
	private Float price;
	
	//建立關聯
	private Set<Category> categorys=new HashSet<Category>();
	
	//強制立即載入
	private Integer initCagetorys=0;
	
	public Integer getInitCagetorys() {
		return initCagetorys;
	}
	public void setInitCagetorys(Integer initCagetorys) {
		this.initCagetorys = initCagetorys;
	}
	public Set<Category> getCategorys() {
		return categorys;
	}
	public void setCategorys(Set<Category> categorys) {
		this.categorys = categorys;
	}
	public Integer getBookId() {
		return bookId;
	}
	public void setBookId(Integer bookId) {
		this.bookId = bookId;
	}
	public String getBookName() {
		return bookName;
	}
	public void setBookName(String bookName) {
		this.bookName = bookName;
	}
	public Float getPrice() {
		return price;
	}
	public void setPrice(Float price) {
		this.price = price;
	}
	
	

}

category類:

package com.zking.five.entity;

import java.util.HashSet;
import java.util.Set;

public class Category {
	
	private Integer categoryId;
	private String categoryName;
	
	//建立關聯
	private Set<Book> books=new HashSet<Book>();
	
	//強制立即載入
	private Integer initBooks=0;
	
	public Integer getInitBooks() {
		return initBooks;
	}
	public void setInitBooks(Integer initBooks) {
		this.initBooks = initBooks;
	}
	public Set<Book> getBooks() {
		return books;
	}
	public void setBooks(Set<Book> books) {
		this.books = books;
	}
	public Integer getCategoryId() {
		return categoryId;
	}
	public void setCategoryId(Integer categoryId) {
		this.categoryId = categoryId;
	}
	public String getCategoryName() {
		return categoryName;
	}
	public void setCategoryName(String categoryName) {
		this.categoryName = categoryName;
	}
	
	

}

配置檔案:

<?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 table="t_hibernate_book" name="com.zking.five.entity.Book">
    		<id name="bookId" type="java.lang.Integer" column="book_id ">
    			<generator class="increment"></generator>
    		</id>
    		<property name="bookName" type="java.lang.String" column="book_name "></property>
    		<property name="price" type="java.lang.Float" column="price "></property>
    		
    		<!-- table:中間連線表
    		name="categorys":Book實體類裡的類屬性
    		 -->
    	    <set table="t_hibernate_book_category" name="categorys" cascade="save-update" inverse="false">
    	    	<!-- one -->
    			<key column="bid"></key>
    			<!-- many -->
    			<many-to-many column="cid" class="com.zking.five.entity.Category"></many-to-many>
    		</set> 
    		
    		
    	</class>
    </hibernate-mapping>
<?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 table="t_hibernate_category" name="com.zking.five.entity.Category">
    		<id name="categoryId" type="java.lang.Integer" column="category_id ">
    			<generator class="increment"></generator>
    		</id>
    		<property name="categoryName" type="java.lang.String" column="category_name "></property>
    		
    		<set table="t_hibernate_book_category" name="books" cascade="save-update" inverse="true">
    			<key column="cid"></key>
    			<many-to-many column="bid" class="com.zking.five.entity.Book"></many-to-many>
    		</set> 
    	
    	</class>
    </hibernate-mapping>

下面是測試結果:

1.如果cascade不管是主控方設定還是被控方設定,被設定為all,delete等與delete級聯刪除有關的屬性,兩端的以及中間表的記錄都會被刪除,這樣的需求一般比較少,但是如要這種需求,把cascade設定為all,delete即可刪除

2.只想刪除某一端和中間表的記錄。這種需求很常見,這時只要把cascade的設定是與delete有關的任何級聯約束。

       2.1:如果執行刪除的是主控方,只需要簡單的刪除這條記錄,級聯關係以及主控方的記錄同時刪除,但被控方的記錄仍然存在。因此只對主控方的多對多刪除是最簡單,直接的。程式碼如下:

/**
	 * 主控方刪除
	 * @param book
	 */
	public void delete(Book book) {
		Session session = SessionFactoryUtils.getSession();
		Transaction transaction = session.beginTransaction();
		session.delete(book);
		transaction.commit();
		session.close();
	}
	

        2.2:如果你這個時候想直接刪除被控方,那麼很遺憾的告訴你,你只做到了一半,你只是簡單的把被控方的記錄刪除了,關聯關係仍然存在中間表裡,並且中間表裡的資料並沒有刪除掉,系統隨時會因為你的關聯訪問報外來鍵資訊的錯,程式碼如下:

public void delete(Category category) {
		Session session = SessionFactoryUtils.getSession();
		Transaction transaction = session.beginTransaction();
		
		session.delete(category);
		transaction.commit();
		session.close();
	}

       2.3:如果想要刪除被控方和中間表的資料,就要先解除主控方和被控方的關聯關係,並且解除關係只能是主控方解除,被控方沒有這個能力,程式碼如下:

/**
	 * 被控方刪除:
	 * 1.先查詢出從表關係
	 * 2.被控方通過主控方來解除關聯關係
	 * 3.再去刪除被控方
	 * @param category
	 */
	public void delete(Category category) {
		Session session = SessionFactoryUtils.getSession();
		Transaction transaction = session.beginTransaction();
		Category c = session.get(Category.class, category.getCategoryId());
		for (Book b : c.getBooks()) {
			b.getCategorys().remove(c);     //主控方控制移除關聯
		}
		session.delete(c);
		transaction.commit();
		session.close();
	}

Junit測試程式碼:

        /**
	 * 主控方刪除
	 * 需求:刪除有關聯關係的一本書
	 * 刪除斗羅大陸這本書    目前這本書在中間表引用
	 */
	@Test
	public void testDelete() {
		book.setBookId(7);
		this.bookDao.delete(book);
	}
	
	/**
	 * 被控方刪除
	 * 刪除有關聯關係的一個類別,包括該類別下的所有書籍
	 * 刪除玄幻這個類別   目前這個類別在中間表引用
	 */
	@Test
	public void testDelete1() {
		category.setCategoryId(4);
		this.categoryDao.delete(category);
	}

級聯刪除雖然可以用,但是卻有禁用級聯刪除的結論,因為有時用的級聯刪除會把所有資料刪除: