Hibernate(9)_双向n对n

1.概述
①双向 n-n 关联需要两端都使用集合属性
②双向n-n关联必须使用连接表
③集合属性应增加 key 子元素用以映射外键列, 集合元素里还应增加many-to-many子元素关联实体类
④在双向 n-n 关联的两边都需指定连接表的表名及外键列的列名. 两个集合元素 set 的 table 元素的值必须指定,而且必须相同。set元素的两个子元素:key 和 many-to-many 都必须指定 column 属性,其中,key 和 many-to-many 分别指定本持久化类和关联类在连接表中的外键列名,因此两边的 key 与 many-to-many 的column属性交叉相同。也就是说,一边的set元素的key的 cloumn值为a,many-to-many 的 column 为b;则另一边的 set 元素的 key 的 column 值 b,many-to-many的 column 值为 a.
⑤对于双向 n-n 关联, 必须把其中一端的 inverse 设置为 true, 否则两端都维护关联关系可能会造成主键冲突.
2.实体类
Category.java

public class Category {

    private Integer id;
    private String name;
    //添加集合属性
    private Set<Item> item = new HashSet<>();
    //忽略getter和setter方法
    ...
}

Item.java

public class Item {

    private Integer id;
    private String name;
    //添加集合属性
    private Set<Category> category = new HashSet<Category>();
    //忽略getter和setter方法
    ... 
}

3.映射文件
Category.hbm.xml

<!-- 
    package指定实体类路径,在这个节点中的实体类就可以不用写全类名了,
    auto-import设置为true(默认)是表示:使用HQL语句时,即使不指定具体的实体类,Hibernate会自动找到唯一的名称的实体映射,将其补全为全类名,
    因为无法确定那个类是所需要的。
 -->
<hibernate-mapping package="com.withXml.bothManyToMany.entity" auto-import="false">

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

        <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:中间表  column:Category持久化类在中间表的外键-->
        <set name="item" table="BOTH_CATEGORY_ITEM" inverse="true">
            <key>
                <column name="C_ID" />
            </key>
           <!-- 使用many-to-many指定多对多的关联关系,column:指定Set集合中的持久化类在中间表的外键列的名称,即Item持久化类在中间表的外键 -->
            <many-to-many class="Item" column="I_ID"></many-to-many>
        </set>
    </class>
</hibernate-mapping>

Item.hbm.xml

<hibernate-mapping package="com.withXml.bothManyToMany.entity" auto-import="false">

    <class name="Item" table="BOTH_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>
      <!-- table:中间表  column:Item持久化类在中间表的外键-->
        <set name="category" table="BOTH_CATEGORY_ITEM" inverse="false" lazy="true">
            <key>
                <column name="I_ID" />
            </key>
            <!-- 使用many-to-many指定多对多的关联关系,column:指定Set集合中的持久化类在中间表的外键列的名称,即Category持久化类在中间表的外键 -->
            <many-to-many class="Category" column="C_ID"></many-to-many>
        </set>
    </class>
</hibernate-mapping>

4.CRUD测试
①保存操作

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

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

        //设定关联关系
        category.getItem().add(item);
        category.getItem().add(item2);

        category2.getItem().add(item);
        category2.getItem().add(item2);

        item.getCategory().add(category);
        item.getCategory().add(category2);

        item2.getCategory().add(category);
        item2.getCategory().add(category2);

        //保存操作
        session.save(category);
        session.save(category2);
        session.save(item);
        session.save(item2);

    }

②获取

@Test
    public void testGet(){
        Category category = (Category) session.get(Category.class, 1);
        System.out.println(category.getName());
        //支持懒加载
        System.out.println(category.getItem().toString());

        //需要连接中间表
        Set<Item> itmes = category.getItem();
        System.out.println(itmes.size());
    }

5.总结
①实体类:两端都需要添加集合属性
②映射文件:

Category端
<set> 元素:name属性指定该映射文件所对应的实体类的集合属性
table属性指定中间表,要与另一端映射文件中指定的中间表一致
<key> 元素:column属性指定外键,引用该映射文件对应数据表的主键,即自己的主键
<many-to-many>元素:class属性指定另一端的实体类,column指定外键,引用另一端实体类对应数据表的主键,即对方的主键,该值要与对方<key> 元素值一致
详细如下:

 <!-- table:中间表  column:Category持久化类在中间表的外键-->
 <set name="item" table="BOTH_CATEGORY_ITEM" inverse="true">
     <key>
         <column name="C_ID" />
     </key>
    <!-- 使用many-to-many指定多对多的关联关系,column:指定Set集合中的持久化类在中间表的外键列的名称,即Item持久化类在中间表的外键 -->
<many-to-many class="Item" column="I_ID"></many-to-many>
 </set>

Item端
<set> 元素:name属性指定该映射文件所对应的实体类的集合属性
table属性指定中间表,要与另一端映射文件中指定的中间表一致
<key> 元素:column属性指定外键,引用该映射文件对应数据表的主键,即自己的主键
<many-to-many>元素:class属性指定另一端的实体类,column指定外键,引用另一端实体类对应数据表的主键,即对方的主键,该值要与对方<key> 元素值一致
详细如下:


 <!-- table:中间表  column:Item持久化类在中间表的外键-->
<set name="category" table="BOTH_CATEGORY_ITEM" inverse="false" lazy="true">
     <key>
         <column name="I_ID" />
     </key>
     <!-- 使用many-to-many指定多对多的关联关系,column:指定Set集合中的持久化类在中间表的外键列的名称,即Category持久化类在中间表的外键 -->
     <many-to-many class="Category" column="C_ID"></many-to-many>
</set>

③必须把其中一端的 inverse 设置为 true, 否则两端都维护关联关系可能会造成主键冲突.

原文地址:https://www.cnblogs.com/tengpengfei/p/10453955.html