1. 程式人生 > >hibernate關係對映之多對多關係

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"?>
<!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的主控方會自動維護