1. 一对多、多对一java
Hibernate框架实现了ORM的思想,将关系数据库中表的数据映射成对象,使开发人员把对数据库的操做转化为对对象的操做,Hibernate的关联关系映射主要包括多表的映射配置、数据的增长、删除等。数据库
数据库中多表之间存在着三种关系,也就是系统设计中的三种实体关系。如图所示。session
从图能够看出,系统设计的三种实体关系分别为:多对多、一对多和一对一关系。在数据库中,实体表之间的关系映射是采用外键来描述的,具体以下:app
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 }
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 }
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>
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>
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 }
1. 使用set集合来描述Customer.java类中的属性linkMans。在Hibernate的映射文件中,使用<set>标签用来描述被映射类中的Set集合,<key>标签的column属性值对应文件多的一方的外键名称,在Customer.java客户类中,客户与联系人是一对多的关系,Hibernate的映射文件中,使用<one-to-many>标签来描述持久化类的一对多关联,其中class属性用来描述映射的关联类。dom
2. <many-to-one>标签订义两个持久化类的关联,这种关联是数据表间的多对一关联,联系人与客户就是多对一的关系,因此用<many-to-one>标签来描述。<many-to-one>标签的name属性用来描述customer在LinkMan.java类中的属性的名称,class属性用来指定映射的类,column属性值对应表中的外键列名。ide
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 }
这样操做显然不行,致使出现异常:瞬时对象异常,一个持久太对象关联了一个瞬时态对象,那就说明咱们不能只保存一方。那若是咱们就想只保存一个方向应该如何进行操做呢?this
咱们能够使用Hibernate的级联操做。spa
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>
值得注意的是,不管是哪一方维护数据,都必需要与另外一方创建关系,才能自动保存和更新:
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 }
默认状况下若是客户下面还有联系人,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. 级联属性