由于数据库中不能直接映射多对多关系,所以处理方式为创建一个桥接表(中间表),将一个多对多关系转换成两个一对多,这里以书籍和书籍类别为例来讲解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配置文件:
<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的主控方会自动维护