Hibernate多对多
以下使用订单(order)和产品(product)的例子,订单-产品关联保存在表orderitem中
1.使用many-to-many单向关联
<class name="com.tazi.domin.Orders" table="orders" catalog="hib"> <id name="id" type="java.lang.Integer"> <column name="id" /> <generator class="identity" /> </id> <property name="address" type="java.lang.String"> <column name="address" length="100" /> </property> <property name="realname" type="java.lang.String"> <column name="realname" length="20" /> </property> <set name="products" table="orderitem" cascade="save-update"> <key column="order_id" /> <many-to-many class="com.tazi.domin.Product" column="product_id"/> </set> </class> <class name="com.tazi.domin.Product" table="product" catalog="hib" > <id name="id" type="java.lang.Integer"> <column name="ID" /> <generator class="identity" /> </id> <property name="name" type="java.lang.String"> <column name="NAME" length="200" not-null="true" /> </property> <property name="price" type="java.lang.Float"> <column name="PRICE" precision="12" scale="0" /> </property> <property name="description" type="java.lang.String"> <column name="DESCRIPTION" length="2000" /> </property> </class>
(1).保存数据。
维护关系的一方(order)负责维护关系表orderitem,不负责维护另一方(product).比如session.save(order),就order本身而言,只会生成如下语句:
Hibernate: insert into hib.orders (address, realname) values (?, ?) Hibernate: insert into orderitem (order_id, product_id) values (?, ?)
所以如果order中的product没有事先和数据库中的记录建立关系的话,执行结果会报错。如果在维护关系的一方(order)的many-to-many中设置了cascade="save-update"等,则由于session.save(order)其实也会导致session.save(product),所以结果正确,如下(orders中有两个products)。
Hibernate: insert into hib.orders (address, realname) values (?, ?) Hibernate: insert into hib.product (NAME, PRICE, DESCRIPTION) values (?, ?, ?) Hibernate: insert into hib.product (NAME, PRICE, DESCRIPTION) values (?, ?, ?) Hibernate: insert into orderitem (order_id, product_id) values (?, ?) Hibernate: insert into orderitem (order_id, product_id) values (?, ?)
(2).加载数据执行Orders order=(Orders)session.get(Orders.class, 2);
Hibernate: select orders0_.id as id5_0_, orders0_.address as address5_0_, orders0_.realname as realname5_0_ from hib.orders orders0_ where orders0_.id=?
当程序中需要用到order的products成员或设置了lazy="false"后,
Hibernate: select orders0_.id as id5_0_, orders0_.address as address5_0_, orders0_.realname as realname5_0_ from hib.orders orders0_ where orders0_.id=? Hibernate: select products0_.order_id as order1_1_, products0_.product_id as product2_1_, product1_.ID as ID4_0_, product1_.NAME as NAME4_0_, product1_.PRICE as PRICE4_0_, product1_.DESCRIPTION as DESCRIPT4_4_0_ from orderitem products0_ left outer join hib.product product1_ on products0_.product_id=product1_.ID where products0_.order_id=?
使用左连接,从关联表(orderitem)再连到另一方的表(product) .
(3).使用双向关联。即在product的映射配置中增加
<set name="orders" table="orderitem" cascade="save-update" > <key column="product_id" /> <many-to-many class="com.tazi.domin.Orders" column="order_id"/> </set>
则如下保存的代码运行会报错:
Product product=new Product(); product.setName("台灯"); product.setPrice(262.3f); Orders order=new Orders(); order.setRealname("tazi"); order.setAddress("苏州"); order.getProducts().add(product); order.getProducts().add(product2); product.getOrders().add(order); session.save(order); //执行的sql语句如下 Hibernate: insert into hib.orders (address, realname) values (?, ?) Hibernate: insert into hib.product (NAME, PRICE, DESCRIPTION) values (?, ?, ?) Hibernate: insert into orderitem (order_id, product_id) values (?, ?) Hibernate: insert into orderitem (product_id, order_id) values (?, ?)
可见,同一个order和product的对应关系试图在关联表(orderitem)中保存两次。这是因为多对多的双方地位是相同的,而每一方都会去维护关系。解决办法是在其中一方的many-to-many中加上inverse="true",取消维护关系(即修改或插入orderitem)的行为,此时另一方必须担负起维护关系的责任。运行结果如下:
Hibernate: insert into hib.orders (address, realname) values (?, ?) Hibernate: insert into hib.product (NAME, PRICE, DESCRIPTION) values (?, ?, ?) Hibernate: insert into orderitem (order_id, product_id) values (?, ?)
(4).双向关联时,如果双方lazy都为false,则执行加载语句
Product product=(Product)session.get(Product.class, 8);执行的结果是很多select语句,而且语句的个数是无法事先估算的,除非知道数据库表中的数据。因为加载原始product,加载原始product对应的orders,然后每个order都要分别加载对应的product,而新加载出来的对象都要考虑关联对象,直到所有关联对象都已经在session中。
2.分解多对多为两个一对多关联。使用many-to-many的缺点就是无法保存多余的数据(如orderitem中的数量,购买价等)。(推荐)
MyEclipse自动生成的对应于关联表(orderitem)的类和映射文件如下:
类OrderitemId 成员Orders orders;(其实是一个产品,因为order是数据库关键字,表名取为orders,自动生成的类和对象也带了s)Product product;
类Orderitem 成员OrderitemId id;(id为复合主键,组件形式)Integer quantity;(多余的属性)
类Orders 中Set orderItems= new HashSet(0);
<class name="com.tazi.domin.Orderitem" table="orderitem" catalog="hib"> <composite-id name="id" class="com.tazi.domin.OrderitemId"> <key-many-to-one name="orders" class="com.tazi.domin.Orders"> <column name="order_id" /> </key-many-to-one> <key-many-to-one name="product" class="com.tazi.domin.Product"> <column name="product_id" /> </key-many-to-one> </composite-id> <property name="quantity" type="java.lang.Integer"> <column name="quantity" /> </property> </class>
Product中不保存关联关系。Order中保存one-to-many关系与orderitem关联。在orderitem中保存与Product和Order的关系,这就足够了。另外,为了把维护关系的责任全权托付给orderitem,在Order的one-to-many上加上inverse.如下
<set name="orderItems" cascade="save-update" inverse="true"> <key column="order_id" /> <one-to-many class="com.tazi.domin.OrderItem" /> </set>
保存如下:
Product product=new Product(); product.setName("台灯"); product.setPrice(262.3f); Orders order=new Orders(); order.setRealname("tazi"); order.setAddress("苏州"); OrderitemId id=new OrderitemId(order, product); Orderitem orderitem=new Orderitem(id,12); session.save(order); session.save(product); session.save(orderitem);
如果orderitem中的数据是从数据库中事先加载的,则在试图保存orderitem时会先检查数据库中是否已经存在这条(order-product)对应的记录。
Product product=(Product)session.get(Product.class, 11); Orders order=(Orders)session.get(Orders.class, 15); OrderitemId id=new OrderitemId(order, product); Orderitem orderitem=new Orderitem(id,12); order.getOrderItems().add(orderitem);
即使没有save语句,但当session关闭或事务提交时,会自动检查是否有需要更新到数据库的。执行sql语句如下:
Hibernate: select product0_.ID as ID4_0_, product0_.NAME as NAME4_0_, product0_.PRICE as PRICE4_0_, product0_.DESCRIPTION as DESCRIPT4_4_0_ from hib.product product0_ where product0_.ID=? Hibernate: select orders0_.id as id5_0_, orders0_.address as address5_0_, orders0_.realname as realname5_0_ from hib.orders orders0_ where orders0_.id=? Hibernate: select orderitems0_.order_id as order1_1_, orderitems0_.product_id as product2_1_, orderitems0_.order_id as order1_6_0_, orderitems0_.product_id as product2_6_0_, orderitems0_.quantity as quantity6_0_ from hib.orderitem orderitems0_ where orderitems0_.order_id=? Hibernate: select orderitem_.order_id, orderitem_.product_id, orderitem_.quantity as quantity6_ from hib.orderitem orderitem_ where orderitem_.order_id=? and orderitem_.product_id=?
如果不存在对应关系,则插入到关联表中
Hibernate: insert into hib.orderitem (quantity, order_id, product_id) values (?, ?, ?)
否则报错。