Hibernate 映射多对多关联关系

Category(商品类别)和Item(商品)是多对多的关系,在关系数据模型中,无法直接表达CATEGORIES表和ITEMS表之间的多对多,需要创建一个连接表CATEGORY_ITEM,它同时参照CATEGORIES表和ITEMS表

1.映射单向多对多关联关系

仅建立Category类到Item类的单向多对多关联,在Category类中定义items属性,而在Item类中不定义categories属性。

数据库表:

create table CATEGORIES(
   ID bigint not null,
   NAME varchar(15),
   primary key (ID)
);

create table ITEMS(
  ID bigint not null,
  NAME varchar(15),
  BASE_PRICE double precision,
  primary key(ID)
);

create table CATEGORY_ITEM(
  CATEGORY_ID bigint not null,
  ITEM_ID bigint not null,
  primary key(CATEGORY_ID,ITEM_ID),
  foreign key (CATEGORY_ID) references CATEGORIES(ID),
  foreign key (ITEM_ID) references ITEMS(ID)
);

Category.java:

public class Category implements Serializable {
    private Long id;
    private String name;
    private Set items=new HashSet();

    constructor()...; getter()...; setter()...;
}
Category.hbm.xml:

<hibernate-mapping >
  <class name="mypack.Category" table="CATEGORIES" >
    <id name="id" type="long" column="ID">
      <generator class="increment"/>
    </id>

    <property name="name" column="NAME" type="string" />
       
    <set name="items" table="CATEGORY_ITEM"
        lazy="true"
        cascade="save-update">
        <key column="CATEGORY_ID" />
        <many-to-many class="mypack.Item" column="ITEM_ID" />
    </set>
       
  </class>
</hibernate-mapping>

<set>元素的<key>子元素指定 CATEGORY_ITEM表中参照 CATEGORIES表的外键为 CATEGORY_ID<many-to-many>子元素的class属性指定items集合中存放的是Item对象,column属性指定 CATEGORY_ITEM表中参照ITEMS表的外键是 ITEM_ID

也可以使用<idbag>、<list>和<map>元素来映射Category类的items集合。

Item.java:

public class Item implements Serializable {
    private Long id;
    private String name;
    private double basePrice;

    constructor()...; getter()...; setter()...;
}
Item.hbm.xml:

<hibernate-mapping >
  <class name="mypack.Item" table="ITEMS" >
    <id name="id" type="long" column="ID">
      <generator class="increment"/>
    </id>

    <property name="name" column="NAME" type="string" />
    <property name="basePrice" column="BASE_PRICE" type="double" />
     
  </class>
</hibernate-mapping>

2.映射双向多对多关联关系

建立Category类到Item类的双向多对多关联,在Category类中定义items属性,而在Item类中也要定义categories属性。

在双向多对多关联中,必须把其中一端的inverse属性设为true。inverse属性为false的一端可以使用<set>、<idbag>、<list>和<map>元素,而在inverse属性为true的一端只能使用<set>和<bag>元素。

<idbag>和<bag>元素的相同点在于都允许在集合中存放重复元素,并且都不支持按索引位置排序,区别在于<idbag>元素要求在连接表中必须定义代理主键,而<bag>元素没有此要求。

2.1.关联两端均使用<set>元素

数据库表:

create table CATEGORIES(
   ID bigint not null,
   NAME varchar(15),
   primary key (ID)
);

create table ITEMS(
  ID bigint not null,
  NAME varchar(15),
  BASE_PRICE double precision,
  primary key(ID)
);

create table CATEGORY_ITEM(
  CATEGORY_ID bigint not null,
  ITEM_ID bigint not null,
  primary key(CATEGORY_ID,ITEM_ID),
  foreign key (CATEGORY_ID) references CATEGORIES(ID),
  foreign key (ITEM_ID) references ITEMS(ID)
);

Category.java:

public class Category implements Serializable {
    private Long id;
    private String name;
    private Set items=new HashSet();

    constructor()...; getter()...; setter()...;
}
Category.hbm.xml:

<hibernate-mapping >
  <class name="mypack.Category" table="CATEGORIES" >
    <id name="id" type="long" column="ID">
      <generator class="increment"/>
    </id>

    <property name="name" column="NAME" type="string" />
       
    <set name="items" table="CATEGORY_ITEM"
        lazy="true"
        cascade="save-update">
        <key column="CATEGORY_ID" />
        <many-to-many class="mypack.Item" column="ITEM_ID" />
    </set>
       
  </class>
</hibernate-mapping>

Item.java:

public class Item implements Serializable {
    private Long id;
    private String name;
    private double basePrice;
    private Set categories=new HashSet();

    constructor()...; getter()...; setter()...;
}
Item.hbm.xml:

<hibernate-mapping >
  <class name="mypack.Item" table="ITEMS" >
    <id name="id" type="long" column="ID">
      <generator class="increment"/>
    </id>

    <property name="name" column="NAME" type="string" />
    <property name="basePrice" column="BASE_PRICE" type="double" />
    
    <set name="categories" table="CATEGORY_ITEM"
        lazy="true"
        inverse="true"
        cascade="save-update">
        <key column="ITEM_ID" />
        <many-to-many class="mypack.Category" column="CATEGORY_ID" />
    </set>
 
  </class>
