Hibernate学习总结(5)——一对多的级联操作

首先介绍一下一对多是什么,一对多关系是关系数据库中两个表之间的一种关系,该关系中第一个表中的单个行可以与第二个表中的一个或多个行相关,但第二个表中的一个行只可以与第一个表中的一个行相关。

例如:我有一个Customer类表示客户,一个Linkman类表示联系人。在这个关系中,我定义一个客户可以对应多个联系人,而一个联系人只能对应一个客户,那么这里的客户就是“一”,联系人就是“多”。

一对多关系的建表原则:在“多”的一方创建字段作为外键,指向“一”的一方的主键。

Hibernate学习总结(5)——一对多的级联操作_第1张图片

例如上图的例子,在“多”的一方创建一个外键cid,指向“一”的一方。例子中的客户张三就有两个联系人。


首先创建两个实体类Customer类和Linkman类,注意在这两个类中要相互关联

我的Customer类中采用Set集合的方式关联Linkman类来表示一个客户可以有多个联系人

package com.hibernate.entity;

import java.util.HashSet;
import java.util.Set;

public class Customer {
	//客户id
	private Integer cid;
	//客户姓名
	private String cName;
	//客户等级
	private String cLevel;
	//客户来源
	private String cSource;
	//客户联系电话
	private String cPhone;
	//客户邮箱
	private String cMail;
	//联系人
	private Set linkmans = new HashSet();
	
	public Set getLinkmans() {
		return linkmans;
	}
	public void setLinkmans(Set linkmans) {
		this.linkmans = linkmans;
	}
	public Integer getCid() {
		return cid;
	}
	public String getcName() {
		return cName;
	}
	public String getcLevel() {
		return cLevel;
	}
	public String getcSource() {
		return cSource;
	}
	public String getcPhone() {
		return cPhone;
	}
	public String getcMail() {
		return cMail;
	}
	public void setCid(Integer cid) {
		this.cid = cid;
	}
	public void setcName(String cName) {
		this.cName = cName;
	}
	public void setcLevel(String cLevel) {
		this.cLevel = cLevel;
	}
	public void setcSource(String cSource) {
		this.cSource = cSource;
	}
	public void setcPhone(String cPhone) {
		this.cPhone = cPhone;
	}
	public void setcMail(String cMail) {
		this.cMail = cMail;
	}
	@Override
	public String toString() {
		return "Customer [cid=" + cid + ", cName=" + cName + ", cLevel=" + cLevel + ", cSource=" + cSource + ", cPhone="
				+ cPhone + ", cMail=" + cMail + ", linkmans=" + linkmans + "]";
	}
}
package com.hibernate.entity;

public class Linkman {
	//联系人id
	private Integer lkm_id;
	//联系人姓名
	private String lkm_name;
	//联系人性别
	private String lkm_gender;
	//联系人电话
	private String lkm_phone;
	//所属客户
	private Customer lkm_customer;
	public Customer getLkm_customer() {
		return lkm_customer;
	}
	public void setLkm_customer(Customer lkm_customer) {
		this.lkm_customer = lkm_customer;
	}
	public Integer getLkm_id() {
		return lkm_id;
	}
	public String getLkm_name() {
		return lkm_name;
	}
	public String getLkm_gender() {
		return lkm_gender;
	}
	public String getLkm_phone() {
		return lkm_phone;
	}
	public void setLkm_id(Integer lkm_id) {
		this.lkm_id = lkm_id;
	}
	public void setLkm_name(String lkm_name) {
		this.lkm_name = lkm_name;
	}
	public void setLkm_gender(String lkm_gender) {
		this.lkm_gender = lkm_gender;
	}
	public void setLkm_phone(String lkm_phone) {
		this.lkm_phone = lkm_phone;
	}
	@Override
	public String toString() {
		return "Linkman [lkm_id=" + lkm_id + ", lkm_name=" + lkm_name + ", lkm_gender=" + lkm_gender + ", lkm_phone="
				+ lkm_phone + ", lkm_customer=" + lkm_customer + "]";
	}
}

实体类有了,我们可以开始配置hibernate的对象映射文件了,推荐采用一个实体类创建一个对象映射文件的方式,注意在这里的配置中我们就要进行级联的相关配置了

Customer.hbm.xml文件的配置如下:




	
		
			
		
		
		
		
		
		
		
		
		
			
			
			
			
		
	

Linkman.hbm.xml文件的配置如下:





	
	
	 	
		
		
			
		
		
		
		
		
		
		 
	

•    元素来映射组成关系

–  name: 设定待映射的持久化类的属性的名字

–  column: 设定和持久化类的属性对应的表的外键

–  class:设定待映射的持久化类的属性的类型

•    元素来映射持久化类的 set 类型的属性

–  name: 设定待映射的持久化类的属性的

•    元素设定与所关联的持久化类对应的表的外键

–  column: 指定关联表的外键名

•    元素设定集合属性中所关联的持久化类

class: 指定关联的持久化类的类名


配置了对象映射文件后就是hibernate的核心配置文件hibernate.cfg.xml了




  
  	
		com.mysql.jdbc.Driver
		jdbc:mysql://localhost:3306/javadatabase
		root
		*******
		
		
		thread
		
		true
		
		true
		
		 update
		 
		  org.hibernate.dialect.MySQL5InnoDBDialect
		
		
		
  

级联操作:

    1.级联保存:

在对象 关系映射文件中 , 用于映射持久化类之间关联关系的元素 ,, 都有一个 cascade 属性 , 它用于指定如何操纵与当前对象关联的其他对象
Hibernate学习总结(5)——一对多的级联操作_第2张图片

    这里我配置了标签的cascade属性进行简化,即在“一”的一方的对象映射文件中进行配置

对应上文Customer.hbm.xml中的save-update

测试代码如下:

//简化级联保存,需在配置“一”一方的对象映射文件中为标签配置cascade属性为save-update
	@Test
	public void testSave(){
		Session session = null;
		Transaction tx = null;
		try {
			session = HibernateUtil.getSession();
			tx = session.beginTransaction();
			//创建客户
			Customer customer = new Customer();
			customer.setcName("张三");
			customer.setcLevel("vip");
			customer.setcSource("网站");
			customer.setcPhone("13622222222");
			customer.setcMail("[email protected]");
			//创建联系人
			Linkman linkman = new Linkman();
			linkman.setLkm_name("王舞");
			linkman.setLkm_gender("女");
			linkman.setLkm_phone("13678944321");
			//关联客户和联系人
			customer.getLinkmans().add(linkman);
			//保存
			session.save(customer);
			tx.commit();
		} catch (Exception e) {
			tx.rollback();
		}finally {
			session.close();
		}
	}

执行完成后,可以发现数据库中创建了对应的两张表

Hibernate学习总结(5)——一对多的级联操作_第3张图片

表中信息如下:

Hibernate学习总结(5)——一对多的级联操作_第4张图片

Hibernate学习总结(5)——一对多的级联操作_第5张图片

接下来我们看看控制台中的sql语句的输出

首先创建了两张表

Hibernate学习总结(5)——一对多的级联操作_第6张图片

然后为hibernate_Linkman添加了外键指向hibernate_Customer的cid

Hibernate学习总结(5)——一对多的级联操作_第7张图片

再为两张表添加数据

Hibernate学习总结(5)——一对多的级联操作_第8张图片

最后为hibernate_Linkman中的外键赋值

Hibernate学习总结(5)——一对多的级联操作_第9张图片

以上就是创建并保存的全过程。

级联修改

这里我们首先看看数据库中修改前的数据

Hibernate学习总结(5)——一对多的级联操作_第10张图片

Hibernate学习总结(5)——一对多的级联操作_第11张图片

接下来我们将李四的联系人赵柳,改到张三的联系人中

测试代码如下

//级联修改
	@Test
	public void testUpdate(){
		Session session = null;
		Transaction tx = null;
		try {
			session = HibernateUtil.getSession();
			tx = session.beginTransaction();
			//根据id获取客户
			Customer customer = session.get(Customer.class, 1);
			//获取联系人
			Linkman linkman = session.get(Linkman.class, 2);
			//修改持久态对象实现数据库自动更新
			customer.getLinkmans().add(linkman);
			linkman.setLkm_customer(customer);
			tx.commit();
		} catch (Exception e) {
			tx.rollback();
		}finally {
			session.close();
		}
	}

此时我们发现数据库中数据改变了

Hibernate学习总结(5)——一对多的级联操作_第12张图片

但此时我们看到控制台中的update语句执行了两次

Hibernate: 
    select
        customer0_.cid as cid1_0_0_,
        customer0_.cName as cName2_0_0_,
        customer0_.cLevel as cLevel3_0_0_,
        customer0_.cSource as cSource4_0_0_,
        customer0_.cPhone as cPhone5_0_0_,
        customer0_.cMail as cMail6_0_0_ 
    from
        hibernate_Customer customer0_ 
    where
        customer0_.cid=?
Hibernate: 
    select
        linkman0_.lkm_id as lkm_id1_1_0_,
        linkman0_.lkm_name as lkm_name2_1_0_,
        linkman0_.lkm_gender as lkm_gend3_1_0_,
        linkman0_.lkm_phone as lkm_phon4_1_0_,
        linkman0_.clid as clid5_1_0_ 
    from
        hibernate_Linkman linkman0_ 
    where
        linkman0_.lkm_id=?
Hibernate: 
    select
        linkmans0_.clid as clid5_1_0_,
        linkmans0_.lkm_id as lkm_id1_1_0_,
        linkmans0_.lkm_id as lkm_id1_1_1_,
        linkmans0_.lkm_name as lkm_name2_1_1_,
        linkmans0_.lkm_gender as lkm_gend3_1_1_,
        linkmans0_.lkm_phone as lkm_phon4_1_1_,
        linkmans0_.clid as clid5_1_1_ 
    from
        hibernate_Linkman linkmans0_ 
    where
        linkmans0_.clid=?
Hibernate: 
    update
        hibernate_Linkman 
    set
        lkm_name=?,
        lkm_gender=?,
        lkm_phone=?,
        clid=? 
    where
        lkm_id=?
Hibernate: 
    update
        hibernate_Linkman 
    set
        clid=? 
    where
        lkm_id=?

这就说明效率并不高,同样的数据进行了两次update。

这里就要介绍一下的inverse属性了

hibernate 中通过对 inverse 属性的来决定是由双向关联的哪一方来维护表和表之间的关系 .
inverse = false 的为主动方, inverse= true 的为被动方 , 由主动方负责维护关联关系。
即inverse = true 表示放弃关系维护,false为不放弃关系维护。
在没有设置 inverse=true 的情况下,父子两边都维护父子

  关系 

一对多 关系中,将“多”的一方设为主控方将有助于性能改善

接下来我们进行配置,将Customer.hbm.xml中的标签添加inverse属性值为true,即Customer放弃关系维护,而交由Linkman来进行关系的维护(因为不设置inverse属性则默认为false)

然后我们再把刚刚做的数据库修改再改回去。

@Test
	public void testUpdate(){
		Session session = null;
		Transaction tx = null;
		try {
			session = HibernateUtil.getSession();
			tx = session.beginTransaction();
			//根据id获取客户
			Customer customer = session.get(Customer.class, 2);
			//获取联系人
			Linkman linkman = session.get(Linkman.class, 2);
			//修改持久态对象实现数据库自动更新
			customer.getLinkmans().add(linkman);
			linkman.setLkm_customer(customer);
			tx.commit();
		} catch (Exception e) {
			tx.rollback();
		}finally {
			session.close();
		}
	}

可以看到数据已经修改回原来的值了

Hibernate学习总结(5)——一对多的级联操作_第13张图片

现在控制台中的输出如下:

Hibernate: 
    select
        customer0_.cid as cid1_0_0_,
        customer0_.cName as cName2_0_0_,
        customer0_.cLevel as cLevel3_0_0_,
        customer0_.cSource as cSource4_0_0_,
        customer0_.cPhone as cPhone5_0_0_,
        customer0_.cMail as cMail6_0_0_ 
    from
        hibernate_Customer customer0_ 
    where
        customer0_.cid=?
Hibernate: 
    select
        linkman0_.lkm_id as lkm_id1_1_0_,
        linkman0_.lkm_name as lkm_name2_1_0_,
        linkman0_.lkm_gender as lkm_gend3_1_0_,
        linkman0_.lkm_phone as lkm_phon4_1_0_,
        linkman0_.clid as clid5_1_0_ 
    from
        hibernate_Linkman linkman0_ 
    where
        linkman0_.lkm_id=?
Hibernate: 
    select
        linkmans0_.clid as clid5_1_0_,
        linkmans0_.lkm_id as lkm_id1_1_0_,
        linkmans0_.lkm_id as lkm_id1_1_1_,
        linkmans0_.lkm_name as lkm_name2_1_1_,
        linkmans0_.lkm_gender as lkm_gend3_1_1_,
        linkmans0_.lkm_phone as lkm_phon4_1_1_,
        linkmans0_.clid as clid5_1_1_ 
    from
        hibernate_Linkman linkmans0_ 
    where
        linkmans0_.clid=?
Hibernate: 
    update
        hibernate_Linkman 
    set
        lkm_name=?,
        lkm_gender=?,
        lkm_phone=?,
        clid=? 
    where
        lkm_id=?

可以看到update语句只有一句了,则提高了效率。

级联删除

先看目前数据库中数据的情况

Hibernate学习总结(5)——一对多的级联操作_第14张图片

Hibernate学习总结(5)——一对多的级联操作_第15张图片

接下来我们删除第二个客户李四,由于第二个客户李四的联系人是赵柳,那么删除李四的同时也会删除赵柳。

现在控制台中的输出如下:

//级联删除,在“一”的一方标签中配置cascade属性为delete(多个值用英文的“,”隔开)
	@Test
	public void testDelete(){
		Session session = null;
		Transaction tx = null;
		try {
			session = HibernateUtil.getSession();
			tx = session.beginTransaction();
			//根据id获取客户
			Customer customer = session.get(Customer.class, 2);
			//删除
			session.delete(customer);
			tx.commit();
		} catch (Exception e) {
			tx.rollback();
		}finally {
			session.close();
		}
	}

执行之后数据库的变化

Hibernate学习总结(5)——一对多的级联操作_第16张图片

Hibernate学习总结(5)——一对多的级联操作_第17张图片

发现两张表的第二个记录都被删除了。

接着我们看看控制台中的sql语句的输出

Hibernate: 
    select
        customer0_.cid as cid1_0_0_,
        customer0_.cName as cName2_0_0_,
        customer0_.cLevel as cLevel3_0_0_,
        customer0_.cSource as cSource4_0_0_,
        customer0_.cPhone as cPhone5_0_0_,
        customer0_.cMail as cMail6_0_0_ 
    from
        hibernate_Customer customer0_ 
    where
        customer0_.cid=?
Hibernate: 
    select
        linkmans0_.clid as clid5_1_0_,
        linkmans0_.lkm_id as lkm_id1_1_0_,
        linkmans0_.lkm_id as lkm_id1_1_1_,
        linkmans0_.lkm_name as lkm_name2_1_1_,
        linkmans0_.lkm_gender as lkm_gend3_1_1_,
        linkmans0_.lkm_phone as lkm_phon4_1_1_,
        linkmans0_.clid as clid5_1_1_ 
    from
        hibernate_Linkman linkmans0_ 
    where
        linkmans0_.clid=?
Hibernate: 
    delete 
    from
        hibernate_Linkman 
    where
        lkm_id=?
Hibernate: 
    delete 
    from
        hibernate_Customer 
    where
        cid=?
我们发现一个细节,hibernate是先删除了linkman再删除的customer。这是因为linkman中含有一个外键,若先删除customer,则有可能linkman中原来所指向的customer就没有了,那么逻辑上的对应关系就不存在了,所以数据库不允许我们这样操作。

你可能感兴趣的:(hibernate,hibernate,一对多关系,级联操作)