【Hibernate学习笔记】10:使用many-to-many的单向/双向N-M关联

上学期数据库有学过,只要是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层面多做了文章,在数据库层面上还是完全一样的结构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值