hibernate关系映射之多对多关系

由于数据库中不能直接映射多对多关系,所以处理方式为创建一个桥接表(中间表),将一个多对多关系转换成两个一对多,这里以书籍和书籍类别为例来讲解Hibernate关联映射中的多对多关联关系。数据库设计如图:

书籍表(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 initCategorys=0;//用于判断是否需要懒加载,0懒加载,1立即加载

public Integer getInitCategorys() {
	return initCategorys;
}

public void setInitCategorys(Integer initCategorys) {
	this.initCategorys = initCategorys;
}

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;
}

public Set<Category> getCategorys() {
	return categorys;
}

public void setCategorys(Set<Category> categorys) {
	this.categorys = categorys;
}

}
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;//用于判断是否需要懒加载,0懒加载,1立即加载

public Integer getInitBooks() {
	return initBooks;
}

public void setInitBooks(Integer initBooks) {
	this.initBooks = initBooks;
}

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;
}

public Set<Book> getBooks() {
	return books;
}

public void setBooks(Set<Book> books) {
	this.books = books;
}

}
Book.hbm.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
	<set name="categorys" cascade="save-update" inverse="false" table="t_hibernate_book_category">
		<key column="bid"></key>
		<many-to-many column="cid" class="com.zking.five.entity.Category"></many-to-many>
	</set>
</class>
Category.hbm.xml配置文件: <?xml version="1.0" encoding="UTF-8"?>
	<set name="books" cascade="save-update" inverse="true" table="t_hibernate_book_category">
		<key column="cid"></key>
		<many-to-many column="bid" class="com.zking.five.entity.Book"></many-to-many>
	</set>
</class>
在hibernate中,你只管查询当前表对象即可,因为 hibernate会自动关联桥接表以及关联表查询出关联对象,hibernate的多对多可以直接映射多对多关联关系(看作两个一对多)

测试根据书本ID查询单个:

    /**
 * 根据书本ID查询单个
 * @author LJ
 * @Date 2018年10月25日
 * @Time 下午8:33:31
 * @param book
 * @return
 */
public Book get(Book book) {
	Session session = SessionFactoryUtil.getSession();//获取session
	Transaction transaction = session.beginTransaction();//开启事务
	Book b = session.get(Book.class, book.getBookId());//查询
	if(b!=null&&new Integer(1).equals(book.getInitCategorys())) {//如果book.getInitCategorys()为1则立即加载
		Hibernate.initialize(b.getCategorys());//设为立即加载
	}
	transaction.commit();//提交事务
	SessionFactoryUtil.closeSession();//关闭session
	return b;
}

@Test
public void testGet1() {
	Book book=new Book();
	book.setBookId(4);//查询书本ID为4的书本
	book.setInitCategorys(1);//立即加载
	Book b = this.get(book);
	System.out.println("书名:"+b.getBookName());
	for (Category cg : b.getCategorys()) {
		System.out.println("所属类别:"+cg.getCategoryName());
	}
}

运行效果:

测试类别ID查询单个:

package com.zking.five.dao;

import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.zking.five.entity.Book;
import com.zking.five.entity.Category;
import com.zking.two.util.SessionFactoryUtil;

public class CategoryDao {

/**
 * 根据类别ID查询单个
 * @author LJ
 * @Date 2018年10月25日
 * @Time 下午8:41:52
 * @param category
 * @return
 */
public Category get(Category category) {
	Session session = SessionFactoryUtil.getSession();
	Transaction transaction = session.beginTransaction();
	Category cg = session.get(Category.class, category.getCategoryId());
	if(cg!=null&&new Integer(1).equals(category.getInitBooks())) {
		Hibernate.initialize(cg.getBooks());
	}
	transaction.commit();
	SessionFactoryUtil.closeSession();
	return cg;
}

@Test
public void testGet2() {
	Category category=new Category();
	category.setCategoryId(3);
	category.setInitBooks(1);
	Category cg = this.get(category);
	System.out.println("类别名:"+cg.getCategoryName());
	for (Book b : cg.getBooks()) {
		System.out.println("该类别下的书本:"+b.getBookName());
	}
}

}
运行效果:

hibernate多对多查询语句生成过程分析(用一些伪代码来讲解):

    <!-- 
	以查询一本书(ID为4)为例
	session.get(Book.class,4)->生成的SQL语句:select * from t_hibernate_book where book_id=4
	得到一个结果集ResultSet->4	三国演义	50
	通过反射实例化一个对象Book b = Class.forName("com.zking.five.entity.Book").newInstance();
	b.setBookId(5);分别给属性赋值
	b.setBookName(a);
	b.setPrice(10);
	
	hibernate处理关联关系:
	1、通过set标签找到桥接表(table属性)
	2、找到当前实体类对应表的主键在桥接表中的外键(key标签里的column属性)
		生成的SQL语句:select cid from t_hibernate_book_category where bid=4
		得到结果集ResultSet->重要的在最后一列
		6	4	1
		7	4	3
	3、查出关联表(t_hibernate_category)的主键(category_id)->List<String>=1,3
	4、查出来的外键关联了一个实体类(class=com.zking.five.entity.Category),它可以
	找到这个类的映射文件(class标签的name=com.zking.five.entity.Category),从而
	找到了对应的实体类对应的表的主键id标签中的column字段->category_id
		生成的SQL语句:select * from t_hibernate_category where category_id in(1,3)
		得到结果集ResultSet->
		1	古典
		3	历史
	通过反射实例化一个对象Category c = Class.forName("com.zking.five.entity.Category").newInstance();
            public List<T> foreach(ResultSet rs) throws Exception {
			List<T> categories=new ArrayList<>();
			while(rs.next()) {
				/*
				 * 1、new个对象
				 * 2、给对象赋值	
				 * 3、把有值的对象装到list容器中
				 * 4、list集合返回
				 */
				T t = (T) clz.newInstance();
				Field[] dFields = clz.getDeclaredFields();
				for (Field field : dFields) {
					field.setAccessible(true);
					field.set(t, rs.getObject(field.getName()));
				}
				categories.add(t);
			}
			return categories;
		}
	5、b.setCategories(categories)
            然后在调用方就可以通过b.getCategories()来得到categorie集合了
 -->

多对多关系注意事项:

1、一定要定义一个主控方,即配置文件里inverse属性要一个为true一个为false

2、多对多删除:a、主控方直接删除
b 、被控方先通过主控方解除多对多关系,再删除被控方
c、禁用级联删除

3、关联关系编辑,不需要直接操作桥接表,hibernate的主控方会自动维护

猜你喜欢

转载自blog.csdn.net/qq_43195035/article/details/83449017