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

书籍表(t_hibernate_book):

hibernate关系映射之多对多关系

书籍类别表(t_hibernate_category):

hibernate关系映射之多对多关系

桥接表(t_hibernate_book_category):

hibernate关系映射之多对多关系

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"?>
<!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>
		
		<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>
</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 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 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-mapping>

在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());
		}
	}

 运行效果:

hibernate关系映射之多对多关系

测试类别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关系映射之多对多关系

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的主控方会自动维护

相关文章: