hibernate關係對映之多對多關係
阿新 • • 發佈:2018-12-17
由於資料庫中不能直接對映多對多關係,所以處理方式為建立一個橋接表(中間表),將一個多對多關係轉換成兩個一對多,這裡以書籍和書籍類別為例來講解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的主控方會自動維護