11.Hibernate一对多关系

创建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);

//------提交事务释放资源

如果一方下的cascadedelete属性依然存在,所有的数据都被删除

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用来维护外键的  ----配置在一方
posted @ 2017-04-26 19:02  NEWHOM  阅读(215)  评论(0编辑  收藏  举报