Hibernate - 多对多关联关系映射

简介: Hibernate - 多对多关联关系映射

【1】单向多对多

如Category:Item=n:n。


在关系数据模型中,是需要一个中间表Category-Item来维持这种关联关系的。该表中存放Category_ID和Item_ID。


与 1-n 映射类似,必须为 set 集合元素添加 key 子元素,指定 CATEGORIES_ITEMS 表中参照 CATEGORIES 表的外键为 CATEGORIY_ID。


与 1-n 关联映射不同的是,建立 n-n 关联时, 集合中的元素使用 many-to-many。many-to-many 子元素的 class 属性指定 items 集合中存放的是 Item 对象, column 属性指定 CATEGORIES_ITEMS 表中参照 ITEMS 表的外键为 ITEM_ID。


如下所示:

  <!-- 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>


Category类如下:

public class Category {
  private Integer id;
  private String name;
  private Set<Item> items = new HashSet<>();
  public Integer getId() {
    return id;
  }
  public void setId(Integer id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public Set<Item> getItems() {
    return items;
  }
  public void setItems(Set<Item> items) {
    this.items = items;
  }
  @Override
  public String toString() {
    return "Category [id=" + id + ", name=" + name + ", items=" + items + "]";
  }
}

Category.hbm.xml如下:

<hibernate-mapping package="com.jane.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类如下:

public class Item {
  private Integer id;
  private String name;
  public Integer getId() {
    return id;
  }
  public void setId(Integer id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
}

Item.hbm.xml如下:

<hibernate-mapping package="com.jane.n2n">
    <class name="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>

【2】代码测试

① 单向多对对持久化

代码如下:

  @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);
  }

测试结果如下:

Hibernate: 
    create table CATEGORIES (
       ID integer not null auto_increment,
        NAME varchar(255),
        primary key (ID)
    ) engine=InnoDB
Hibernate: 
    create table CATEGORIES_ITEMS (
       C_ID integer not null,
        I_ID integer not null,
        primary key (C_ID, I_ID)
    ) engine=InnoDB
Hibernate: 
    create table ITEMS (
       ID integer not null auto_increment,
        NAME varchar(255),
        primary key (ID)
    ) engine=InnoDB
Hibernate: 
    alter table CATEGORIES_ITEMS 
       add constraint FKapmdmq8lb9r8dx9xcvr0s3ull 
       foreign key (I_ID) 
       references ITEMS (ID)
Hibernate: 
    alter table CATEGORIES_ITEMS 
       add constraint FK9x44r4y633v6gw9tyxlhqn3kk 
       foreign key (C_ID) 
       references CATEGORIES (ID)
Hibernate: 
    insert 
    into
        CATEGORIES
        (NAME) 
    values
        (?)
Hibernate: 
    insert 
    into
        CATEGORIES
        (NAME) 
    values
        (?)
Hibernate: 
    insert 
    into
        ITEMS
        (NAME) 
    values
        (?)
Hibernate: 
    insert 
    into
        ITEMS
        (NAME) 
    values
        (?)
