天天看點

Hibernate的多對多關聯關系(單向和雙向)

  n-n(多對多)的關聯關系必須通過連接配接表實作。下面以商品種類和商品之間的關系,即一個商品種類下面可以有多種商品,一種商品又可以屬于多個商品種類,分别介紹單向的n-n關聯關系和雙向的n-n關聯關系。

單向的n-n關聯關系

  如果僅使用兩張資料表,是不能實作n-n的關聯關系的,如下圖:

  

Hibernate的多對多關聯關系(單向和雙向)

  商品ITEM_AA屬于商品種類CATEGORY_AA,但是如果商品ITEM_AA又同時屬于商品種類CATEGORY_BB呢,兩張資料表無法實作這種關系,是以我們需要使用到連接配接表,如下圖所示:

  

Hibernate的多對多關聯關系(單向和雙向)

  我們添加一張連接配接表,其中的每一條記錄表示某一個商品和某一個商品種類的對應關系。

  單向的n-n關聯關系的域模型和關系資料模型如下圖所示:

  

Hibernate的多對多關聯關系(單向和雙向)

  下面來實作這種關系,首先建立兩個類:

public class Category {

    private Integer id;
    private String name;

    private Set<Item> items = new HashSet<>();

    //getters and setters
}

public class Item {

    private Integer id;
    private String name;

    //getters and setters
}
           

生成并編輯映射檔案:

 

Category.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.atguigu.hibernate.n2n">

    <class name="Category" table="CATEGORIES">

        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>

        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>

        <!-- table: 指定中間表 -->
        <set name="items" table="CATEGORIES_ITEMS">
            <key>
                <column name="C_ID" />
            </key>
            <!-- 使用 many-to-many 指定多對多的關聯關系. column 執行 Set 集合中的持久化類在中間表的外鍵列的名稱  -->
            <many-to-many class="Item" column="I_ID"></many-to-many>
        </set>

    </class>
</hibernate-mapping>
           

Item.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

    <class name="com.atguigu.hibernate.n2n.Item" table="ITEMS">

        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>

        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
    </class>
</hibernate-mapping>
           

  與1-n關聯關系類似,在映射時,必須在category類的映射檔案中設定set節點。但是不同的是,在set節點中指定的表為連接配接表CATEGORIES_ITEMS,而不再是所關聯的類Item所對應的表,而且,在set節點中設定的是many-to-many節點,而不再是one-to-many。many-to-many節點的class屬性指定了items集合中存放的對象類型是Item,colume屬性指定了連接配接表CATEGORIES_ITEMS中參照ITEMS表的外鍵是ITEM_ID。

  現在随便一個測試程式,發現除了生成了表CATEGORIES和表ITEMS,還生成了連接配接表CATEGORIES_ITEMS,表結構為:

  

Hibernate的多對多關聯關系(單向和雙向)

   

  

Hibernate的多對多關聯關系(單向和雙向)

  

  

Hibernate的多對多關聯關系(單向和雙向)

  

Hibernate的多對多關聯關系(單向和雙向)

  

下面測試save操作和get操作:

  

單向n-n關聯關系的save操作:

@Test
    public void testSave(){
        Category category1 = new Category();
        category1.setName("C-AA");

        Category category2 = new Category();
        category2.setName("C-BB");

        Item item1 = new Item();
        item1.setName("I-AA");

        Item item2 = new Item();
        item2.setName("I-BB");

        //設定關聯關系
        category1.getItems().add(item1);
        category1.getItems().add(item2);

        category2.getItems().add(item1);
        category2.getItems().add(item2);

        //執行儲存操作
        session.save(category1);
        session.save(category2);

        session.save(item1);
        session.save(item2);
    }
           

  運作程式,根據控制台列印的sql語句,發現會先往CATEGORIES表和ITEMS表分别插入兩條記錄,然後再往連接配接表CATEGORIES_ITEMS插入四條記錄,這是符合我們的預期的:

  

Hibernate的多對多關聯關系(單向和雙向)

  

Hibernate的多對多關聯關系(單向和雙向)

   

  

Hibernate的多對多關聯關系(單向和雙向)

  

Hibernate的多對多關聯關系(單向和雙向)

  

Hibernate的多對多關聯關系(單向和雙向)

  

單向n-n關聯關系的get操作:

@Test
    public void testGet(){
        Category category = (Category) session.get(Category.class, );
        System.out.println(category.getName()); 

        //需要連接配接中間表
        Set<Item> items = category.getItems();
        System.out.println(items.size()); 
    }
           

  同之前的1-n、1-1中類似,先查詢category會使用懶加載機制,不會立即加載items,隻有使用到items的時候才會加載。當加載items的時候,是通過内連接配接連接配接中間表CATEGORIES_ITEMS進行查詢的:

  

Hibernate的多對多關聯關系(單向和雙向)

  

雙向的n-n關聯關系

  在雙向的n-n關聯關系中,兩端的類都需要使用集合屬性,即Category中要包含一個Set<Item>,Item中也要包含一個Set<Category>,,且在資料庫中同樣需要使用連接配接表連接配接。其域模型和關系資料模型如下:

  

Hibernate的多對多關聯關系(單向和雙向)

  

  下面編寫代碼,首先建立兩個類:

public class Category {

    private Integer id;
    private String name;

    private Set<Item> items = new HashSet<>();

    //getters and setters
}

public class Item {

    private Integer id;
    private String name;

    private Set<Category> categories = new HashSet<>();

    //getters and setters
}
           

生成并編輯映射檔案:

 

Category.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.atguigu.hibernate.n2n">

    <class name="Category" table="CATEGORIES">

        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>

        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>

        <!-- table: 指定中間表 -->
        <set name="items" table="CATEGORIES_ITEMS">
            <key>
                <column name="C_ID" />
            </key>
            <!-- 使用 many-to-many 指定多對多的關聯關系. column 執行 Set 集合中的持久化類在中間表的外鍵列的名稱  -->
            <many-to-many class="Item" column="I_ID"></many-to-many>
        </set>

    </class>
</hibernate-mapping>
           

Item.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

    <class name="com.atguigu.hibernate.n2n.Item" table="ITEMS">

        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>

        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>

        <set name="categories" table="CATEGORIES_ITEMS" inverse="true">
            <key column="I_ID"></key>
            <many-to-many class="com.atguigu.hibernate.n2n.Category" column="C_ID"></many-to-many>
        </set>

    </class>
</hibernate-mapping>
           

  大部門内容和單向n-n中類似,隻是需要在Item類中添加字段

  private Set<Category> categories = new HashSet<>();
           

并且需要在Item類的映射檔案中像Category映射檔案中那樣配置set節點。此外,還有一個很重要的地方,就是需要在Category和Item的其中一端的映射檔案中的set節點中設定inverse=”true”屬性,以放棄維護關聯關系,否則兩端都會維護預設關系,這在某些情況下會導緻錯誤,下面會示範。

  現在生成資料表,表結構和單向n-n中一樣:

  

Hibernate的多對多關聯關系(單向和雙向)

   

  

Hibernate的多對多關聯關系(單向和雙向)

  

  

Hibernate的多對多關聯關系(單向和雙向)

  

Hibernate的多對多關聯關系(單向和雙向)

  下面測試save操作和get操作:

  

雙向n-n關聯關系的save操作:

Category category1 = new Category();
        category1.setName("C-AA");

        Category category2 = new Category();
        category2.setName("C-BB");

        Item item1 = new Item();
        item1.setName("I-AA");

        Item item2 = new Item();
        item2.setName("I-BB");

        //設定關聯關系
        category1.getItems().add(item1);
        category1.getItems().add(item2);

        category2.getItems().add(item1);
        category2.getItems().add(item2);

        item1.getCategories().add(category1);
        item1.getCategories().add(category2);

        item2.getCategories().add(category1);
        item2.getCategories().add(category2);

        //執行儲存操作
        session.save(category1);
        session.save(category2);

        session.save(item1);
        session.save(item2);
           

  運作程式,和單向n-n中一樣,一共列印8條insert語句,并成功插入記錄。但是,現在我們移除Item.hbm.xml檔案中的inverse=”true”屬性,再運作程式,會抛出異常。這是因為在預設情況下n-n的兩端都會維護關聯關系,當執行上述四條save代碼後,category要維護關聯關系,往連接配接表中添加4條記錄,然後item也要維護關聯關系,往連接配接表中添加相同的4條記錄,會導緻連接配接表中主鍵重複,解決方法就是在Item.hbm.xml檔案中設定inverse=”true”屬性。

雙向n-n關聯關系的get操作:

@Test
    public void testGet(){
        Category category = (Category) session.get(Category.class, );
        System.out.println(category.getName()); 

        //需要連接配接中間表
        Set<Item> items = category.getItems();
        System.out.println(items.size()); 
    }
           

  雙向n-n關聯關系的get操作和單向n-n中一樣,包含懶加載機制和内連接配接。

  補充一下,雙向n-n中兩端的映射檔案的字段對應如下圖所示:

  

Hibernate的多對多關聯關系(單向和雙向)