创建JavaBean
一方: 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; //存储联系人的集合,该属性并不会出现在数据库中 private Set<LinkMan> linkmans = new HashSet<LinkMan>(); //--加上所有的get/set方法 无参构造函数
多方: 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; //外键对象 private Customer customer; //--加上所有的get/set方法 无参构造函数
配置多方的映射文件
<!-- 先配置多方 name:当前JavaBean中的属性 class:属性的全路径 column:外键的字段 --> <many-to-one name="customer" class="com.hibernateday3.domain.Customer" column="lkm_cust_id"/>
配置一方的映射文件
<!-- 后配置一方 set->name:表示集合的名称 --> <set name="linkmans"> <!-- 外键的字段与路径 --> <key column="lkm_cust_id"/> <one-to-many class="com.hibernateday3.domain.LinkMan"/> </set>
将映射文件配置到核心文件
<!-- 映射配置文件 --> <mapping resource="com/hibernateday3/domain/Customer.hbm.xml"/> <mapping resource="com/hibernateday3/domain/Linkman.hbm.xml"/>
测试:
1.测试双向关联方式
@Test public void m01(){ Session session = HibernateUtils.openSession(); Transaction tr = session.beginTransaction(); Customer c1 = new Customer(); c1.setCust_name("客户1"); LinkMan l1 = new LinkMan(); l1.setLkm_name("联系人1"); LinkMan l2 = new LinkMan(); l2.setLkm_name("联系人2"); c1.getLinkmans().add(l1); c1.getLinkmans().add(l2); //------保存数据 //双向关联保存
session.save(c1); session.save(l1); session.save(l2); //------提交事务释放资源 tr.commit(); session.close(); }
2.单向的关联,如果不配置级联保存,程序出现异常
//------保存数据 //双向关联保存 session.save(c1); //------提交事务释放资源
警告:org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.hibernateday3.domain.LinkMan
如果想完成只保存一方的数据,并且把相关联的数据都保存到数据库中,那么需要配置级联!!
级联保存是具有方向性的
3.测试级联保存:保存客户,级联联系人
---在一方映射文件加入cascade属性
<set name="linkmans" cascade="save-update"> <!-- 外键的字段与路径 --> <key column="lkm_cust_id"/> <one-to-many class="com.hibernateday3.domain.LinkMan" /> </set>
//------保存数据 //双向关联保存 session.save(c1); //------提交事务释放资源
运行成功。。
4.测试级联保存:保存联系人,级联客户
---在多方映射文件加入cascade属性
<many-to-one name="customer" class="com.hibernateday3.domain.Customer" column="lkm_cust_id" cascade="save-update"/>
//------保存数据 //双向关联保存 session.save(l1); session.save(l2); //------提交事务释放资源
运行成功。。
5.测试级联保存
//------保存数据 //双向关联保存
l1.setCustomer(c1); c1.getLinkmans().add(l2); session.save(l1);
//------提交事务释放资源
运行成功。。
6.测试:删除一方,一方下有2个联系人
public void m06(){ Session session = HibernateUtils.openSession(); Transaction tr = session.beginTransaction(); //------保存数据 //双向关联保存 Customer c1 = session.get(Customer.class, 1L); session.delete(c1); //------提交事务释放资源 tr.commit(); session.close(); }
能够成功 并不会因为一方是多方的外键二不能删除
Hibernate先让多方删除外键 再删除一方
7.测试级联删除,删除客户,级联删除客户下的联系人
在一方映射文件下添加属性
<set name="linkmans" cascade="save-update,delete"> <!-- 外键的字段与路径 --> <key column="lkm_cust_id"/> <one-to-many class="com.hibernateday3.domain.LinkMan" /> </set>
//------保存数据 //双向关联保存 Customer c1 = session.get(Customer.class, 1L); session.delete(c1); //------提交事务释放资源
删除c1 c1对应的l1,l2都被删除了
8.删除联系人,级联删除客户
在多方映射文件下添加属性
<many-to-one name="customer" class="com.hibernateday3.domain.Customer" column="lkm_cust_id" cascade="save-update,delete"/>
//------保存数据 //双向关联保存 LinkMan l1 = session.get(LinkMan.class,1L ); session.delete(l1); //------提交事务释放资源
如果一方下的cascade的delete属性依然存在,所有的数据都被删除
9.解除关系,从一方集合中移除多方数据
// 先获取到客户 Customer c1 = session.get(Customer.class, 1L); LinkMan l1 = session.get(LinkMan.class, 1L); // 解除 c1.getLinkmans().remove(l1);
多方的外键被解除设为null
如果cascade属性为delete-orphan 解除关系 ,多方记录将被删除(孤儿关系)
1. 取值如下 * none -- 不使用级联 * save-update -- 级联保存或更新 * delete -- 级联删除 * delete-orphan -- 孤儿删除.(注意:只能应用在一对多关系) * all -- 除了delete-orphan的所有情况.(包含save-update delete) * all-delete-orphan -- 包含了delete-orphan的所有情况.(包含save-update delete delete-orphan) 2. 孤儿删除(孤子删除),只有在一对多的环境下才有孤儿删除 * 在一对多的关系中,可以将一的一方认为是父方.将多的一方认为是子方.孤儿删除:在解除了父子关系的时候.将子方记录就直接删除。 * <many-to-one cascade="delete-orphan" />
放弃外键维护
1. 先测试双方都维护外键的时候,会产生多余的SQL语句。
* 想修改客户和联系人的关系,进行双向关联,双方都会维护外键,会产生多余的SQL语句。
/** * 放弃外键的维护 * 需求:让c1联系人l1属于c2 */ @Test public void m11(){ Session session = HibernateUtils.getCurrentSession(); Transaction tr = session.beginTransaction(); Customer c2 = session.get(Customer.class,2L); LinkMan l1 = session.get(LinkMan.class, 1L); c2.getLinkmans().add(l1); l1.setCustomer(c2); tr.commit(); }
* 产生的原因:session的一级缓存中的快照机制,会让双方都更新数据库,产生了多余的SQL语句。
2. 如果不想产生多余的SQL语句,那么需要一方来放弃外键的维护!
* 在<set>标签上配置一个inverse=”true”.true:放弃.false:不放弃.默认值是false
* <inverse="true">
<set name="linkmans" inverse="true"> <!-- 外键的字段与路径 --> <key column="lkm_cust_id"/> <one-to-many class="com.hibernateday3.domain.LinkMan" /> </set>
/** * 放弃外键的维护 * 需求:让c1联系人l1属于c2 */ @Test public void m11(){ Session session = HibernateUtils.getCurrentSession(); Transaction tr = session.beginTransaction(); Customer c2 = session.get(Customer.class,2L); LinkMan l1 = session.get(LinkMan.class, 1L); c2.getLinkmans().add(l1); l1.setCustomer(c2); tr.commit(); }
cascade和inverse的区别
1. cascade用来级联操作(保存、修改和删除)---配置在多方
2. inverse用来维护外键的 ----配置在一方