本篇总结了一对多和多对多关系的在数据库表中和实体中的表达,级联操作(cascade),关系的维护(inverse)!
一、一对多
1.1、一对多关系的表达
表中的表达(在一对多的表中,外键指向一的那一方的主键):
看下图的两张表,客户表与联系人表的关系是一对多。客户表的id是主键,在联系人表中是cid,即外键所以联系人表的外键指向了客户表的主键。
实体中的表达:
下面用代码演示客户与联系人之间的关系(本例中没有手动去建数据库表):
先分别创建实体类Customer和LinkMan:
hibernate的主配置文件中加入两行:
1.2、不用级联操作
hibernate映射文件:
Customer.hbm.xml
LinkMan.hbm.xml
(上述在配置一对多和多对一的时候属性名都是一样的,其中要注意的是column属性的值要是一样的外键列名即lm_cust_id)
写一个方法测试一下:
package com.zl.one2many;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import com.zl.domain.Customer;
import com.zl.domain.LinkMan;
import com.zl.utils.HibernateUtils;
public class TestOne2Many {
@Test
public void fun1() { //保存客户以及客户下的联系人
//1.获得session
Session session = HibernateUtils.openSession();
//2.开启事务
Transaction tx = session.beginTransaction();
//3.操作
Customer c = new Customer();
c.setCust_name("Baidu公司");
LinkMan lm1 = new LinkMan();
lm1.setLm_name("李彦宏");
LinkMan lm2 = new LinkMan();
lm2.setLm_name("陆奇");
//表达一对多,客户下有多个联系人
c.getLinkMens().add(lm1);
c.getLinkMens().add(lm2);
//表达多对多,联系人属于哪个客户
lm1.setCustomer(c);
lm2.setCustomer(c);
session.save(c);
session.save(lm1);
session.save(lm2);
//4.提交事务
tx.commit();
//5.关闭资源
session.close();
}
}
控制台打印:
数据库中:
customer表:
linkman表:
在多的一方添加一条记录:
数据库linkman表中已添加了一条记录:
为客户移除掉刚刚添加的联系人:
为了下面的测试,我删除了这两张表,当然,不删也可以看出来效果
1.3、使用级联操作——cascade属性
级联操作:cascade —— 简化操作
save-update:级联保存更新 delete:级联删除 all:save-update + delete
级联保存更新
在Customer.hbm.xml中配置 :
测试级联保存:
数据库表中:
除了通过保存客户来级联保存联系人之外,也可以通过保存联系人来级联保存客户,这时就需要级联操作cascade属性配置在LinkMan.hbm.xml里了
<many-to-one name="customer" column="lm_cust_id" class="Customer" cascade="save-update" ></many-to-one>
数据库表:
级联删除
在Customer.hbm.xml中配置 :
测试级联删除:
数据库表中不仅客户被删除了,联系人也被删除了:
在开发中,级联操作是为了简化操作,也可以不用,若一定要使用级联操作的话,就用save-update,不建议使用delete!
1.4、关系维护——inverse属性
上述不使用级联操作,保存客户和联系人,控制台打印的SQL语句:
在保存时,默认情况下,两方都会维护外键关系,关系维护两次,冗余了。上述多余的维护关系语句(最后两句),显然是客户这一方。
inverse属性:inverse属性优化了性能,配置该类是否来进行关系维护,true:不维护关系,false——维护关系,默认为false。
inverse属性的原则——无论怎么放弃,总有一方必须来维护关系。在一对多的关系中,一的一方放弃。也只能一的一方放弃,多的一方不能放弃!
下面我们来配置让Customer(一的一方)放弃维护外键:
测试代码里:
控制台打印(没有客户维护外键语句):
二、多对多
2.1、关系的表达
表中的表达(使用中间表,至少两列,都是外键列,分别引用两张表的主键):
员工表: 角色表:
两张表的主键组合成一张中间表把两张表关联起来:
实体中的表达(两方都使用集合来表达拥有多个对方):
员工类:
角色类:
ORM元数据 配置文件中的配置多对多关系:
2.2、多对多中关系的维护——inverse属性
inverse属性与一对多中的一样,true表示放弃维护外键关系,false表示维护关系。
在开发中遇到多对多关系,必须选择一方放弃维护关系,谁来维护关系谁放弃维护要看业务方向。
上例中,录入员工时,需要为员工指定所属角色,那么业务方向就是由员工维护角色,角色不需要维护与员工的关系,所以,角色放弃维护关系!所以需要在Role.hbm.xml里面配置inverse属性:
主配置文件里添加:
编写测试类:
public class TestMany2Many {
@Test
public void fun1() { //添加员工、添加角色,并维护两者之间的关系
//1.获得session
Session session = HibernateUtils.openSession();
//2.开启事务
Transaction tx = session.beginTransaction();
//3.操作
//创建Employee和Role对象
Employee e1 = new Employee();
e1.setEmployee_name("张山");
Employee e2 = new Employee();
e2.setEmployee_name("李市");
Role r1 = new Role();
r1.setRole_name("老板");
Role r2 = new Role();
r2.setRole_name("程序员");
Role r3 = new Role();
r3.setRole_name("UI");
//用户表达关系,关系只用一方维护
e1.getRoles().add(r1);
e1.getRoles().add(r2);
e2.getRoles().add(r2);
e2.getRoles().add(r3);
session.save(e1);
session.save(e2);
session.save(r1);
session.save(r2);
session.save(r3);
//4.提交事务
tx.commit();
//5.关闭资源
session.close();
}
}
员工表: 角色表: 中间表:
测试:给某个员工解除某个角色
只有中间表少了第一行 1 1,其他两个表没有变化!
2.3、多对多中的级联操作
例:给上例中id为1的员工添加一个新角色:
数据库中,role表和中间表都多了一条记录:
给Employee添加级联操作cascade属性
这样在测试的时候,r1就不用我们手动去save了,即上面测试代码截图的最后一行可以注释掉
cascade属性简化了代码的属性,该属性也是可选属性,在开发中,一般也就会用到save-update值,用delete太危险了,在多对多中,不建议用!