</hibernate-mapping>

2.2.在inverse端使用<bag>元素

假如在Category类中已经定义了List类型的items集合,并在Item类中定义了List类型的categories集合,只有在non-inverse的一端可以使用<list>元素,那么在inverse的一端使用什么元素,显然不能使用<set>,因为<set>不能映射<list>类型的集合,为此,Hibernate提供了<bag>元素。

数据库表:

create table CATEGORIES(
   ID bigint not null,
   NAME varchar(15),
   primary key (ID)
);

create table ITEMS(
  ID bigint not null,
  NAME varchar(15),
  BASE_PRICE double precision,
  primary key(ID)
);

create table CATEGORY_ITEM(
  POSITION bigint not null,
  CATEGORY_ID bigint not null,
  ITEM_ID bigint not null,
  primary key(POSITION,CATEGORY_ID),
  foreign key (CATEGORY_ID) references CATEGORIES(ID),
  foreign key (ITEM_ID) references ITEMS(ID)
);

Category.java:

public class Category implements Serializable {
    private Long id;
    private String name;
    private List items=new ArrayList();

    constructor()...; getter()...; setter()...;
}
Category.hbm.xml:

<hibernate-mapping >
  <class name="mypack.Category" table="CATEGORIES" >
    <id name="id" type="long" column="ID">
      <generator class="increment"/>
    </id>

    <property name="name" column="NAME" type="string" />
       
 <list name="items" table="CATEGORY_ITEM"
        lazy="true"
        cascade="save-update">
        <key column="CATEGORY_ID" />
        <index column="POSITION" />
        <many-to-many class="mypack.Item" column="ITEM_ID" />
 </list>
      
  </class>
</hibernate-mapping>

Item.java:

public class Item implements Serializable {
    private Long id;
    private String name;
    private double basePrice;
    private List categories=new ArrayList();

    constructor()...; getter()...; setter()...;
}
Item.hbm.xml:

<hibernate-mapping >
  <class name="mypack.Item" table="ITEMS" >
    <id name="id" type="long" column="ID">
      <generator class="increment"/>
    </id>

    <property name="name" column="NAME" type="string" />
    <property name="basePrice" column="BASE_PRICE" type="double" />
    
    <bag name="categories" table="CATEGORY_ITEM"
        lazy="true"
        inverse="true"
        cascade="save-update">
        <key column="ITEM_ID" />
        <many-to-many class="mypack.Category" column="CATEGORY_ID" />
    </bag>
 
  </class>
</hibernate-mapping>

2.3.使用组件类集合

Item和Order之间也是多对多的关联关系,可以通过专门的组件类LineItem来描述Order与Item的关联关系。

LineItem类作为组件类没有OID,LINEITEMS表为连接表。

数据库表:

create table ORDERS(
   ID bigint not null,
   ORDER_NUMBER varchar(15),
   primary key (ID)
);

create table ITEMS(
  ID bigint not null,
  NAME varchar(15),
  BASE_PRICE double precision,
  primary key(ID)
);

create table LINEITEMS(
  ORDER_ID bigint not null,
  ITEM_ID bigint not null,
  BASE_PRICE double precision not null,
  QUANTITY  int not null,
  primary key(ORDER_ID,ITEM_ID,BASE_PRICE,QUANTITY),
  oreign key (ORDER_ID) references ORDERS(ID),
  foreign key (ITEM_ID) references ITEMS(ID)
);

Order.java:

public class Order implements Serializable {
    private Long id;
    private String orderNumber;
    private double price;
    private Set lineItems=new HashSet();

    constructor()...; getter()...; setter()...;
}
Order.hbm.xml:

<hibernate-mapping >
  <class name="mypack.Order" table="ORDERS" >
    <id name="id" type="long" column="ID">
      <generator class="increment"/>
    </id>

    <property name="orderNumber" column="ORDER_NUMBER" type="string" />
    
    <property name="price" formula=
        "(select  sum(line.BASE_PRICE*line.QUANTITY)  from LINEITEMS line where line.ORDER_ID=ID)" />

    <set name="lineItems" lazy="true" table="LINEITEMS" >
        <key column="ORDER_ID" />
        <composite-element class="mypack.LineItem" >
          <parent name="order" />
          <many-to-one name="item" class="mypack.Item" column="ITEM_ID" not-null="true"/>
          <property name="quantity" column="QUANTITY" type="int" not-null="true" />
          <property name="basePrice" column="BASE_PRICE" type="double" not-null="true" />
         </composite-element>
    </set>
     
  </class>
</hibernate-mapping>

Order类的price属性被映射为了派生属性,因此在ORDERS表中不必定义PRICE字段。

Item.java:

public class Item implements Serializable {
    private Long id;
    private String name;
    private double basePrice;
    private Set lineItems=new HashSet();
    constructor()...; getter()...; setter()...;
}

