3.4 Hibernate多表操作

1. 一对多、多对一

Hibernate框架实现了ORM的思想,将关系数据库中表的数据映射成对象,使开发人员把对数据库的操作转化为对对象的操作,Hibernate的关联关系映射主要包括多表的映射配置、数据的增加、删除等。

数据库中多表之间存在着三种关系,也就是系统设计中的三种实体关系。如图所示。

从图可以看出,系统设计的三种实体关系分别为:多对多、一对多和一对一关系。在数据库中,实体表之间的关系映射是采用外键来描述的,具体如下:

  1. 1对多  建表原则:在多的一方创建外键指向一的一方的主键:

 1 package com.eagle.domain;
 2 
 3 import java.util.HashSet;
 4 import java.util.Set;
 5 
 6 public class Customer {
 7 
 8     private Long cust_id;
 9     private String cust_name;
10     private String cust_source;
11     private String cust_industry;
12     private String cust_level;
13     private String cust_phone;
14     private String cust_mobile;
15     private Set<LinkMan> linkmans = new HashSet<LinkMan>();        // 一个客户有多个联系人,客户中应该放有联系人的集合
16 
17     public Long getCust_id() {
18         return cust_id;
19     }
20 
21     public void setCust_id(Long cust_id) {
22         this.cust_id = cust_id;
23     }
24 
25     public String getCust_name() {
26         return cust_name;
27     }
28 
29     public void setCust_name(String cust_name) {
30         this.cust_name = cust_name;
31     }
32 
33     public String getCust_source() {
34         return cust_source;
35     }
36 
37     public void setCust_source(String cust_source) {
38         this.cust_source = cust_source;
39     }
40 
41     public String getCust_industry() {
42         return cust_industry;
43     }
44 
45     public void setCust_industry(String cust_industry) {
46         this.cust_industry = cust_industry;
47     }
48 
49     public String getCust_level() {
50         return cust_level;
51     }
52 
53     public void setCust_level(String cust_level) {
54         this.cust_level = cust_level;
55     }
56 
57     public String getCust_phone() {
58         return cust_phone;
59     }
60 
61     public void setCust_phone(String cust_phone) {
62         this.cust_phone = cust_phone;
63     }
64 
65     public String getCust_mobile() {
66         return cust_mobile;
67     }
68 
69     public void setCust_mobile(String cust_mobile) {
70         this.cust_mobile = cust_mobile;
71     }
72     
73     public Set<LinkMan> getLinkmans() {
74         return linkmans;
75     }
76 
77     public void setLinkmans(Set<LinkMan> linkmans) {
78         this.linkmans = linkmans;
79     }
80 
81     @Override
82     public String toString() {
83         return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + ", cust_source=" + cust_source
84                 + ", cust_industry=" + cust_industry + ", cust_level=" + cust_level + ", cust_phone=" + cust_phone
85                 + ", cust_mobile=" + cust_mobile + "]";
86     }
87 
88 }
Customer
 1 package com.eagle.domain;
 2 
 3 public class LinkMan {
 4 
 5     private Long lkm_id;
 6     private String lkm_name;
 7     private String lkm_gender;
 8     private String lkm_phone;
 9     private String lkm_mobile;
10     private String lkm_email;
11     private String lkm_qq;
12     private String lkm_position;
13     private String lkm_memo;
14     private Customer customer;     // Hibernate是一个ORM的框架:在关系型的数据库中描述表与表之间的关系,使用的是外键,开发语言使用是Java,面向对象的。
15     public Long getLkm_id() {
16         return lkm_id;
17     }
18     public void setLkm_id(Long lkm_id) {
19         this.lkm_id = lkm_id;
20     }
21     public String getLkm_name() {
22         return lkm_name;
23     }
24     public void setLkm_name(String lkm_name) {
25         this.lkm_name = lkm_name;
26     }
27     public String getLkm_gender() {
28         return lkm_gender;
29     }
30     public void setLkm_gender(String lkm_gender) {
31         this.lkm_gender = lkm_gender;
32     }
33     public String getLkm_phone() {
34         return lkm_phone;
35     }
36     public void setLkm_phone(String lkm_phone) {
37         this.lkm_phone = lkm_phone;
38     }
39     public String getLkm_mobile() {
40         return lkm_mobile;
41     }
42     public void setLkm_mobile(String lkm_mobile) {
43         this.lkm_mobile = lkm_mobile;
44     }
45     public String getLkm_email() {
46         return lkm_email;
47     }
48     public void setLkm_email(String lkm_email) {
49         this.lkm_email = lkm_email;
50     }
51     public String getLkm_qq() {
52         return lkm_qq;
53     }
54     public void setLkm_qq(String lkm_qq) {
55         this.lkm_qq = lkm_qq;
56     }
57     public String getLkm_position() {
58         return lkm_position;
59     }
60     public void setLkm_position(String lkm_position) {
61         this.lkm_position = lkm_position;
62     }
63     public String getLkm_memo() {
64         return lkm_memo;
65     }
66     public void setLkm_memo(String lkm_memo) {
67         this.lkm_memo = lkm_memo;
68     }
69     public Customer getCustomer() {
70         return customer;
71     }
72     public void setCustomer(Customer customer) {
73         this.customer = customer;
74     }
75     @Override
76     public String toString() {
77         return "LinkMan [lkm_id=" + lkm_id + ", lkm_name=" + lkm_name + ", lkm_gender=" + lkm_gender + ", lkm_phone="
78                 + lkm_phone + ", lkm_mobile=" + lkm_mobile + ", lkm_email=" + lkm_email + ", lkm_qq=" + lkm_qq
79                 + ", lkm_position=" + lkm_position + ", lkm_memo=" + lkm_memo + ", customer=" + customer + "]";
80     }
81     
82 }
LinkMan
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
 3 <hibernate-mapping>
 4     <class name="com.eagle.domain.Customer" table="cst_customer">
 5         <id name="cust_id" column="cust_id" >
 6             <generator class="native"></generator>
 7         </id>    
 8         <property name="cust_name" column="cust_name"></property>
 9         <property name="cust_source" column="cust_source"></property>
10         <property name="cust_industry" column="cust_industry"></property>
11         <property name="cust_level" column="cust_level"></property>
12         <property name="cust_phone" column="cust_phone"></property>
13         <property name="cust_mobile" column="cust_mobile"></property>
14         
15         <!-- 集合,一对多关系,在配置文件中配置 -->
16         <!--
17             name属性:    集合属性名
18             column属性:    外键列名
19             class属性:    与我关联的对象完整类名
20         -->
21         <set name="linkmans">
22             <key column="lkm_cust_id"></key>
23             <one-to-many class="com.eagle.domain.LinkMan" />
24         </set>
25     </class>
26 </hibernate-mapping>
Customer.hbm.xml
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
 3 <hibernate-mapping>
 4     <class name="com.eagle.domain.LinkMan" table="cst_linkman">
 5         <id name="lkm_id" column="lkm_id">
 6             <generator class="native"></generator>
 7         </id>
 8         <property name="lkm_name"></property>
 9         <property name="lkm_gender"></property>
10         <property name="lkm_phone"></property>
11         <property name="lkm_mobile"></property>
12         <property name="lkm_email"></property>
13         <property name="lkm_qq"></property>
14         <property name="lkm_position"></property>
15         <property name="lkm_memo"></property>
16         
17         <!-- 
18             name属性:    引用属性名
19             column:        外键列名
20             class属性:    与我关联的对象完整类名
21          -->
22         <many-to-one name="customer" column="lkm_cust_id" class="com.eagle.domain.Customer">
23         </many-to-one>
24     </class>
25 </hibernate-mapping>
LinkMan.hbm.xml
 1 package com.eagle.test;
 2 
 3 
 4 import org.hibernate.Session;
 5 import org.hibernate.Transaction;
 6 import org.junit.Test;
 7 
 8 import com.eagle.domain.Customer;
 9 import com.eagle.domain.LinkMan;
10 import com.eagle.utils.HibernateUtils;
11 
12 public class HibernateTest {
13 
14     private Session session = HibernateUtils.getCurrentSession();
15     
16     @Test
17     public void test() {
18         Transaction tx = session.beginTransaction();
19         // 创建一个客户
20         Customer customer = new Customer();
21         customer.setCust_name("百度");
22         // 创建3个联系人
23         LinkMan linkMan1 = new LinkMan();
24         linkMan1.setLkm_name("姜总");
25         LinkMan linkMan2 = new LinkMan();
26         linkMan2.setLkm_name("李秘书");
27         LinkMan linkMan3 = new LinkMan();
28         linkMan3.setLkm_name("王助理");
29         customer.getLinkmans().add(linkMan1);
30         customer.getLinkmans().add(linkMan2);
31         customer.getLinkmans().add(linkMan3);
32         
33         session.save(customer);
34         session.save(linkMan1);
35         session.save(linkMan2);
36         session.save(linkMan3);
37         tx.commit();
38     }
39 
40 }
test

  1. 使用set集合来描述Customer.java类中的属性linkMans。在Hibernate的映射文件中,使用<set>标签用来描述被映射类中的Set集合,<key>标签的column属性值对应文件多的一方的外键名称,在Customer.java客户类中,客户与联系人是一对多的关系,Hibernate的映射文件中,使用<one-to-many>标签来描述持久化类的一对多关联,其中class属性用来描述映射的关联类。

  2. <many-to-one>标签定义两个持久化类的关联,这种关联是数据表间的多对一关联,联系人与客户就是多对一的关系,所以用<many-to-one>标签来描述。<many-to-one>标签的name属性用来描述customer在LinkMan.java类中的属性的名称,class属性用来指定映射的类,column属性值对应表中的外键列名。

   3. 这就是双向关联,那么既然已经进行了双向的关联关系设置,那么我们还保存了双方,那如果我们只保存一方是否可以呢?也就是说我们建立了双向的维护关系,只保存客户或者只保存联系人是否可以。测试一下:

 1 @Test
 2     public void test1() {
 3         Transaction tx = session.beginTransaction();
 4         // 创建一个客户
 5         Customer customer = new Customer();
 6         customer.setCust_name("百度");
 7         // 创建3个联系人
 8         LinkMan linkMan1 = new LinkMan();
 9         linkMan1.setLkm_name("姜总");
10         LinkMan linkMan2 = new LinkMan();
11         linkMan2.setLkm_name("李秘书");
12         LinkMan linkMan3 = new LinkMan();
13         linkMan3.setLkm_name("王助理");
14         customer.getLinkmans().add(linkMan1);
15         customer.getLinkmans().add(linkMan2);
16         customer.getLinkmans().add(linkMan3);
17         
18         session.save(customer);
19 //        session.save(linkMan1);
20 //        session.save(linkMan2);
21 //        session.save(linkMan3);
22         tx.commit();
23     }
test1

这样操作显然不行,导致出现异常:瞬时对象异常,一个持久太对象关联了一个瞬时态对象,那就说明我们不能只保存一方。那如果我们就想只保存一个方向应该如何进行操作呢?

我们可以使用Hibernate的级联操作。

2. Hibernate的级联操作

它的目的就是为了简化操作,少写两行代码.

一定要用,save-update,不建议使用delete.


2.1 级联保存或更新,save-update

级联操作是指当主控方执行保存、更新或者删除操作时,其关联对象(被控方)也执行相同的操作。

在映射文件中通过对cascade属性的设置来控制是否对关联对象采用级联操作,级联操作对各种关联关系都是有效的。

级联是由方向性的,所谓的方向性指的是,在保存一的一方级联多的一方和在保存多的一方级联一的一方。

首先要确定我们要保存的主控方是哪一方,我们要保存客户,所以客户是主控方,那么需要在客户的映射文件中进行如下配置:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
 3 <hibernate-mapping>
 4     <class name="com.eagle.domain.LinkMan" table="cst_linkman">
 5         <id name="lkm_id" column="lkm_id">
 6             <generator class="native"></generator>
 7         </id>
 8         <property name="lkm_name"></property>
 9         <property name="lkm_gender"></property>
10         <property name="lkm_phone"></property>
11         <property name="lkm_mobile"></property>
12         <property name="lkm_email"></property>
13         <property name="lkm_qq"></property>
14         <property name="lkm_position"></property>
15         <property name="lkm_memo"></property>
16         
17         <!-- 
18             name属性:    引用属性名
19             column:        外键列名
20             class属性:    与我关联的对象完整类名
21          -->
22         <many-to-one name="customer" column="lkm_cust_id" class="com.eagle.domain.Customer" cascade="save-update">
23         </many-to-one>
24     </class>
25 </hibernate-mapping>
LinkMan.hbm.xml

值得注意的是,无论是哪一方维护数据,都必须要与另一方建立关系,才能自动保存和更新:

  1. 假如customer维护数据,就必须customer与linkMan建立关系,然后只需要保存customer就可以了:

  

  customer.getLinkmans().add(linkMan1);

customer.getLinkmans().add(linkMan2);

customer.getLinkmans().add(linkMan3);

  session.save(customer);

  2. 反之如果linkMan维护数据,就必须与customer建立关系,然后只需保存linkMan就可以了:

  linkMan1.setCustomer(customer);

  session.save(linkMan1);

 1 @Test
 2     public void test3() {
 3         Transaction tx = session.beginTransaction();
 4         // 创建一个客户
 5         Customer customer = new Customer();
 6         customer.setCust_name("百度");
 7         // 创建3个联系人
 8         LinkMan linkMan1 = new LinkMan();
 9         linkMan1.setLkm_name("姜总");
10         LinkMan linkMan2 = new LinkMan();
11         linkMan2.setLkm_name("李秘书");
12         LinkMan linkMan3 = new LinkMan();
13         linkMan3.setLkm_name("王助理");        
14         // 建立关系
15         linkMan1.setCustomer(customer);
16         customer.getLinkmans().add(linkMan2);
17         customer.getLinkmans().add(linkMan3);        
18 //        session.save(linkMan1);
19 //        session.save(customer);
20         session.save(linkMan2);
21         tx.commit();
22     }

  如果单独执行18行,会发现有4条insert语句:因为linkMan1关联了客户,客户又关联了linkMan2和linkMan3,所以当保存linkMan1的时候,linkMan1是可以进入到数据库的,它关联的customer也是会进入到数据库的,linkMan2和linkMan3也同样会进入到数据库,所以有4条insert语句;

 

  如果单独执行19行,这时我们保存的是customer对象,customer进入数据库,与之关联的linkMan2和linkMan3也同样进入到数据库,所以是三条insert语句;

  如果单独执行20行,只会执行1条insert语句,即linkMan2进入到数据库,但是linkMan2没有customer与之建立关系,所以客户不会保存到数据库。

2.2 Hibernate级联删除----delete

级联删除也是有方向性的,删除customer的同时级联删除linkMan,也可以删除联系人同时级联删除客户(这种需求很少)。

原来JDBC中删除客户和联系人的时候,如果有外键关系是不可以删除的,但是现在我们使用了Hibernate,其实Hibernate可以实现这样的功能,但是不会删除customer同时删除联系人,默认情况下Hibernate会怎么做?

1     @Test
2     public void test4() {
3         Transaction tx = session.beginTransaction();
4         Customer customer = session.get(Customer.class, 1l);
5         session.delete(customer);
6         tx.commit();
7     }
test4

默认情况下如果客户下面还有联系人,Hibernate会将联系人的外键置为null,然后去删除客户。那么其实有的时候我们需要删除客户的时候,同时将客户关联的联系人一并删除。这个时候我们就需要使用Hibernate的级联表保存操作了。

<set name="linkmans" cascade="delete">

<key column="lkm_cust_id"></key>

<one-to-many class="com.eagle.domain.LinkMan" />

</set>

 

 

测试代码:

@Test

public void test4() {

Transaction tx = session.beginTransaction();

Customer customer = session.get(Customer.class, 1l);

session.delete(customer);

tx.commit();

}

 

同样,我们删除的是联系人,那么联系人是主控方,需要在联系人端配置:

<many-to-one name="customer" column="lkm_cust_id" class="com.eagle.domain.Customer" cascade="delete">

</many-to-one>

 测试代码:

@Test

public void test5() {

Transaction tx = session.beginTransaction();

LinkMan linkMan1 = session.get(LinkMan.class, 3l);

session.delete(linkMan1);

tx.commit();

}

 

 

2.3 级联all操作

cascade="delete" 相当于 save-update+delete

2.4 级联双向关联产生多余的SQL语句

@Test

public void test6() {

Transaction tx = session.beginTransaction();

LinkMan linkMan1 = session.get(LinkMan.class, 1l);

Customer customer = session.get(Customer.class, 2l);

linkMan1.setCustomer(customer);

customer.getLinkmans().add(linkMan1);

tx.commit();

}

 因为双向维护关系,而且持久态对象可以自动更新数据库,更新customer的时候会修改一次外键,更新联系人的时候同样也会修改一次外键。这样会产生多余的SQL:

解决办法很简单,只需要一方放弃外键维护权即可。

<set name="linkmans" cascade="all" inverse="true">

<key column="lkm_cust_id"></key>

<one-to-many class="com.eagle.domain.LinkMan" />

</set>

3. 多对多

关系表达:

  1. 在表中

  2. 对象中

  3. ORM原数据中

多表操作:

  1. 操作关联属性

  

  2. inverse属性

 

  3. 级联属性

猜你喜欢

转载自www.cnblogs.com/eaglesour/p/9502906.html
3.4
今日推荐