JavaWEB中一对多的设计及建表原则
导入SQL的建表语句
创建数据库:create database hibernate_day03
编写客户和联系人的JavaBean程序(注意一对多的编写规则)
客户的JavaBean如下
public class Customer {
private Long cust_id;
private String cust_name;
private Long cust_user_id;
private Long cust_create_id;
private String cust_source;
private String cust_industry;
private String cust_level;
private String cust_linkman;
private String cust_phone;
private String cust_mobile;
//hibernate框架默认的集合是set集合,集合必须要自己手动的初始化
private Set<Linkman> linkmans = new HashSet<Linkman>();
}
联系人的JavaBean如下:
public class Linkman {
private Long lkm_id;
private String lkm_name;
private String lkm_gender;
private String lkm_phone;
private String lkm_mobile;
private String lkm_email;
private String lkm_qq;
private String lkm_position;
private String lkm_memo;
//编写一个对象,不要自己new,
private Customer customer;
}
测试类
public class Demo1 {
//双向关联的方式保存数据
@Test
public void run1(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
//保存客户和联系人的数据
Customer c1=new Customer();
c1.setCust_name("美美");
//创建两个联系人
Linkman l1 = new Linkman();
l1.setLkm_name("熊大");
Linkman l2 = new Linkman();
l2.setLkm_name("熊二");
//演示双向关联
c1.getLinkmans().add(l1);
c1.getLinkmans().add(l2);
l1.setCustomer(c1);
l2.setCustomer(c1);
//保存数据
session.save(c1);
session.save(l1);
session.save(l2);
tr.commit();
}
}
级联保存
测试:如果代码只插入其中的一方的数据
- 如果只保存其中的一方的数据,那么程序会抛出异常
- 如果想完成只保存一方的数据,并且把相关联的数据都保存到数据库中,那么需要配置级联。
- 级联保存是有方向性的。
级联保存的效果
- 级联保存:保存一方同时可以把关联的对象也保存到数据库中
- 使用 cascade = “save-update”
<!--在hibernate.hbm.xml中配置-->
<!--cascade是级联属性-->
<set name="linkmans" cascade="save-update">
<!--需要的子标签-->
<!--column外键的字段-->
<key column="lkm_cust_id"></key>
<one-to-many class="zst.cm.domain.Linkman"/>
</set>
级联问题
<!--Customer.hbm.xml-->
<set name = "linkmans" cascode="save-update">
<key column = "lkm_cust_id"></key>
<one-to-many class = "zst.cm.domain.Linkman"/>
</set>
<!--Hibernate.hbm.xml-->
<many-to-one name="customer" class="zst.cm,domain.Customer" column="lkm_cust_id" />
测试代码
public void run3(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
//保存客户和联系人的数据
Customer c1=new Customer();
c1.setCust_name("美美");
//创建两个联系人
Linkman l1 = new Linkman();
l1.setLkm_name("熊大");
//l1.setCustomer(c1);
Linkman l2 = new Linkman();
l2.setLkm_name("熊二");
//l2.setCustomer(c1);
//单向关联
c1.getLinkmans().add(l1);
c1.getLinkmans().add(l2);
//保存数据
session.save(c1);
tr.commit();
}
执行后会抛出异常
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column ‘lkm_cust_id’ cannot be null
修改配置后
<!--Customer.hbm.xml-->
<set name="linkmans" inverse="true">
<!--需要的子标签-->
<!--column外键的字段-->
<key column="lkm_cust_id"></key>
<one-to-many class="zst.cm.domain.Linkman"/>
</set>
<!--Linkman.hbm.xml-->
<many-to-one name="customer" class="zst.cm.domain.Customer" column="lkm_cust_id" cascade="save-update"/>
测试代码
public void run3(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
//保存客户和联系人的数据
Customer c1=new Customer();
c1.setCust_name("美美");
//创建两个联系人
Linkman l1 = new Linkman();
l1.setLkm_name("熊大");
//l1.setCustomer(c1);
Linkman l2 = new Linkman();
l2.setLkm_name("熊二");
//l2.setCustomer(c1);
//单向关联
c1.getLinkmans().add(l1);
c1.getLinkmans().add(l2);
//保存数据
session.save(c1);
tr.commit();
}
可以执行,但是真正保存到数据库中的内容是Customer的,而Linkman的数据并没有保存。
修改测试类
<!--Customer.hbm.xml-->
<set name="linkmans" inverse="true">
<!--需要的子标签-->
<!--column外键的字段-->
<key column="lkm_cust_id"></key>
<one-to-many class="zst.cm.domain.Linkman"/>
</set>
<!--Linkman.hbm.xml-->
<many-to-one name="customer" class="zst.cm.domain.Customer" column="lkm_cust_id" cascade="save-update"/>
public void run3(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
//保存客户和联系人的数据
Customer c1=new Customer();
c1.setCust_name("关关");
//创建两个联系人
Linkman l1 = new Linkman();
l1.setLkm_name("大黑");
Linkman l2 = new Linkman();
l2.setLkm_name("小黑");
l1.setCustomer(c1);
l2.setCustomer(c1);
//保存数据
session.save(l1);
session.save(l2);
tr.commit();
}
此时,关联成功,客户和联系人的 数据都保存。
级联删除
- 在数据库中删除含有外键的客户的某个字段,SQL语句会报出错误。
- delete from customers where cid =1;
- 如果使用Hibernate框架直接删除客户的功能,测试发现是可以删除的
- 上述的删除是普通的删除,那么也可以使用级联删除,注意:级联删除也是有方向性的。
<many-to-one cascade="delete"/>
public void run4(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
//查询1号客户
//Linkman man= session.get(Linkman.class, 3L);
Customer customer = session.get(Customer.class,20L);
session.delete(customer);
tr.commit();
}
<!--customer.hbm.xml-->
<set name="linkmans" inverse="true" cascade="delete">
<!--需要的子标签-->
<!--column外键的字段-->
<key column="lkm_cust_id"></key>
<one-to-many class="zst.cm.domain.Linkman" />
</set>
<!--Linkman.hbm.xml-->
<many-to-one name="customer" class="zst.cm.domain.Customer" column="lkm_cust_id" cascade="save-update" />
级联的取值(cascade的取值)和孤儿删除
- none ----不使用级联
- save-update ----级联保存或更新
- delete----级联删除
- delete-orphan----孤儿删除(注意:只能应用在一对多关系)
- all----除了delete-orphan的所有情况(包含save-update delete)
- all-delete-orphan----包含了delete-orphan 的所用情况(包含save-update delete delete-orphan)
孤儿删除(孤子删除),只用在一对多的环境下才有孤儿删除
- 在一对多的关系中,可以将一的一方认为是父方,将多的一方认为是子方,孤儿删除:在解除看父子关系的时候,将子方记录就直接删除
<many-to-one cascade="delete-orphan">
解除关系
public void run6(){
Session session = HibernateUtils.getCurrentSession();
Transaction tr = session.beginTransaction();
//先获取到客户
Customer c1 = session.get(Customer.class ,25L);
Linkman l1 = session.get(Linkman.class,6L);
//解除
Set<Linkman> linkmans = c1.getLinkmans();
linkmans.remove(l1);
tr.commit();
}
让某一放放弃外键的维护,为多对多做准备
- 先测试双方都维护外键的时候,会产生多余的SQL语句
- 想修改客户和联系人的关系,进行双向关联,双方都会维护外键,会产生多余的SQL语句
- 产生的原因:session的一级缓存中的快照机制,会让双方都更新数据库,产生了多余的SQL语句
- 如果不想产生多余的SQL语句,那么需要一方来放弃外键的维护
- 在
<set>
标签上配置一个inverse=“true”。true:放弃,false:不放弃,默认是false。 <inverse = "true">
- 在
cascade 和inverse的区别
- cascade 用来级联操作(保存,修改和删除)
- inverse用来维护外键的