Item.hbm.xml:

<hibernate-mapping >
  <class name="mypack.Item" table="ITEMS" >
    <id name="id" type="long" column="ID">
      <generator class="increment"/>
    </id>

    <property name="name" column="NAME" type="string" />
    <property name="basePrice" column="BASE_PRICE" type="double" />
    
     <set name="lineItems" lazy="true" inverse="true" table="LINEITEMS" >
        <key column="ITEM_ID" />
        <composite-element class="mypack.LineItem" >
          <parent name="item" />
          <many-to-one name="order" class="mypack.Order" column="ORDER_ID" not-null="true"/>
          <property name="quantity" column="QUANTITY" type="int" not-null="true" />
          <property name="basePrice" column="BASE_PRICE" type="double" not-null="true" />
         </composite-element>
    </set>
 
  </class>
</hibernate-mapping>

LineItem.java:

public class LineItem implements Serializable {
    private Item item;
    private Order order;
    private double basePrice;
    private int quantity;

    constructor()...; getter()...; setter()...;
}

2.4.把多对多关联分解为两个一对多关联

对于Order与Item的多对多关联,也可以把它分解为两个一对多关联。LineItem为独立的实体类,有单独的OID。Order类与LineItem类,以及Item类与LineItem类都是一对多的双向关联关系。

LINEITEMS表作为连接表,LINEITEMS表有单独的ID代理主键。

数据库表:

create table ORDERS(
   ID bigint not null,
   ORDER_NUMBER varchar(15),
   primary key (ID)
);

create table ITEMS(
  ID bigint not null,
  NAME varchar(15),
  BASE_PRICE double precision,
  primary key(ID)
);

create table LINEITEMS(
  ID bigint not null,
  ORDER_ID bigint not null,
  ITEM_ID bigint not null,
  BASE_PRICE double precision,
  QUANTITY  int,
  primary key(ID),
  foreign key (ORDER_ID) references ORDERS(ID),
  foreign key (ITEM_ID) references ITEMS(ID)
);

Order.java:

public class Order implements Serializable {
    private Long id;
    private String orderNumber;
    private double price;
    private Set lineItems=new HashSet();

    constructor()...; getter()...; setter()...;

    public void setLineItems(Set lineItems) {
        this.lineItems = lineItems;
        calculatePrice();
    }

    private void calculatePrice(){
      double totalPrice=0;
      if(lineItems==null) return;
      Iterator it=lineItems.iterator();
      while(it.hasNext()){
        LineItem line=(LineItem)it.next();
        totalPrice=totalPrice+line.getUnitPrice();
      }
      setPrice(totalPrice);
    }
}
setLineItems()方法中自动为price属性赋值,因此无需在Order.hbm.xml中映射price属性,也无须在ORDERS表中定义PRICE字段。setLineItems()方法调用calculatePrice()方法计算price属性值。

Order.hbm.xml:

<hibernate-mapping >
  <class name="mypack.Order" table="ORDERS" >
    <id name="id" type="long" column="ID">
      <generator class="increment"/>
    </id>

   <property name="orderNumber" column="ORDER_NUMBER" type="string" />
    
   <set name="lineItems" lazy="true" inverse="true" cascade="save-update">
        <key column="ORDER_ID" />
        <one-to-many  class="mypack.LineItem" />
   </set>
     
  </class>
</hibernate-mapping>

Item.java:

public class Item implements Serializable {
    private Long id;
    private String name;
    private double basePrice;
    private Set lineItems=new HashSet();

    constructor()...; getter()...; setter()...;
}
Item.hbm.xml:

<hibernate-mapping >
  <class name="mypack.Item" table="ITEMS" >
    <id name="id" type="long" column="ID">
      <generator class="increment"/>
    </id>

    <property name="name" column="NAME" type="string" />
    <property name="basePrice" column="BASE_PRICE" type="double" />
    
     <set name="lineItems" lazy="true" inverse="true" cascade="save-update">
        <key column="ITEM_ID" />
        <one-to-many  class="mypack.LineItem" />
     </set>
 
  </class>
</hibernate-mapping>

LineItem.java:

public class LineItem implements Serializable {
    private Long id;
    private Item item;
    private Order order;
    private double basePrice;
    private int quantity;

    constructor()...; getter()...; setter()...;
}
LineItem.hbm.xml:

<hibernate-mapping >
  <class name="mypack.LineItem" table="LINEITEMS" >
    <id name="id" type="long" column="ID">
      <generator class="increment"/>
    </id>

    <property name="quantity" column="QUANTITY" type="int" />
    <property name="basePrice" column="BASE_PRICE" type="double" />
    
    <many-to-one name="order" column="ORDER_ID" class="mypack.Order" not-null="true" />
    <many-to-one name="item" column="ITEM_ID" class="mypack.Item" not-null="true" />
 
  </class>
</hibernate-mapping>


所有的多对多关联都可以分解为两个一对多关联,按照这种方式映射多对多关联,会使域模型和关系数据模型具有更好的可扩展性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值