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);
}
级联删除虽然可以用,但是却有禁用级联删除的结论,因为有时用的级联删除会把所有数据删除: