zoukankan      html  css  js  c++  java
  • hibernate多对多单向(双向)关系映射

    n-n(多对多)的关联关系必须通过连接表实现。下面以商品种类和商品之间的关系,即一个商品种类下面可以有多种商品,一种商品又可以属于多个商品种类,分别介绍单向的n-n关联关系和双向的n-n关联关系。

    单向的n-n关联关系

      如果仅使用两张数据表,是不能实现n-n的关联关系的,如下图:

    商品ITEM_AA属于商品种类CATEGORY_AA,但是如果商品ITEM_AA又同时属于商品种类CATEGORY_BB呢,两张数据表无法实现这种关系,所以我们需要使用到连接表,如下图所示: 

    我们添加一张连接表,其中的每一条记录表示某一个商品和某一个商品种类的对应关系。 
    单向的n-n关联关系的域模型和关系数据模型如下图所示: 

     下面来实现这种关系,首先新建两个类:

     1 public class Category {
     2 
     3     private Integer id;
     4     private String name;
     5 
     6     private Set<Item> items = new HashSet<>();
     7 
     8     //getters and setters
     9 }
    10 
    11 public class Item {
    12 
    13     private Integer id;
    14     private String name;
    15 
    16     //getters and setters
    17 }

     生成并编辑映射文件: 
      
    Category.hbm.xml

     1 <?xml version="1.0"?>
     2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
     3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
     4 
     5 <hibernate-mapping package="com.atguigu.hibernate.n2n">
     6 
     7     <class name="Category" table="CATEGORIES">
     8 
     9         <id name="id" type="java.lang.Integer">
    10             <column name="ID" />
    11             <generator class="native" />
    12         </id>
    13 
    14         <property name="name" type="java.lang.String">
    15             <column name="NAME" />
    16         </property>
    17 
    18         <!-- table: 指定中间表 -->
    19         <set name="items" table="CATEGORIES_ITEMS">
    20             <key>
    21                 <column name="C_ID" />
    22             </key>
    23             <!-- 使用 many-to-many 指定多对多的关联关系. column 执行 Set 集合中的持久化类在中间表的外键列的名称  -->
    24             <many-to-many class="Item" column="I_ID"></many-to-many>
    25         </set>
    26 
    27     </class>
    28 </hibernate-mapping>

    Item.hbm.xml

     1 <?xml version="1.0"?>
     2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
     3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
     4 
     5 <hibernate-mapping>
     6 
     7     <class name="com.atguigu.hibernate.n2n.Item" table="ITEMS">
     8 
     9         <id name="id" type="java.lang.Integer">
    10             <column name="ID" />
    11             <generator class="native" />
    12         </id>
    13 
    14         <property name="name" type="java.lang.String">
    15             <column name="NAME" />
    16         </property>
    17     </class>
    18 </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,表结构为:

    下面测试save操作和get操作: 
       
    单向n-n关联关系的save操作:

     1 @Test
     2     public void testSave(){
     3         Category category1 = new Category();
     4         category1.setName("C-AA");
     5 
     6         Category category2 = new Category();
     7         category2.setName("C-BB");
     8 
     9         Item item1 = new Item();
    10         item1.setName("I-AA");
    11 
    12         Item item2 = new Item();
    13         item2.setName("I-BB");
    14 
    15         //设定关联关系
    16         category1.getItems().add(item1);
    17         category1.getItems().add(item2);
    18 
    19         category2.getItems().add(item1);
    20         category2.getItems().add(item2);
    21 
    22         //执行保存操作
    23         session.save(category1);
    24         session.save(category2);
    25 
    26         session.save(item1);
    27         session.save(item2);
    28     }

    运行程序,根据控制台打印的sql语句,发现会先往CATEGORIES表和ITEMS表分别插入两条记录,然后再往连接表CATEGORIES_ITEMS插入四条记录,这是符合我们的预期的: 

     

    单向n-n关联关系的get操作:

    1 @Test
    2     public void testGet(){
    3         Category category = (Category) session.get(Category.class, 1);
    4         System.out.println(category.getName()); 
    5 
    6         //需要连接中间表
    7         Set<Item> items = category.getItems();
    8         System.out.println(items.size()); 
    9     }

    同1-n、1-1中类似,先查询category会使用懒加载机制,不会立即加载items,只有使用到items的时候才会加载。当加载items的时候,是通过内连接连接中间表CATEGORIES_ITEMS进行查询的: 

     

    双向的n-n关联关系

      在双向的n-n关联关系中,两端的类都需要使用集合属性,即Category中要包含一个Set<Item>,Item中也要包含一个Set<Category>,,且在数据库中同样需要使用连接表连接。其域模型和关系数据模型如下: 

     1 public class Category {
     2 
     3     private Integer id;
     4     private String name;
     5 
     6     private Set<Item> items = new HashSet<>();
     7 
     8     //getters and setters
     9 }
    10 
    11 public class Item {
    12 
    13     private Integer id;
    14     private String name;
    15 
    16     private Set<Category> categories = new HashSet<>();
    17 
    18     //getters and setters
    19 }

    生成并编辑映射文件: 
      
    Category.hbm.xml

     1 <?xml version="1.0"?>
     2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
     3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
     4 
     5 <hibernate-mapping package="com.atguigu.hibernate.n2n">
     6 
     7     <class name="Category" table="CATEGORIES">
     8 
     9         <id name="id" type="java.lang.Integer">
    10             <column name="ID" />
    11             <generator class="native" />
    12         </id>
    13 
    14         <property name="name" type="java.lang.String">
    15             <column name="NAME" />
    16         </property>
    17 
    18         <!-- table: 指定中间表 -->
    19         <set name="items" table="CATEGORIES_ITEMS">
    20             <key>
    21                 <column name="C_ID" />
    22             </key>
    23             <!-- 使用 many-to-many 指定多对多的关联关系. column 执行 Set 集合中的持久化类在中间表的外键列的名称  -->
    24             <many-to-many class="Item" column="I_ID"></many-to-many>
    25         </set>
    26 
    27     </class>
    28 </hibernate-mapping>

    Item.hbm.xml

     1 <?xml version="1.0"?>
     2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
     3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
     4 
     5 <hibernate-mapping>
     6 
     7     <class name="com.atguigu.hibernate.n2n.Item" table="ITEMS">
     8 
     9         <id name="id" type="java.lang.Integer">
    10             <column name="ID" />
    11             <generator class="native" />
    12         </id>
    13 
    14         <property name="name" type="java.lang.String">
    15             <column name="NAME" />
    16         </property>
    17 
    18         <set name="categories" table="CATEGORIES_ITEMS" inverse="true">
    19             <key column="I_ID"></key>
    20             <many-to-many class="com.atguigu.hibernate.n2n.Category" column="C_ID"></many-to-many>
    21         </set>
    22 
    23     </class>
    24 </hibernate-mapping>

    大部门内容和单向n-n中类似,只是需要在Item类中添加字段

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

    并且需要在Item类的映射文件中像Category映射文件中那样配置set节点。此外,还有一个很重要的地方,就是需要在Category和Item的其中一端的映射文件中的set节点中设置inverse=”true”属性,以放弃维护关联关系,否则两端都会维护默认关系,这在某些情况下会导致错误,下面会演示。
      现在生成数据表,表结构和单向n-n中一样:
    下面测试save操作和get操作: 
       
    双向n-n关联关系的save操作:

     1 Category category1 = new Category();
     2         category1.setName("C-AA");
     3 
     4         Category category2 = new Category();
     5         category2.setName("C-BB");
     6 
     7         Item item1 = new Item();
     8         item1.setName("I-AA");
     9 
    10         Item item2 = new Item();
    11         item2.setName("I-BB");
    12 
    13         //设定关联关系
    14         category1.getItems().add(item1);
    15         category1.getItems().add(item2);
    16 
    17         category2.getItems().add(item1);
    18         category2.getItems().add(item2);
    19 
    20         item1.getCategories().add(category1);
    21         item1.getCategories().add(category2);
    22 
    23         item2.getCategories().add(category1);
    24         item2.getCategories().add(category2);
    25 
    26         //执行保存操作
    27         session.save(category1);
    28         session.save(category2);
    29 
    30         session.save(item1);
    31         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, 1);
            System.out.println(category.getName()); 
    
            //需要连接中间表
            Set<Item> items = category.getItems();
            System.out.println(items.size()); 
        }

     双向n-n关联关系的get操作和单向n-n中一样,包含懒加载机制和内连接。 
      补充一下,双向n-n中两端的映射文件的字段对应如下图所示: 

    完事!

  • 相关阅读:
    (0)git安装
    [SCOI2008]奖励关
    bzoj1007 [HNOI2008]水平可见直线
    【bzoj1060】[ZJOI2007]时态同步
    【bzoj2705】[SDOI2012]Longge的问题
    【bzoj3505】[Cqoi2014]数三角形
    【bzoj3747】[POI2015]Kinoman
    P1072 Hankson的趣味题
    poj1845 数论 快速幂
    【bzoj1965】[Ahoi2005]SHUFFLE 洗牌
  • 原文地址:https://www.cnblogs.com/java-123/p/10473044.html
Copyright © 2011-2022 走看看