Hibernate: 
    insert 
    into
        CATEGORIES_ITEMS
        (C_ID, I_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        CATEGORIES_ITEMS
        (C_ID, I_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        CATEGORIES_ITEMS
        (C_ID, I_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        CATEGORIES_ITEMS
        (C_ID, I_ID) 
    values
        (?, ?)

② 单向多对多获取操作

代码如下:

  @Test
  public void testGet(){
    //懒加载,不会立即获取Items
    Category category = (Category) session.get(Category.class, 1);
    System.out.println(category.getName()); 
    //需要连接中间表
    Set<Item> items = category.getItems();
    System.out.println(items.size()); 
  }

测试结果如下:

Hibernate: 
    select
        category0_.ID as ID1_0_0_,
        category0_.NAME as NAME2_0_0_ 
    from
        CATEGORIES category0_ 
    where
        category0_.ID=?
C-AA
Hibernate: 
    select
        items0_.C_ID as C_ID1_1_0_,
        items0_.I_ID as I_ID2_1_0_,
        item1_.ID as ID1_4_1_,
        item1_.NAME as NAME2_4_1_ 
    from
        CATEGORIES_ITEMS items0_ 
    inner join
        ITEMS item1_ 
            on items0_.I_ID=item1_.ID //这里使用了内连接查询中间表
    where
        items0_.C_ID=?
2

【3】双向多对多

如下所示:

Note :


双向 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(放弃维护关联关系), 否则两端都维护关联关系可能会造成主键冲突。


修改Item类如下:

public class Item {
  private Integer id;
  private String name;
  private Set<Category> categories = new HashSet<>();
  public Integer getId() {
    return id;
  }
  public void setId(Integer id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public Set<Category> getCategories() {
    return categories;
  }
  public void setCategories(Set<Category> categories) {
    this.categories = categories;
  }
  @Override
  public String toString() {
    return "Item [id=" + id + ", name=" + name + ", categories=" + categories + "]";
  }
}

修改Item.hbm.xml如下:

<hibernate-mapping package="com.jane.n2n">
    <class name="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>
        <!--  这里设置inverse=true表示放弃维护关联关系 -->
        <set name="categories" table="CATEGORIES_ITEMS" inverse="true">
          <key column="I_ID"></key>
          <many-to-many class="Category" column="C_ID"></many-to-many>
        </set>
    </class>
</hibernate-mapping>

【4】双向多对多测试

双向多对多持久化

代码如下:

@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);
    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);
  }

测试结果如下:

Hibernate: 
    create table CATEGORIES (
       ID integer not null auto_increment,
        NAME varchar(255),
        primary key (ID)
    ) engine=InnoDB
Hibernate: 
    create table CATEGORIES_ITEMS (
       C_ID integer not null,
        I_ID integer not null,
        primary key (C_ID, I_ID)
    ) engine=InnoDB
Hibernate: 
    create table ITEMS (
       ID integer not null auto_increment,
        NAME varchar(255),
        primary key (ID)
    ) engine=InnoDB
Hibernate: 
    alter table CATEGORIES_ITEMS 
       add constraint FKapmdmq8lb9r8dx9xcvr0s3ull 
       foreign key (I_ID) 
       references ITEMS (ID)
Hibernate: 
    alter table CATEGORIES_ITEMS 
       add constraint FK9x44r4y633v6gw9tyxlhqn3kk 
       foreign key (C_ID) 
       references CATEGORIES (ID)
Hibernate: 
    insert 
    into
        CATEGORIES
        (NAME) 
    values
        (?)
Hibernate: 
    insert 
    into
        CATEGORIES
        (NAME) 
    values
        (?)
Hibernate: 
    insert 
    into
        ITEMS
        (NAME) 
    values
        (?)
Hibernate: 
    insert 
    into
        ITEMS
        (NAME) 
    values
        (?)
Hibernate: 
    insert 
    into
        CATEGORIES_ITEMS
        (C_ID, I_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        CATEGORIES_ITEMS
        (C_ID, I_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        CATEGORIES_ITEMS
        (C_ID, I_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        CATEGORIES_ITEMS
        (C_ID, I_ID) 
    values
        (?, ?)

可以看到测试结果和【2】中单向多对多一致,不过需要注意这里Item放弃了维护关联关系,否则将会出现主键冲突异常!


至于获取测试,则和【2】中一样,只不过这里还可以从Item一端获取关联的Category(当然,如果使用本地SQL,【2】中也可以从Item一端获取关联的Category)。


目录
相关文章
|
2月前
|
Java 数据库连接 API
解锁高效开发秘籍:深入探究 Hibernate 如何优雅处理一对多与多对多关系,让数据映射再无烦恼!
【9月更文挑战第3天】Hibernate 是 Java 领域中最流行的 ORM 框架之一,广泛用于处理实体对象与数据库表之间的映射。尤其在处理复杂关系如一对多和多对多时,Hibernate 提供了丰富的 API 和配置选项。本文通过具体代码示例,展示如何使用 `@OneToMany`、`@JoinColumn`、`@ManyToMany` 和 `@JoinTable` 等注解优雅地实现这些关系,帮助开发者保持代码简洁的同时确保数据一致性。
41 4
|
6月前
|
Java 数据库连接 数据库
hibernate多对多、正向工程创建数据表——访问温馨提示
hibernate多对多、正向工程创建数据表——访问温馨提示
|
SQL XML 存储
Hibernate框架【五】——基本映射——多对多映射
Hibernate框架【五】——基本映射——多对多映射
186 0
|
6月前
|
SQL Java 关系型数据库
Hibernate - 对象关系映射文件(*.hbm.xml)详解
Hibernate - 对象关系映射文件(*.hbm.xml)详解
247 1
|
6月前
|
SQL 存储 Java
Hibernate - 继承关联关系映射
Hibernate - 继承关联关系映射
68 0
|
6月前
|
SQL Java 数据库连接
Hibernate -双向一对一关联关系映射
Hibernate -双向一对一关联关系映射
53 0
|
6月前
|
SQL XML Java
Hibernate - 单向多对一关联关系映射
Hibernate - 单向多对一关联关系映射
37 0
|
Java 数据库连接 数据库
hibernate多对多、正向工程创建数据表——访问温馨提示
hibernate多对多、正向工程创建数据表——访问温馨提示
|
XML Java 数据库连接
《Hibernate上课笔记》----class4----Hibernate继承关系映射实现详解
《Hibernate上课笔记》----class4----Hibernate继承关系映射实现详解
91 0
《Hibernate上课笔记》----class4----Hibernate继承关系映射实现详解
|
XML Java 数据库连接
《Hibernate上课笔记》-----class7----Hibernate实现多对多关联映射
《Hibernate上课笔记》-----class7----Hibernate实现多对多关联映射
90 0
《Hibernate上课笔记》-----class7----Hibernate实现多对多关联映射