Hibernate 映射多对多关联关系

来源:互联网 发布:网络用语bgm是什么意思 编辑:程序博客网 时间:2024/05/22 14:41

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>


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

1 0