hibernate关系映射之多对多关系

xiaoxiao2025-10-16  5

由于数据库中不能直接映射多对多关系,所以处理方式为创建一个桥接表(中间表),将一个多对多关系转换成两个一对多,这里以书籍和书籍类别为例来讲解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"?> <!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()); } }

 运行效果:

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

转载请注明原文地址: https://www.6miu.com/read-5038029.html

最新回复(0)