上学期数据库有学过,只要是N-M关联,都要为这个关联关系单独生成一个数据库模式,即生成一张表出来。以记录哪条记录对应了哪条记录。
例如,顾客和商品之间的关联就是一个N-M关联,顾客可以购买多种商品,一种商品也可以由很多顾客购买。
单向N-M关联
在单向N-M关联中,只要一方对于另外一方是可寻的,所以体现在PO上就是一个对象组合了一个另一个对象的集合作为属性。
在映射文件中,只要在组合了对方对象集合的属性上的<set .../>
内使用<many-to-many .../>
元素配置即可。
例如从顾客能找到他买的全部商品,这是一种单向N-M关联。
Customer类的属性
private Integer id;
private String userName;
private String password;
private Set<Product> products;// 每个顾客组合他购买过的商品的集合
Customer.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="Customer" table="CUSTOMER">
<id name="id" column="ID">
<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属性配置N-M关联,会自动生成一张关联关系表 -->
<set name="products" table="CSTMR_PRDCT">
<!-- 设置在连接表中关于本表的外键名 -->
<key column="CUSID" />
<!-- 设置连接表中关于另一表的外键名,需要额外指出其实现类 -->
<many-to-many class="Product" column="PROID" />
</set>
</class>
</hibernate-mapping>
Product类的属性
private Integer id;
private String proName;
private Double price;
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">
<id name="id" column="ID">
<generator class="native" />
</id>
<property name="proName" column="PRONAME" type="string"
not-null="true" />
<property name="price" column="PRICE" type="double" not-null="true" />
</class>
</hibernate-mapping>
Main.java
package myApplication;
import java.util.HashSet;
import java.util.Set;
import myPOJO.Customer;
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);
Product pro2 = new Product("冰红茶", 3.5);
Customer cus1 = new Customer("刘知昊", "123", null);
// 设置关联
Set<Product> myst = new HashSet<Product>();
myst.add(pro1);
myst.add(pro2);
cus1.setProducts(myst);
// 获取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();
}
}
运行结果
控制台输出:
Hibernate: insert into PRODUCT (PRONAME, PRICE) values (?, ?)
Hibernate: insert into PRODUCT (PRONAME, PRICE) values (?, ?)
Hibernate: insert into CUSTOMER (USERNAME, PASSWORD) values (?, ?)
Hibernate: insert into CSTMR_PRDCT (CUSID, PROID) values (?, ?)
Hibernate: insert into CSTMR_PRDCT (CUSID, PROID) values (?, ?)
数据库中生成:
双向N-M关联
前面学过,只要是双向关联,体现在OO层面上就是两边都要组合对方为属性。
在双向1-1关联中是直接组合对方的对象;在双向N-M关联中组合的是对方的对象的引用所组成的集合的上转型set。
另外,两边需要镜像地配置<set .../>
元素以及其内的<many-to-many .../>
元素。
Customer类的属性
private Integer id;
private String userName;
private String password;
private Set<Product> products;// 组合对方的集合
Customer.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="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属性配置N-M关联,会自动生成一张关联关系表 -->
<set name="products" table="CSTMR_PRDCT" cascade="all">
<!-- 设置在连接表中关于本表的外键名 -->
<key column="CUSID"/>
<!-- 设置连接表中关于另一表的外键名,需要额外指出其实现类 -->
<many-to-many class="Product" column="PROID"/>
</set>
</class>
</hibernate-mapping>
Product类的属性
增设对方对象的set集合。
private Integer id;
private String proName;
private Double price;
private Set<Customer> cus;// 组合对方的集合
Product.hbm.xml
镜像地配置N-M关联。
<!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属性配置N-M关联,会自动生成一张关联关系表 -->
<set name="cus" table="CSTMR_PRDCT" cascade="all">
<!-- 设置在连接表中关于本表的外键名 -->
<key column="PROID"/>
<!-- 设置连接表中关于另一表的外键名,需要额外指出其实现类 -->
<many-to-many class="Customer" column="CUSID"/>
</set>
</class>
</hibernate-mapping>
Main.java
如果将注释部分打开,会因为Hibernate的这两种PO共同维护了N-M关联表,而造成重复插入,会报主键冲突的错误。
在网上找了很久都没有解决的办法,所以干脆将一边的set集设定为null
,这样就不会对共同维护的N-M关联表造成重复insert
,毕竟目的只是将他们存入数据库中,在OO层面上不是很好看,但存到数据库里完全没问题。
package myApplication;
import java.util.HashSet;
import java.util.Set;
import myPOJO.Customer;
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);
// 获取Session对象
Session sssn = HibernateUtils.getSession();
// 开启事务
Transaction trnsctn = sssn.beginTransaction();
// Set<Customer> st_pro1 = new HashSet<Customer>();
// st_pro1.add(cus1);
// pro1.setCus(st_pro1);
sssn.save(pro1);
// Set<Customer> st_pro2 = new HashSet<Customer>();
// st_pro2.add(cus1);
// pro2.setCus(st_pro2);
sssn.save(pro2);
Set<Product> st_cus1 = new HashSet<Product>();
st_cus1.add(pro1);
st_cus1.add(pro2);
cus1.setProducts(st_cus1);
sssn.save(cus1);
// 提交事务
trnsctn.commit();
// 关闭Session实例并且把ThreadLocal中的副本清除
HibernateUtils.closeSession();
}
}
运行结果
控制台输出:
Hibernate: insert into PRODUCT (PRONAME, PRICE) values (?, ?)
Hibernate: insert into PRODUCT (PRONAME, PRICE) values (?, ?)
Hibernate: insert into CUSTOMER (USERNAME, PASSWORD) values (?, ?)
Hibernate: insert into CSTMR_PRDCT (CUSID, PROID) values (?, ?)
Hibernate: insert into CSTMR_PRDCT (CUSID, PROID) values (?, ?)
数据库内生成:
相比单向N-M关联,双向N-M关联只在OOP层面多做了文章,在数据库层面上还是完全一样的结构。