多对多
why
1. 数据库的多对多
1.1 数据库中不能直接映射多对多
处理:创建一个桥接表(中间表),将一个多对多关系转换成两个一对多
注1:数据库多表联接查询
永远就是二个表的联接查询
A B C D
t1 C D
t2 D
t3
注2:交叉连接
注3:外连接:left(左)/right(右)/full(左右)
主从表:连接条件不成立时,主表记录永远保留,与null匹配
2. hibernate的多对多
2.1 hibernate可以直接映射多对多关联关系(看作两个一对多)
3. 多对多关系注意事项
3.1 一定要定义一个主控方
3.2 多对多删除
3.2.1 主控方直接删除
3.2.2 被控方先通过主控方解除多对多关系,再删除被控方com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails
3.2.3 禁用级联删除
3.3 关联关系编辑,不需要直接操作桥接表,hibernate的主控方会自动维护
org.hibernate.TransientObjectException: The given object has a null identifier: com.zking.hibernate06.entity.Book
org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session :
1)示例1:新增一本书及书本类型
2)示例2:新增一本书及已有的书本类型
3)示例3:主控方inverse,true表示对方是主控方,false表示什么都不管
a)Book是主控方(Category.hbm.xml中inverse=true)
b)Category是主控方(Book.hbm.xml中inverse=true)
c)都不是主控方(Category.hbm.xml和Book.hbm.xml中inverse=false)
4)示例4:删除
a)删除主控方(直接删除) session.delete()
b)删除被控方(先解除关联关系,再删除)
Category
Book
Category c1=session.get(Category.class,c.getCategoryId);
if(null!=c1){
//解除关联关系,先删从表,再删主表
Set<Book> books= c1.getBooks();
foreach(Book book : books){
//错误
//c1.getBooks().remove(book);
//正确
book.getCategories().remove(c1);
}
session.delete(c1);
}
c)级联删除(cascade="delete" 禁用)
Book book=session.get(Book.class,b.getBookId());
if(null!=book)
session.delete(book);
多对多案例
数据库中的表级表数据
-- 书本类别表
create table t_category_hb
(
category_id int primary key auto_increment,
category_name varchar(50) not null
);
-- 书本表
create table t_book_hb
(
book_id int primary key auto_increment,
book_name varchar(50) not null,
price float not null
);
-- 桥接表
-- 定义三个列,其实只要两个列
-- 一个类别对应多本书,一本书对应多个类别
CREATE TABLE t_book_category_hb
(
bcid INT PRIMARY KEY AUTO_INCREMENT,
bid INT NOT NULL,
cid INT NOT NULL,
FOREIGN KEY(bid) REFERENCES t_book_hb(book_id),
FOREIGN KEY(cid) REFERENCES t_category_hb(category_id)
);
insert into t_book_hb(book_id, book_name, price) values(1,'西游记',50);
insert into t_book_hb(book_id, book_name, price) values(2,'红楼梦',50);
insert into t_book_hb(book_id, book_name, price) values(3,'水浒',50);
insert into t_book_hb(book_id, book_name, price) values(4,'三国演义',50);
insert into t_category_hb(category_id, category_name) values(1,'古典');
insert into t_category_hb(category_id, category_name) values(2,'神话');
insert into t_category_hb(category_id, category_name) values(3,'历史');
insert into t_book_category_hb(bid, cid) values(1,1);
insert into t_book_category_hb(bid, cid) values(1,2);
insert into t_book_category_hb(bid, cid) values(2,1);
insert into t_book_category_hb(bid, cid) values(3,1);
insert into t_book_category_hb(bid, cid) values(3,3);
insert into t_book_category_hb(bid, cid) values(4,1);
insert into t_book_category_hb(bid, cid) values(4,3);
实体类及实体类的配置
实体类
Book
package com.tang4.entity;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
public class Book implements Serializable{
private Integer bookId;
private String bookName;
private Float price;
Set<Category> categorys = new HashSet<Category>();
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;
}
@Override
public String toString() {
return "Book [bookId=" + bookId + ", bookName=" + bookName + ", price=" + price + ", categorys=" + categorys
+ "]";
}
}
Category
package com.tang4.entity;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
public class Category implements Serializable{
private Integer categoryId;
private String categoryName;
Set<Book> books = new HashSet<Book>();
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;
}
@Override
public String toString() {
return "Category [categoryId=" + categoryId + ", categoryName=" + categoryName + ", books=" + books + "]";
}
}
实体类的配置
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.tang4.entity.Book" table="t_book_hb">
<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 name="price" type="java.lang.Float" column="price" />
<!-- 多对多映射关系 -->
<!--
name:一方包含多方的属性对象名称,指向多方
inverse:是否是主控方,false表示对方不是主控方
true表示对方是主控方,由对方来更新(维护)中间表
cascade:级联操作 save-update/none/delete/all=delete+save-update
table:表示中间表的名称
-->
<set name="categorys"
inverse="false"
cascade="save-update"
table="t_book_category_hb"
>
<!-- column:指向己方在中间表的外键字段 -->
<key column="bid"></key>
<!--
class:对方实例完整路径
column:对方在中间表的外键字段
-->
<many-to-many class="com.tang4.entity.Category"
column="cid">
</many-to-many>
</set>
</class>
</hibernate-mapping>
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.tang4.entity.Category" table="t_category_hb">
<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" />
<!-- 多对多映射关系 -->
<set name="books"
inverse="true"
cascade="save-update"
table="t_book_category_hb"
>
<key column="cid"></key>
<many-to-many class="com.tang4.entity.Book"
column="bid">
</many-to-many>
</set>
</class>
</hibernate-mapping>
把实体类的配置配置到核心文件配置中
dao
BookDao
package com.tang4.dao;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.tang.util.SessionFactoryUtils;
import com.tang4.entity.Book;
public class BookDao {
public void addBook(Book book) {
Session session = SessionFactoryUtils.openSession();
Transaction ts = session.beginTransaction();
session.save(book);
ts.commit();
SessionFactoryUtils.closeSession();
}
}
BookDaoTest
package com.tang4.dao;
import com.tang4.entity.Book;
import com.tang4.entity.Category;
import junit.framework.TestCase;
public class BookDaoTest extends TestCase {
BookDao bookDao = new BookDao();
Book book = null;
protected void setUp() throws Exception {
book = new Book();
}
public void testAddBook() {
book.setBookName("皇帝内径");
book.setPrice(100f);
Category category = new Category();
category.setCategoryName("药店");
Category category1 = new Category();
category1.setCategoryName("经书");
book.getCategorys().add(category);
book.getCategorys().add(category1);
category.getBooks().add(book);
category1.getBooks().add(book);
bookDao.addBook(book);
}
}
CategoryDao
package com.tang4.dao;
import org.hibernate.Session;
import org.hibernate.Transaction;
import com.tang.util.SessionFactoryUtils;
import com.tang4.entity.Category;
public class CategoryDao {
public Category getCategoryById(Category category) {
Session session = SessionFactoryUtils.openSession();
Transaction ts = session.beginTransaction();
Category c = session.get(Category.class,category.getCategoryId());
ts.commit();
SessionFactoryUtils.closeSession();
return c;
}
}
CategoryDaoTest
package com.tang4.dao;
import com.tang4.entity.Book;
import com.tang4.entity.Category;
import junit.framework.TestCase;
public class CategoryDaoTest extends TestCase {
CategoryDao categoryDao = null;
BookDao bookDao = new BookDao();
protected void setUp() throws Exception {
categoryDao = new CategoryDao();
}
public void testGetCategoryById() {
Book book = new Book();
book.setBookName("山海经");
book.setPrice(200f);
Category category = new Category();
category.setCategoryId(4);
Category c = categoryDao.getCategoryById(category);
book.getCategorys().add(c);
bookDao.addBook(book);
}
}
测试结果
BooKDao的测试结果
CategoryDao的测试结果