实际使用时原生的N-M关联用的不多,因为这样的N-M关联表是自动生成的,只能含有两个表的主键,而不能带有其它信息。但实际上往往会带有其它属性:
如订单本身可以有购买数目这一属性,还可以有当时折扣等属性。这时就要去人为建立订单表了,然后顾客到商品的N-M关联可以拆分成两个1-N关联,借助这个自己建立的订单表。
注意在1-N关联的配置时,1方映射文件的<set .../>
里使用inverse="true"
属性来让顾客、商品等作为订单这种弱实体的主控对象,即是由顾客、商品信息的变化去更新订单这张表,而不应该是反过来的。
Customer类的属性
组合订单类对象。
private Integer id;
private String userName;
private String password;
private Set<Order> ordr;// 组合订单的集合
Customer.hbm.xml
对于cascade="all"
这里应当慎重考虑,有些时候用户被删除,但是用户的行为(订单信息)应当保存下来,这时候就应该设置成cascade="save-update"
了。
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="myPOJO">
<class name="Customer" table="CUSTOMER">
<!-- 指定saveOrUpdate为null时插入,非null时更新 -->
<id name="id" column="ID" unsaved-value="null">
<generator class="native" />
</id>
<property name="userName" column="USERNAME" type="string"
not-null="true" />
<property name="password" column="PASSWORD" type="string"
not-null="true" />
<!-- 用组合的set属性配置1-N关联 -->
<set name="ordr" table="ORDERMSG" cascade="all" inverse="true">
<!-- 设置在关联表中关于本表的外键名 -->
<key column="CUSID" />
<!-- 指出1-N关联的N方的实现类 -->
<one-to-many class="Order" />
</set>
</class>
</hibernate-mapping>
Product类的属性
同样地要组合订单类的对象。
private Integer id;
private String proName;
private Double price;
private Set<Order> ordr;// 组合订单的集合
Product.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="myPOJO">
<class name="Product" table="PRODUCT">
<!-- 指定saveOrUpdate为null时插入,非null时更新 -->
<id name="id" column="ID" unsaved-value="null">
<generator class="native" />
</id>
<property name="proName" column="PRONAME" type="string"
not-null="true" />
<property name="price" column="PRICE" type="double" not-null="true" />
<!-- 用组合的set属性配置1-N关联 -->
<set name="ordr" table="ORDERMSG" cascade="all" inverse="true">
<!-- 设置在关联表中关于本表的外键名 -->
<key column="PROID" />
<!-- 指出1-N关联的N方的实现类 -->
<one-to-many class="Order" />
</set>
</class>
</hibernate-mapping>
Order类的属性
组合了左右两个类的对象,把它们关联起来。
private Integer id;// 订单号
private Customer cus;// 顾客对象引用
private Product pro;// 商品对象引用
private int num;// 购买数目(不能为空所以没用包装类)
private Double discount;// 折扣(可以为空)
Order.hbm.xml
往两边配置N-1映射。
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="myPOJO">
<class name="Order" table="ORDERMSG">
<!-- 指定saveOrUpdate为null时插入,非null时更新 -->
<id name="id" column="ID" unsaved-value="null">
<generator class="native" />
</id>
<!-- 配置两个方向的N-1关联关系,给出实现类和对应的字段名 -->
<many-to-one name="cus" class="Customer" column="CUSID" />
<many-to-one name="pro" class="Product" column="PROID" />
<property name="num" column="NUM" type="integer" not-null="true" />
<property name="discount" column="DISCOUNT" type="double"/>
</class>
</hibernate-mapping>
Main.java
同样也是,为了存储只对一侧设置关联就够了。
package myApplication;
import java.util.HashSet;
import java.util.Set;
import myPOJO.Customer;
import myPOJO.Order;
import myPOJO.Product;
import myTools.HibernateUtils;
import org.hibernate.Session;
import org.hibernate.Transaction;
public class Main {
public static void main(String[] args) {
// 先分别建立这两个类的对象
Product pro1 = new Product("子弟薯片", 6.0, null);
Product pro2 = new Product("冰红茶", 3.5, null);
Customer cus1 = new Customer("刘知昊", "123", null);
// 建立关联类(订单)的对象
Order ordr1 = new Order(cus1, pro1, 5, 0.3);
Order ordr2 = new Order(cus1, pro2, 7, null);
// 设置关联
// 在持久化时可以不做设置关联,直接把关联对象本身持久化
// 也可以只做一侧,然后不必持久化关联对象,下面使用制作一侧的方式
Set<Order> st_cus1 = new HashSet<Order>();
st_cus1.add(ordr1);
st_cus1.add(ordr2);
cus1.setOrdr(st_cus1);
// 获取Session对象
Session sssn = HibernateUtils.getSession();
// 开启事务
Transaction trnsctn = sssn.beginTransaction();
// 持久化PO
sssn.save(pro1);
sssn.save(pro2);
sssn.save(cus1);
// 提交事务
trnsctn.commit();
// 关闭Session实例并且把ThreadLocal中的副本清除
HibernateUtils.closeSession();
}
}
刚做的时候一个BUG坑了很久,原来ORDER是SQL关键字,拿它做表名才出了语法问题,所以改成了ORDERMSG。
运行结果
控制台输出:
Hibernate: insert into PRODUCT (PRONAME, PRICE) values (?, ?)
Hibernate: insert into PRODUCT (PRONAME, PRICE) values (?, ?)
Hibernate: insert into CUSTOMER (USERNAME, PASSWORD) values (?, ?)
Hibernate: insert into ORDERMSG (CUSID, PROID, NUM, DISCOUNT) values (?, ?, ?, ?)
Hibernate: insert into ORDERMSG (CUSID, PROID, NUM, DISCOUNT) values (?, ?, ?, ?)
数据库内生成: