(八)Hibernate的一对多关联关系

一、概述

1
2
3
4
5
6
7
例如,以客户(Customer)和订单(Order)为例,一个客户能有多个订单,一个订单只能有一个客户。
 
从Customer到Order是一对多关联,在java类中的面向对象设计应该一个Customer对象包含多个Order对象,因此应该定义一个集合,来包含所有的Order对象。 
 
从Order到Customer是多对一关联,在java类中设计每个Order对象需要关联一个Customer对象,因此Order类中应该定义一个Cutomer类型的属性,来引用关联的customer对象。
 
但是在关系数据库中,只存在主外键参照关系来表达两者的关联。

  

 

二、实例

(1)先创建两个实体类 Customer和Order

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class Customer {
                  private Integer id;
                  private String name;
                  private Set <Order> orders=new HashSet<Order>();
                  public Integer getId() {
                      return id;
                  }
                  public void setId(Integer id) {
                      this.id = id;
                  }
                  public String getName() {
                      return name;
                  }
                  public void setName(String name) {
                      this.name = name;
                  }
                  public Set getOrders() {
                      return orders;
                  }
                  public void setOrders(Set orders) {
                      this.orders = orders;
                  }
 
          }
 
          public class Order {
              private Integer id;
              private String name;
              private Customer customer;
              public Integer getId() {
                  return id;
              }
              public void setId(Integer id) {
                  this.id = id;
              }
              public String getName() {
                  return name;
              }
              public void setName(String name) {
                  this.name = name;
              }
              public Customer getCustomer() {
                  return customer;
              }
              public void setCustomer(Customer customer) {
                  this.customer = customer;
              }
 
          }

 (2)实体类写好了,使用Hibernate映射文件来映射关系 

创建Customer.hbm.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<hibernate-mapping >
    <!--指定实体类和表的映射关系-->
    <class name="com.cad.domain.Customer" table="customer">
        <id name="id" column="id">
            <generator class="native"></generator>
        </id>
        <property name="name" column="name"></property>
        <!--使用<set>元素来映射set集合类型-->
        <!--
            name:持久化类中的属性名
        -->
        <set name="orders">
            <!--<key>元素设定所关联的持久化类对应的表的外键-->
            <!--<one-to-many>元素设定关联的持久化类-->
            <key column="cid"/>
            <one-to-many class="com.cad.domain.Order"/>
        </set>
    </class>
 </hibernate-mapping> 
 
Hibernate根据映射文件获得以下信息
-<set>元素表明Customer类中的orders属性为java.util.Set集合
-<one-to-many>元素表明orders集合中存放的是一组order对象
-<key>元素表明orders表通过外键cid关联customer表

  
创建Order.hbm.xml

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<hibernate-mapping >
    <class name="com.cad.domain.Order" table="order">
        <id name="id" column="id">
            <generator class="native"></generator>
        </id>
        <property name="name" column="name"></property>
        <!--<many-to-one>元素建立了customer属性和对应表中外键的映射-->
        <!--
            name:持久化类中的属性名
            column:表中的外键
            class:关键的Customer对象实现类
        -->
        <many-to-one name="customer" column="cid" class="com.cad.domain.Customer"></many-to-one>
    </class>
 </hibernate-mapping> 

 (3)在hibernate.cfg.xml中配置映射文件

1
2
<mapping resource="com/cad/domain/Customer.hbm.xml"/>
 <mapping resource="com/cad/domain/Order.hbm.xml"/>

 (4)测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//添加方法 
   @Test
   public void test() {
       //读取配置文件
       Configuration conf=new Configuration().configure();
 
       //根据配置创建factory
       SessionFactory sessionfactory=conf.buildSessionFactory();
       session = sessionfactory.openSession();
        Transaction ts=session.beginTransaction();
        //创建Customer
       Customer c=new Customer();
       c.setName("张三");
       //创建订单
       Order o1=new Order();
       o1.setName("矿泉水");
       Order o2=new Order();
       o2.setName("方便面");
       //双向关联
       c.getOrders().add(o1);
       c.getOrders().add(o2);
       o1.setCustomer(c);
       o2.setCustomer(c);
       //保存
       session.save(c);
       session.save(o1);
       session.save(o2);
       ts.commit();
       session.close();
       sessionfactory.close();
   }
 
   然后执行,控制台会打印如下语句
           Hibernate:
               insert
               into
                   customer
                   (name)
               values
                   (?)
           Hibernate:
               insert
               into
                   orders
                   (name, cid)
               values
                   (?, ?)
           Hibernate:
               insert
               into
                   orders
                   (name, cid)
               values
                   (?, ?)
           Hibernate:
               update
                   orders
               set
                   cid=?
               where
                   id=?
           Hibernate:
               update
                   orders
               set
                   cid=?
               where
                   id=?   

  我们来执行删除操作,直接删除Customer,但由于还有Order关联着Customer 会执行成功么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
                    @Test
                    public void test() {
                        //读取配置文件
                        Configuration conf=new Configuration().configure();
 
                        //根据配置创建factory
                        SessionFactory sessionfactory=conf.buildSessionFactory();
                        session = sessionfactory.openSession();
                         Transaction ts=session.beginTransaction();
                         Customer c=session.get(Customer.class, 2);
                         //删除Customer
                        session.delete(c);
 
                        ts.commit();
                        session.close();
                        sessionfactory.close();
                    }
                执行,打印如下语句
                    Hibernate:
                        select
                            customer0_.id as id1_0_0_,
                            customer0_.name as name2_0_0_
                        from
                            customer customer0_
                        where
                            customer0_.id=?
                    Hibernate:
                        update
                            orders
                        set
                            cid=null
                        where
                            cid=?
                    Hibernate:
                        delete
                        from
                            customer
                        where
                            id=?
 
我们会发现Hibernate会自动将Order中的cid设置为null,然后执行删除操作
我们发现Hibernate还是挺智能的,但这是由inverse属性操控的。

  

三、< set >元素的inverse属性

 

1
2
3
4
5
6
7
8
9
10
11
inverse所描述的是对象之间关联关系的维护方式。 inverse属性指定由哪方来维护关联关系。
inverse默认为false,即关联关系由自己控制,若为true,则反转,关联关系由对方控制.
Inverse属性的作用是:是否将对集合对象的修改反映到数据库中。
 
在映射一对多的双向关联关系中,应该在"一"方把inverse属性设为true,由对方来维护主键关联关系.
 
所以上述例子中,inverse默认是false.即Customer维护关联关系,所以Customer会执行两条更新语句来更新Order的cid.
但是我们Order在插入的时候已经插入cid,所以这样会影响性能,我们只需要将Customer的<set>元素的inverse属性改为true即可。
 
我们的删除案例中也是,Customer执行删除时,会先去把Order的主键约束解除,然后删除。
我们只需要将Customer的inverse设置为true,然后由对方维护关联关系,我们再进行删除时,就会出现异常,因为有主键约束,我们Customer不再维护关联关系。

 

四、级联操纵

在实际应用中,对象和对象之间是相互关联的。例如我们的一对多关联关系。 
在关系-对象映射文件中,用于映射持久化类之间关联关系的元素,如 < set>,< many-to-one>,< one-to-many>,都有一个cascade属性,用来指定如何操纵与当前对象关联的其他对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
我们先看下面的例子,我们创建一个Customer,再创建两个Order,然后关联
我们只保存Customer,会抛出org.hibernate.TransientObjectException异常,这是为什么呢?
 
这是因为我们的Customer的inverse为false,关联关系由Customer维护。我们保存Customer时,
会维护Customer中orders中的所有Order的主键,但是Order是临时对象,并没有转变为持久状态,这时候就会抛出异常。
 
        @Test
        public void test() {
            //读取配置文件
            Configuration conf=new Configuration().configure();
 
            //根据配置创建factory
            SessionFactory sessionfactory=conf.buildSessionFactory();
            session = sessionfactory.openSession();
             Transaction ts=session.beginTransaction();
             Customer c=new Customer();
             c.setName("jack");
             Order o1=new Order();
             o1.setName("苹果");
             Order o2=new Order();
             o2.setName("香蕉");
             c.getOrders().add(o1);
             c.getOrders().add(o2);
             o1.setCustomer(c);
             o2.setCustomer(c);
 
             session.save(c);
 
            ts.commit();
            session.close();
            sessionfactory.close();
        }

 当Hibernate持久化一个临时对象时,并不会自动持久化所关联的其他临时对象,所以会抛出异常。 
如果我们希望Hibernate持久化对象时自动持久化所关联的其他对象,那么就需要指定cascade属性 

 (1)级联保存和更新

1
2
3
4
当我们持久化对象时自动持久化所关联的其他对象。
把cascade属性设置为save-update ,这时候我们再执行上面的代码就会自动帮我们保存Customer关联的Order对象。
 
当cascade属性为save-update时,表明保存或更新当前对象时,会级联保存或更新与它关联的对象。

 (2)级联删除

1
2
3
4
如果我们的cascade属性为delete时,我们删除当前对象,会自动删除与之关联的对象。
   慎用这个delete属性。
   例如:我们的Order配置了这个属性,Customer也配置了这个属性,我们删除订单时,因为是级联删除
   所以会查找Customer,删除Customer,但Customer也配置了级联删除,所以会查找所有关联的订单,最后会删除该客户的所有订单和该客户。

 (3)孤儿删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
如果我们的对象和关联的对象解除关系后,希望自动删除不再关联的对象。
需要将cascade设置为delete-orphan.
 
例如 ,我们设置cascade="delete-orphan"
                 Transaction ts=session.beginTransaction();
                 Customer c=session.get(Customer.class, 7);
                 Order order=(Order) c.getOrders().iterator().next();
                 c.getOrders().remove(order);
                 order.setCustomer(null);
                 ts.commit();
 
我们解除Customer和Order的关系,Hibernate就会自动删除Order。 
 
 
当cascade的值为all时,是save-update和delete的整合。
当cascade的值为all-dalete-orphan时,是all和delete-orphan的整合。

  

 

 

 

 

posted @   跃小云  阅读(237)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示