ssh备考-02多表关联关系映射(一对一、一对多、多对多如何配置)

01搭建hibernate框架

02多表关联关系映射

03hibernate各种查询方式


目录

一、搭建环境

二、javabean和xml的基础配置

三、测试多表之间的关联配置效果

一对多级联保存:

TestSave.java

一对多级联删除:

TestDelete.java

拓展了解:

项目文件下载


一、搭建环境

具体参考上篇   (写在前面,hibernate工具类有更新 改成getCurrentSession,核心配置文件里得加一行配置,绑定开启绑定本地session,使用时不需要手动关闭,线程结束自动关闭 不过学校上课说是上ssh,最重要的spring却没教。。因此不存在service层和dao层session的传递,用上篇的getSession也完全可以,想练练ssh整合的就改过来,反正都是死代码,直接当模板用,直接复制粘贴就行了)

本篇的数据库与javabean和上篇不同       

两张表:
customer.sql 客户表

create table customer (
  custId bigint(32) not null auto_increment comment '客户编号(主键)',
  custName varchar(32) not null comment '客户名称(公司名称)',
  -- cust_linkman varchar(64) default null comment '联系人(一个客户多个联系人,此处对应map 实际多个选项 不知道填啥)',
  custPhone varchar(64) default null comment '固定电话',
  primary key (custId)
) engine=innodb auto_increment=1 default charset=utf8;

linkman.sql   联系人表

create table linkman (
  lkmId bigint(32) not null auto_increment comment '联系人编号(主键)',
  lkmName varchar(16) default null comment '联系人姓名',
  custId bigint(32) not null comment '所代表的客户id',
  lkmPhone varchar(16) default null comment '联系人办公电话',
  primary key (lkmId),
  -- key fk_linkman_customer_id (custid),
  constraint fk_linkman_customer_id foreign key (custId) references customer (custId) on delete no action on update no action
) engine=innodb auto_increment=3 default charset=utf8;

关系描述:客户与联系人关系:
                   一对多(客户一方  联系人多方)
                   一个客户对应多个联系人,一个联系人对应一个客户。
                  也即你是客户,可以通过你的兄弟,朋友,父母的多个联系人联系到你。但通过你的父母只能联系到你这个客户

表:
     多方表里写一方的主键作为外键,匹配到多方唯一的一方。即:
     联系人表里写客户表的主键作为外键,匹配到联系人对应的唯一的客户

javabean:
     多方javabean(表对应的类对象),写一个一方的类对象对应外键
     一方类对象写一个set集合,存储对应的所有的多方对象
即:
     Linkman类有一个字段属性为"Customer customer;",对应表的外键(而不是和其他属性一下写个简单的字段)
     Customer类有一个属性为"Set linkmans",存储客户所有的中介联系人,表中可以没有对应的列

详细下面有

配好后目录如下:

ssh备考-02多表关联关系映射(一对一、一对多、多对多如何配置)_第1张图片

二、javabean和xml的基础配置

Customer.java

package cn.ahpu.domain;

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

public class Customer {
	private Long custId;//客户id
	private String custName;//客户姓名
	private String custPhone;//客户电话
	
	//一对多,一方写集合    具体含义,一个客户有多个联系人  eg:你是客户,公司可以通过你的老师,同学,父母等好多人联系到你
	//hibernate默认使用set集合 和配置文件对应   数据库表里可以没有此属性对应的列
	private Set linkmans=new HashSet();//★ 千万注意必须手动初始化  hibernate不会帮你new
	
	//省略get/set  自己生成
	
}

Linkman.java

package cn.ahpu.domain;

public class Linkman {
	private Long lkmId;
	private String lkmName;
	private String lkmPhone;
	
	//一对多 多方写类对象  具体含义 每个联系人只能对应一个客户  eg:通过你的父母只能找到你,不能联系到别人家的孩子
	private Customer customer;//外键-----一方类写类对象     简单属性,数据库也有对应的匹配列,因而不用自己new         
	
	
	//省略get/set  自己生成
	
}

Customer.hbm.xml





	
		
		
			
		
		
		
		
		
		
		
		
			
			
		
		
	

linkman.hbm.xml





	
		
		
			
		
		
		
		
		
		
		
		
		
	

hibernate.cfg.xml



		

	
		 
		 com.mysql.jdbc.Driver
		 jdbc:mysql:///hibernate02
		 root
		 root
		 
		 org.hibernate.dialect.MySQLDialect
		 
		 thread
		 
		 
		 true
		 true
		 update
		 
		 
		 
		 
	

三、测试多表之间的关联配置效果

一对多级联保存:

其中相对于上面最原始的配置情况:
    run2           不用修改任何配置文件
    run2           Customer.hbm.xml中多配置:       
                      也即是:一方配置级联
    run2_1       仅linkman.hbm.xml中多配置:
                      也即是:多方配置级联
    run2_2      Customer.hbm.xml中多配置:       
                      linkman.hbm.xml中多配置:
                     也即是:多方和一方都配置级联 (双向互相级联保存)

注:cascade="save-update" 级联保存方式为:有则更新 没有则保存

每次测试完可以删掉数据库中的两张表,一启动测试(本质是调用getSession这个api)hibernate框架就会根据javabean和配置文件帮你自动创建表结构,自动生成表(hibernate强大到可以让一个没有学过数据库的后端程序员也能轻松地操作操作数据库,当然要是你懒不想学如何写hibernate的xml配置文件,像胡平那样先建好表然后通过eclipse反向工程来反向生成xml配置文件,就无法体会到这种强大了)

TestSave.java

package cn.ahpu.test;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import cn.ahpu.domain.Customer;
import cn.ahpu.domain.Linkman;
import cn.ahpu.utils.HibernateUtils;

/**
 * 测试一对多 保存
 * @author 软件163韩竹安
 * 2019年12月23日-下午4:36:20
 */
public class TestSave {
	
	/**
	 * 方法1:最麻烦的双向关联方式,保存数据(就是手动调用get/set方法将对方设置进来)
	 * 优点:不用再动配置文件
	 * 缺点:手动set/get 麻烦
	 */
	@Test
	public void run1() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction tr = session.beginTransaction();
		
		//添加客户"天河" 他有两个联系人 "天河爸" "天河妈"  也一并添加进来
		Customer c = new Customer();
		c.setCustName("天河");
		Linkman l1 = new Linkman();
		l1.setLkmName("天河妈");
		Linkman l2 = new Linkman();
		l2.setLkmName("天河爸");
		
		//双向关联:手动双向添加
		c.getLinkmans().add(l1);
		c.getLinkmans().add(l2);
		l1.setCustomer(c);
		l2.setCustomer(c);
		
		//保存客户
		session.save(c);
		session.save(l1);
		session.save(l2);
		
		tr.commit();
		session.close();
	}
	
	/**
	 * 方法2.0:单向关联,保存数据。就是级联保存 (一方一个个添加多方,多方会自动保存一方)
	 * 优点:一方保存多方即可,不用双向保存
	 * 缺点:多配置一个级联保存
	 * 
	 * 当然也可以多方保存一方,然后只保存多方,一方自动保存。级联保存有方向性
	 * 此处保存customer,级联linkman,casecade配置到customer(一方)那边
	 * 	配置cascade属性  
	 */
	@Test
	public void run2() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction tr = session.beginTransaction();
		
		//添加客户"天河" 他有两个联系人 "天河爸" "天河妈"  也一并添加进来
		Customer c = new Customer();
		c.setCustName("天河");
		Linkman l1 = new Linkman();
		l1.setLkmName("天河妈");
		Linkman l2 = new Linkman();
		l2.setLkmName("天河爸");
		
		//单向关联:保存其中一边即可
		c.getLinkmans().add(l1);
		c.getLinkmans().add(l2);
		
		//保存客户  (不需要保存联系人了)
		session.save(c);
		
		tr.commit();
	}
	
	
	/**
	 * 方法2.1: 单向关联,保存数据。级联保存
	 * 保存linkamn(多方) 级联customer(一方)
	 * 
	 * 此处加linkman的配置:    (记得测试前先删掉customer的级联配置)
	 */
	@Test
	public void run2_1() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction tr = session.beginTransaction();
		
		//添加客户"天河" 他有两个联系人 "天河爸" "天河妈"  也一并添加进来
		Customer c = new Customer();
		c.setCustName("天河");
		Linkman l1 = new Linkman();
		l1.setLkmName("天河妈");
		Linkman l2 = new Linkman();
		l2.setLkmName("天河爸");
		
		//单向关联:保存其中一边即可   保存谁 谁主动去添加另一方
		l1.setCustomer(c);
		l2.setCustomer(c);
		
		//保存联系人   (不需要保存客户了)
		session.save(l1);//save   此时数据库中没有c1 保存customer表
		session.save(l2);//update 此时数据库中有c1  更新customer表(此处表太简单,不需要修改啥,但绝对避免了插入两条一样的记录)
		
		tr.commit();
	}
	
	/**
	 * 方法2.2: 单向关联,保存数据。级联保存
	 * 双方各保存一次 两边都配置级联
	 * 
	 * linkman.hbm.xml和customer.hbm.xml都配置cascade="save-update"  
	 */
	@Test
	public void run2_2() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction tr = session.beginTransaction();
		
		//添加客户"天河" 他有两个联系人 "天河爸" "天河妈"  也一并添加进来
		Customer c = new Customer();
		c.setCustName("天河");
		Linkman l1 = new Linkman();
		l1.setLkmName("天河妈");
		Linkman l2 = new Linkman();
		l2.setLkmName("天河爸");
		
		//单向关联:保存其中一边即可   保存谁 谁主动去添加另一方
		l1.setCustomer(c);//下面保存l1同时保存了c  
		c.getLinkmans().add(l2);//c已经在数据库中了 操作javabean=操作表  l2也会被保存到表中 因为双方都配置了级联 相互级联
							
		session.save(l1);
		
		tr.commit();
	}
	//小结:方法2.0最好用  也即是:一方配置级联  保存一方 级联多方  (然而保存数据你却不知道是先录入的一方,还是先录入的多方,所以都得掌握)
}

我想,一般情况下,就保存而言,双向都配置级联保存“casecade=“save-update””吧 这样保存任何一方,另一方要是没有的话都会级联保存,新插入没有的记录。所以我就都留着

默认配置更新1:此时默认配置变成更新1,就是双方都加个级联保存,都只多配置一个cascade属性

customer.hbm.xml

ssh备考-02多表关联关系映射(一对一、一对多、多对多如何配置)_第2张图片

linkman.hbm.xml

一对多级联删除:

delete0:        默认配置不变
delete1:        customer.hbm.xml级联里加个属性值delete:
deleteX:        相对于默认配置更新1: 仅linkman.hbm.xml配置了级联删除:
deleteY:        双方都配置了级联删除,删任何一条数据,哪怕是联系人,相互级联,整个数据库空了

TestDelete.java

package cn.ahpu.test;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import cn.ahpu.domain.Customer;
import cn.ahpu.domain.Linkman;
import cn.ahpu.utils.HibernateUtils;

/**
 * 测试一对多 级联删除
 * @author 软件163韩竹安 
 * 2019年12月23日-下午6:04:22
 */
public class TestDelete {
	// 导入数据
	@Test
	public void importDate() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction tr = session.beginTransaction();

		// 添加客户"天河" 他有两个联系人 "天河爸" "天河妈" 也一并添加进来
		Customer c = new Customer();
		c.setCustName("天河");
		Linkman l1 = new Linkman();
		l1.setLkmName("天河妈");
		Linkman l2 = new Linkman();
		l2.setLkmName("天河爸");

		// 单向关联:保存其中一边即可
		c.getLinkmans().add(l1);
		c.getLinkmans().add(l2);

		// 保存客户 (不需要保存联系人了)
		session.save(c);

		tr.commit();
	}
	
	/**
	 * hibernate正常删除   默认情况:
	 * 删除谁就只删除谁,且一定能删除成功,不会多删除与之有关联的数据,也不会报错说删不了
	 * eg:删除客户,会只删除这个客户,不会删除下面的联系人,也不会报错说你这个客户被某些联系人外键关联了而删不了
	 * 	  原因:避免外键冲突即可,hibernate会事先将此客户下的所有联系人的外键置为null,再删除就没冲突了(可以通过打印的sql语句察觉到这一点)
	 */
	@Test
	public void delete0() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction tr = session.beginTransaction();
		
		Customer c = session.get(Customer.class, 1L);
		session.delete(c);
		
		tr.commit();
	}
	
	/**
	 * 删除一方,级联删除旗下所有的多方
	 * eg:一般一方删除了,多方数据没必要保留 eg:删除部门,部门下的员工没有存在的必要的    |  删除客户,不需要联系他了,旗下的联系人也没必要还留在数据库了
	 * 	在一方级联配置里加个删除delete
	 * 	本来是:	    
	 *  加个配置改成:	   即配置级联删除
	 *  删除客户,级联删除其下的所有联系人
	 */
	@Test
	public void delete1() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction tr = session.beginTransaction();
		
		Customer c = session.get(Customer.class, 1L);
		session.delete(c);
		
		tr.commit();
	}
	
	/*******************************下面两个配置仅做了解,不需要会,实际开发用不到*****************************/
	
	/**
	 * 了解,没必要学,一般用不到
	 * 类上面的配置:先删除customer.hbm.xml里的级联删除属性(不删的话待会儿两边都级联删除,两边相互作用可能两张表的数据全没了),
	 * 			   转而在linkman.hbm.xml里加上级联删除cascade="..,delete"
	 * 			   此时若删除,联系人,同样会其所属的客户(其他联系人的客户外键变为null)
	 * 了解即可,基本用不到
	 */
	@Test
	public void deleteX() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction tr = session.beginTransaction();
		
		Linkman l = session.get(Linkman.class, 1L);
		session.delete(l);
		
		tr.commit();
	}
	
	/**
	 * 了解,没必要学,肯定用不到
	 * 两边都配置级联删除,这样删除一个联系人会删除对应客户,而客户也配置了级联,被删除时又去级联删除其下所有的联系人,
	 * 整个联系人客户群体全没了,明显不合理
	 * 
	 * 此时双方都配置了级联删除
	 */
	@Test
	public void deleteY() {
		Session session = HibernateUtils.getCurrentSession();
		Transaction tr = session.beginTransaction();
		
		Linkman l = session.get(Linkman.class, 1L);
		session.delete(l);
		
		tr.commit();
	}
}

一般一方删除,多方也级联删除比较合理,而多方删除一条记录,一方与其他多方不受影响,这样的配置最科学,也即是再加上一方的级联删除配置,多方配置不懂

配置更新2:

customer.hbm.xml:

linkman.hbm.xml:相对于更新1不变

 

 

拓展了解:

下面更多的了解下即可(考试肯定不会考,ssh框架在国内也已经过时了,主要学设计思想):

cascade属性的一些常见取值:
    * none                        -- 不使用级联
    * save-update                -- 级联保存或更新
    * delete                    -- 级联删除
    * delete-orphan                -- 孤儿删除.(注意:只能应用在一对多关系)
    * all                        -- 等价于"save-update,delete"
    * all-delete-orphan            -- 等价于"save-update delete delete-orphan"

孤儿删除(孤子删除)(只有在一对多的环境下才有孤儿删除)
    * 在一对多的关系中,可以将一的一方认为是父方.将多的一方认为是子方.孤儿删除:在解除了父子关系的时候.将子方记录就直接删除。(具体表现为在set集合中删除一个子方  本来子方应该只是数据库中的外键字段被置为null了,但孤儿删除报复心态严重,和我解除关系不是简单地外键字段=null,而是把你給弄死(直接从数据库中删了))
    简言之:多方表的外键为null的记录会被hibernate自动给删除
    *

有时让其中一方(尤其多对多)放弃外键约束 inverse="true" (了解)

cascade和inverse的区别
    cascade用来级联操作(保存、修改和删除)           ★保存和删除数据的
    inverse用来维护外键的

多对多,一般设计为具体中间实体,变成两个一对多,hibernate的一对多就不深究了

项目文件下载

今日项目文件下载:直接下载hibernate02.zip       网盘备份下载

 


最后的配置  今天无非学个级联,详细琢磨了下各种级联的效果罢了。。完全不需要


            
            

 

小结:

save-update,保存此方后,自动保存(已近有就不用了,必要时更新下)此方的javabean对象主动set,add而关联的另一方的记录

delete:级联删除,只要加上,删除了此类的一条记录,与之关联的另一方就会被级联删除(千万不能双方都配置级联,顶多一方配置个,否则相互级联,一删全删)

 

 

 

你可能感兴趣的:(备考SSH,#,Hibernate)