hibernate多对多关系维护主要是级联新增与级联删除,这里以书籍(Book)和书籍类别(Category)为例来讲解Hibernate关联映射中的多对多关联关系的维护,相关数据库设计、实体类、配置文件见:https://blog.csdn.net/weixin_42687829/article/details/83385610
两个配置文件的inverse属性共有四种组合方式:1、Book.hbm.xml:true,Category.hbm.xml:false代表的意思是将关系维护的责任交给Category;2、Book.hbm.xml:false,Category.hbm.xml:true代表的意思是将关系维护的责任交给Book;3、两个都为true;4、两个都为false。这里先以第二种配置方式为例:
1、级联新增
测试新增书本:
/**
* 新增书本的方法
* @author LJ
* @Date 2018年10月27日
* @Time 上午10:05:40
* @param book
* @return
*/
public Integer save(Book book) {
Session session = SessionFactoryUtil.getSession();
Transaction transaction = session.beginTransaction();
Integer bid = (Integer) session.save(book);
transaction.commit();
SessionFactoryUtil.closeSession();
return bid;
}
/**
* 新增书本:填写书本信息->勾选复选框类别->提交
* jdbc的做法:bookDao.add()、categoryDao.add()
* hibernate做法:bookDao.add()
*
* 配置:
* book.hbm:false
* category.hbm:true
* 代表的意思是将关系维护的责任交给book
* @author LJ
* @Date 2018年10月27日
* @Time 上午10:32:28
*/
@Test
public void testSaveBook() {
Book book=new Book();
book.setBookName("你是最好的自己");
Category category=new Category();
category.setCategoryId(3);
/*
* 错误写法:book.getCategorys().add(category);
* 异常Caused by:
* com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:
* Column 'category_name' cannot be null
* 因为hibernate通过管理持久态对象来操作数据库,所以要先查询一下将category变成持久态
*/
book.getCategorys().add(this.categoryDao.get(category));
this.save(book);
}
结果:书籍表和桥接表都新增成功,没有异常
测试新增书籍类别:
/**
* 新增类别的方法
* @author LJ
* @Date 2018年10月27日
* @Time 上午10:14:04
* @param category
* @return
*/
public Integer save(Category category) {
Session session = SessionFactoryUtil.getSession();
Transaction transaction = session.beginTransaction();
Integer cid = (Integer) session.save(category);
transaction.commit();
SessionFactoryUtil.closeSession();
return cid;
}
/**
* 当配置为:
* book.hbm:false
* category.hbm:true时,操作category是不能往桥接表里添加数据的
* 因为其代表的意思是将关系维护的责任交给book,所以这种添加方式是不可行的
* @author LJ
* @Date 2018年10月27日
* @Time 上午10:42:37
*/
@Test
public void testSaveCategory() {
Category category=new Category();
category.setCategoryName("玄幻");
Book book=new Book();
book.setBookId(5);
category.getBooks().add(this.bookDao.get(book));
this.categoryDao.save(category);
}
结果:书籍列表新增成功,但桥接表没有新增,没有报异常
当配置都为true时测试新增书籍:
/**
* 当配置为:
* book.hbm:true
* category.hbm:true时会出现中间表无对象维护的情况,即书本能添加进去,但中间表无法添加
* @author LJ
* @Date 2018年10月27日
* @Time 上午10:46:22
*/
@Test
public void testSaveBook2() {
Book book=new Book();
book.setBookName("你是最好的自己2");
Category category=new Category();
category.setCategoryId(3);
book.getCategorys().add(this.categoryDao.get(category));
this.save(book);
}
结果:书本能添加进去,但中间表无法添加
当配置都为false时测试新增书籍:
/**
* 当配置为:
* book.hbm:false
* category.hbm:false
* @author LJ
* @Date 2018年10月27日
* @Time 上午10:46:22
*/
@Test
public void testSaveBook3() {
Book book=new Book();
book.setBookName("你是最好的自己2");
Category category=new Category();
category.setCategoryId(3);
book.getCategorys().add(this.categoryDao.get(category));
this.save(book);
}
结果:书本能添加进去,中间表也能添加进去,但若再新增书本类别且是同一组关系,依然能新增进去,造成数据重复的现象
2、级联删除(以第二种配置方式为例)
删除主控方(Book):
/**
* 删除书本的方法
* @author LJ
* @Date 2018年10月27日
* @Time 上午10:50:19
* @param book
*/
public void del(Book book) {
Session session = SessionFactoryUtil.getSession();
Transaction transaction = session.beginTransaction();
session.delete(book);
transaction.commit();
SessionFactoryUtil.closeSession();
}
/**
* 删除主控方book
* 结论:一并将从表关联的中间表信息删除
* @author LJ
* @Date 2018年10月27日
* @Time 上午10:52:13
*/
@Test
public void testDelBook() {
Book book=new Book();
book.setBookId(5);//删除书本ID为5的
this.del(book);
}
结果:一并将从表关联的中间表信息删除
删除被控方(Category):
/**
* 删除类别(被控方)
* @author LJ
* @Date 2018年10月27日
* @Time 上午10:51:05
* @param category
*/
public void del(Category category) {
Session session = SessionFactoryUtil.getSession();
Transaction transaction = session.beginTransaction();
session.delete(category);
transaction.commit();
SessionFactoryUtil.closeSession();
}
/**
* 删除被控方category
* 结论:删除失败
* @author LJ
* @Date 2018年10月27日
* @Time 上午10:54:52
*/
@Test
public void testDelCategory() {
Category category=new Category();
category.setCategoryId(2);
this.del(category);
}
以上做法会删除失败且报异常:Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t_hibernate_book_category`, CONSTRAINT `t_hibernate_book_category_ibfk_2` FOREIGN KEY (`cid`) REFERENCES `t_hibernate_category` (`category_id`)),这是因为主外键约束出现的异常。
解决:被控方先通过主控方解除多对多关系,再删除被控方。
/**
* 删除类别(被控方)
* 若直接删除会失败,因为被控方被中间表所引用
* 解决:1、解除关联关系(先删除中间表的引用数据)
* 2、再去删除主表信息
* @author LJ
* @Date 2018年10月27日
* @Time 上午10:51:05
* @param category
*/
public void del(Category category) {
Session session = SessionFactoryUtil.getSession();
Transaction transaction = session.beginTransaction();
Category c = session.get(Category.class, category.getCategoryId());
//c里面保存着与某一些书籍相关联的关系
for (Book b : c.getBooks()) {
/*
* 注意关联关系是交于book,所以解除关系应b.getCategorys().remove(c),
* 而不是c.getBooks().remove(b)
*/
b.getCategorys().remove(c);
}
session.delete(c);
transaction.commit();
SessionFactoryUtil.closeSession();
}
/**
* 删除被控方category
* @author LJ
* @Date 2018年10月27日
* @Time 上午10:54:52
*/
@Test
public void testDelCategory() {
Category category=new Category();
category.setCategoryId(2);
this.del(category);
}
注意:关联关系交于哪一方就由哪一方来解除关系