Hibernate中的级联策略和object references an unsaved transient instance - save the transient instance before

转载博客

博客来源: https://blog.csdn.net/VipMao/article/details/51378237


本片博文整理关于Hibernate中级联策略cascade和它导致的异常:

Exception in thread "main" org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.mao.Address

关于这个异常应该是Hibernate初学者经常遇到的,导致该异常的原因就是:你的对象引用了一个未保存的瞬态实例,换句话讲就是:因为主表的记录不曾插入,所以参照该记录的从表记录也就无法插入

举个例子 咱们有两个表

表一 person_inf(从表)

结构:person_id(主键)    person_name    address_id(外键)

表二 address_inf(主表)

结构:address_id(主键)   address_detail

如果从表的记录(address_detail)都没持久化,还处于瞬态,你在主表person_inf中就引用了从表信息,从表记录自然也不会插入,而且就会报该错。

下面是具体一个OneToOne实例

1:Person.java

import java.util.*;
 
import javax.persistence.*;
@Entity
@Table(name="person_inf")
public class Person
{
    // 标识属性
    @Id @Column(name="person_id")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer id;
    private String name;
    private int age;
    // 定义该Person实体关联的Address实体
    @OneToOne(targetEntity=Address.class,cascade=CascadeType.ALL)
    // 映射名为address_id的外键列,参照关联实体对应表的addres_id主键列
    @JoinColumn(name="address_id"
        , referencedColumnName="address_id" , unique=true)
    private Address address;
    //省略所有set get方法
}


注意上面代码中@OneToOne(cascade=CascadeType.All)这条,它就代表着将所有的持久化操作都级联到关联体中,意思就是系统会自动级联插入主表记录(address_inf表),再插入从表记录(person_inf),而不用担心该关联体是否已经保存为持久化状态
2:关联体Address.java

import java.util.ArrayList;
import java.util.List;
 
import javax.persistence.*;
 
@Entity
@Table(name="address_inf")
public class Address {
    
    @Id
    @Column(name="address_id")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int addressId;
    private  String addressDetail;
    public Address() {
        
    }
    public Address(String addressDetail) {
        this.addressDetail = addressDetail;
    }
    //省略set get方法

3:主程序PersonManager.java 这里只贴出部分添加数据代码
                Person person=new Person();
        person.setName("VipMao");
        person.setAge(24);
        //创建一个瞬态 的Adress对象
        Address address=new Address("山东曲阜");
        //设置Person和Adress之间的关联关系
        person.setAddress(address);
        //persist方法并不是立刻将标识符填入到实例化中 这是与save方法的区别
        session.persist(person);
        tx.commit();


代码可以看出,我们并没有将address对象保存为持久化就直接通过session.persist(person)映射到person_inf数据表中了,这是因为我们前面设置了@OneToOne(cascade=CascadeType.All),这就说明当程序执行到session.persist(person)的时候,会先执行插入address_inf表记录,再执行插入person_inf表记录,从打印的SQL语句也可以看出:


这里也建议大家在学习Hibernate时把show_sql 设置成true 这样你就会清楚的看见程序先执行了什么后干了什么。

这就是级联操作。如果将@OneToOne(cascade=CascadeType.All)去掉,就没法通过级联插入address表数据,就会报错:Exception in thread "main" org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.mao.Address

另一种解决方式就是将程序改成如下


              Person p = new Person();
        // 设置Person的name为crazyit字符串
        p.setName("VipMao");
        p.setAge(24);
        // 创建一个瞬态的Address对象
        Address a = new Address("山东曲阜");
        // 通过Person对象建立它自己与Address实体的关联关系
        p.setAddress(a);
        // 先持久化Address对象(对应为插入主表记录)
        session.persist(a);
        // 再持久化Person对象(对应为插入从表记录)
        session.save(p);
        tx.commit();


在将数据插入之前就将他持久化。
4:运行结果
可以发现无论通过cascade=CascadeType.All级联方式还是通过手动先保存持久化都可以实现

总结:
Exception in thread "main" org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.mao.Address

导致原因:

你的对象引用了一个未保存的瞬态实例,换句话讲就是:因为主表的记录不曾插入,所以参照该记录的从表记录也就无法插入

解决方案:

1:通过cascade=CascadeType.All将Hibernate的所有持久化操作都级联到关联实体

2:将你需要插入的数据先持久化,再映射到相应的表中。


--------------------- 
作者:VipMao 
来源:CSDN 
原文:https://blog.csdn.net/vipmao/article/details/51378237

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值