【问题标题】:spring - hibernate - org.hibernate.TransientObjectException: object references an unsaved transient instancespring - hibernate - org.hibernate.TransientObjectException:对象引用了一个未保存的瞬态实例
【发布时间】:2015-05-29 13:01:14
【问题描述】:

我有一个主表 Blends 和一个表 cards,它们之间存在一对一的关系。在添加card 时,我会在下拉菜单中选择一个blend,卡片有一列blend_id

在添加card 时,我想将card 保存在表中,其中blend_id 值等于从下拉列表中选择的混合ID。

我正在使用 spring hibernate,这是我提交卡片表单时遇到的异常:

org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.terp.entity.BlendEntity
    org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:243)
    org.hibernate.type.EntityType.getIdentifier(EntityType.java:456)
    org.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:265)
    org.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:275)
    org.hibernate.type.TypeHelper.findDirty(TypeHelper.java:295)
    org.hibernate.persister.entity.AbstractEntityPersister.findDirty(AbstractEntityPersister.java:3403)
    org.hibernate.event.def.DefaultFlushEntityEventListener.dirtyCheck(DefaultFlushEntityEventListener.java:520)
    org.hibernate.event.def.DefaultFlushEntityEventListener.isUpdateNecessary(DefaultFlushEntityEventListener.java:230)
    org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:154)
    org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:219)
    org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:99)
    org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
    org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
    org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383)
    org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133)
    org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:656)
    org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
    org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
    org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
    org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
    org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    $Proxy26.addCard(Unknown Source)
    com.terp.controller.CardController.addCard(CardController.java:122)
    com.terp.controller.CardController$$FastClassByCGLIB$$43e17ccc.invoke(<generated>)
    net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
    org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:617)
    com.terp.controller.CardController$$EnhancerByCGLIB$$4627162d_2.addCard(<generated>)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    java.lang.reflect.Method.invoke(Unknown Source)
    org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:427)
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:415)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:788)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:717)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:727)

异常似乎是它想要保存选定的blend 对象,但我不想保存混合,因为这是我的主表,我只想在我的cards 表中引用它。

这些是 java 类:

BlendEntity.java

package com.terp.entity;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.hibernate.annotations.Proxy;


@Entity
@Table(name="blends")
@Proxy(lazy=false)
public class BlendEntity {

    @Id
    @Column(name="id")
    @GeneratedValue
    private Integer id;
    private String name;
    private String code;

    @Temporal(TemporalType.TIMESTAMP)
    private Date created_at;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public Date getCreated_at() {
        return created_at;
    }

    public void setCreated_at(Date created_at) {
        this.created_at = created_at;
    }

}

CardEntity.java

package com.terp.entity;

import java.util.Date;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.hibernate.annotations.Proxy;
import org.hibernate.annotations.Sort;
import org.hibernate.annotations.SortType;
import org.springframework.transaction.annotation.Transactional;

@Entity
@Table(name="cards")
@Proxy(lazy=false)
public class CardEntity {

    @Id
    @Column(name="id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @OneToOne
    @JoinColumn(name="division_id", referencedColumnName="id")  
    private DivisionEntity division;

    private Integer card_series;
    private String card_number;
    private Integer shade;

    @OneToOne
    @JoinColumn(name="unit_of_qty_id", referencedColumnName="id")  
    private UnitEntity unit;

    private Date card_date;
    private Date issue_date;


    private String cutomer_ref;
    private String hl_number;
    private String design_number;

    @OneToOne
    @JoinColumn(name="designWay_id", referencedColumnName="id")  
    private DesignWayEntity designWay;

/*  @OneToOne
    @JoinColumn(name="dyeingType_id", referencedColumnName="id")  
    private DyeingTypeEntity dyeingType;*/

    private String dyeingType;

    private Integer total_days;
    private Date order_date;
    private Date grey_date;
    private Date target_date;

    @OneToOne
    @JoinColumn(name="qualityBase_id", referencedColumnName="id")  
    private QualityBaseEntity qualityBase;

    @OneToOne
    @JoinColumn(name="openFor_id", referencedColumnName="id")  
    private OpenForEntity openFor;

    @OneToOne
    @JoinColumn(name="width_id", referencedColumnName="id")  
    private WidthEntity width;

    @OneToOne
    @JoinColumn(name="finishType_id", referencedColumnName="id")  
    private FinishTypeEntity finishType;

    @OneToOne
    @JoinColumn(name="blend_id", referencedColumnName="id")  
    private BlendEntity blend;

    private Float weight;

    @OneToOne
    @JoinColumn(name="merchant_id", referencedColumnName="id")  
    private MerchantEntity merchant;

    @OneToOne
    @JoinColumn(name="priority_id", referencedColumnName="id")  
    private PriorityEntity priority;

    @OneToMany(fetch = FetchType.EAGER, mappedBy="card", cascade = CascadeType.ALL)
    @Sort(type=SortType.NATURAL)
    private SortedSet<ShadeEntity> shades = new TreeSet<ShadeEntity>();

    private Float total_qty;

    @Transactional
    public SortedSet<ShadeEntity> getShades() {
        return shades;
    }

    @Transactional
    public void setShades(SortedSet<ShadeEntity> shades) {
        this.shades = shades;
    }

    @Temporal(TemporalType.TIMESTAMP)
    private Date created_at;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public DivisionEntity getDivision() {
        return division;
    }

    public void setDivision(DivisionEntity division) {
        this.division = division;
    }


    public Integer getCard_series() {
        return card_series;
    }

    public void setCard_series(Integer card_series) {
        this.card_series = card_series;
    }

    public String getCard_number() {
        return card_number;
    }

    public void setCard_number(String card_number) {
        this.card_number = card_number;
    }

    public Integer getShade() {
        return shade;
    }

    public void setShade(Integer shade) {
        this.shade = shade;
    }



    public UnitEntity getUnit() {
        return unit;
    }

    public void setUnit(UnitEntity unit) {
        this.unit = unit;
    }

    public Date getCard_date() {
        return card_date;
    }

    public void setCard_date(Date card_date) {
        this.card_date = card_date;
    }

    public String getCutomer_ref() {
        return cutomer_ref;
    }

    public void setCutomer_ref(String cutomer_ref) {
        this.cutomer_ref = cutomer_ref;
    }

    public String getHl_number() {
        return hl_number;
    }

    public void setHl_number(String hl_number) {
        this.hl_number = hl_number;
    }

    public String getDesign_number() {
        return design_number;
    }

    public void setDesign_number(String design_number) {
        this.design_number = design_number;
    }

    public DesignWayEntity getDesignWay() {
        return designWay;
    }

    public void setDesignWay(DesignWayEntity designWay) {
        this.designWay = designWay;
    }




    public String getDyeingType() {
        return dyeingType;
    }

    public void setDyeingType(String dyeingType) {
        this.dyeingType = dyeingType;
    }

    public Date getOrder_date() {
        return order_date;
    }

    public void setOrder_date(Date order_date) {
        this.order_date = order_date;
    }

    public Date getGrey_date() {
        return grey_date;
    }

    public void setGrey_date(Date grey_date) {
        this.grey_date = grey_date;
    }

    public Date getTarget_date() {
        return target_date;
    }

    public void setTarget_date(Date target_date) {
        this.target_date = target_date;
    }

    public QualityBaseEntity getQualityBase() {
        return qualityBase;
    }

    public void setQualityBase(QualityBaseEntity qualityBase) {
        this.qualityBase = qualityBase;
    }

    public OpenForEntity getOpenFor() {
        return openFor;
    }

    public void setOpenFor(OpenForEntity openFor) {
        this.openFor = openFor;
    }

    public WidthEntity getWidth() {
        return width;
    }

    public void setWidth(WidthEntity width) {
        this.width = width;
    }

    public FinishTypeEntity getFinishType() {
        return finishType;
    }

    public void setFinishType(FinishTypeEntity finishType) {
        this.finishType = finishType;
    }

    public BlendEntity getBlend() {
        return blend;
    }

    public void setBlend(BlendEntity blend) {
        this.blend = blend;
    }


    public Float getWeight() {
        return weight;
    }

    public void setWeight(Float weight) {
        this.weight = weight;
    }



    public Integer getTotal_days() {
        return total_days;
    }

    public void setTotal_days(Integer total_days) {
        this.total_days = total_days;
    }

    public MerchantEntity getMerchant() {
        return merchant;
    }

    public void setMerchant(MerchantEntity merchant) {
        this.merchant = merchant;
    }

    public PriorityEntity getPriority() {
        return priority;
    }

    public void setPriority(PriorityEntity priority) {
        this.priority = priority;
    }

    public Float getTotal_qty() {
        return total_qty;
    }

    public void setTotal_qty(Float total_qty) {
        this.total_qty = total_qty;
    }

    public Date getCreated_at() {
        return created_at;
    }

    public void setCreated_at(Date created_at) {
        this.created_at = created_at;
    }

    public Date getIssue_date() {
        return issue_date;
    }

    public void setIssue_date(Date issue_date) {
        this.issue_date = issue_date;
    }

}

卡表中的约束:

CONSTRAINT `fk_40` FOREIGN KEY (`blend_id`) REFERENCES `blends` (`id`),

在卡片中,如果我添加级联,它会在混合中添加一个新行:

@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name="blend_id", referencedColumnName="id")  
private BlendEntity blend;

【问题讨论】:

  • 似乎分配给卡的 BlendEntity 没有有效的 id,因此休眠将其保存为 blend 中的新行(在您的测试中使用级联类型)。检查您如何将混合设置为卡片实体。
  • BlendEntity 不是必填字段,因此如果我没有从下拉列表中选择任何内容进行混合,则卡片对象中的 BlendEntity 包含所有空字段,包括 id 并且没有Blends 表中的行与空id,所以这可能是问题所在。关于如何处理这种情况的任何建议?
  • 如果这是您的情况,我会在 @JoinColumn 注释中添加一个 nullable=true。
  • 添加nullable=true无效。
  • 还尝试为 Blend 实体包含 @OneToOne(optional=true)。

标签: java mysql database spring hibernate


【解决方案1】:

您遇到了持久性上下文问题。见What is Persistence Context未保存的瞬态实例不在持久性上下文中。

TransientObjectExceptionjavadoc:

当用户将瞬态实例传递给 Session 方法时抛出 需要一个持久实例。

两种可能的解决方案:

  1. 如果关系真的是@OneToOne,考虑重构 通过在 Card 表中定义 Blend 列的模式。 @OneToOne#optional@JoinColumn#nullable 默认为 true。但是,@OneToOne 关系的意图是目标实体(行)不被多个引用实体共享。允许一个空的 Blend 外键意味着许多 Blend 可以被 no Card 共享。

  2. 不要将@Transactional 放在实体方法上。 Best practice 是定义@Transactional 在服务(可能还有道)层。此外,检查您的 Service/Dao/Repository 配置和代码,确保两个实体由相同的持久性上下文管理,例如,通过 find()ing 混合 ID 引用的先前持久化的 Blend 实体,然后分配它到卡。为了更好地理解实体、实体管理器、持久性上下文和事务如何交互,我从Pro JPA 2, 2nd Edition by Mike Keith and Merrick Schincariol 中摘录了以下要点。我与作者无关。

    • Dao/Repository 层必须使用 JPA EntityManager 进行持久化。
    • 实体管理器管理持久性上下文,即一组持久性实体实例。
    • 持久实体同时具有内存中对象标识和数据库标识,该标识由由(主)键唯一标识的数据库表示(行)定义。

    • 瞬态实体没有数据库表示,无论应用是否设置了实体的“唯一身份”字段。

    • Java 内存模型不是事务性的。分离(或瞬态)对象实体的状态不会自动与 DB 同步。

    • 如果持久性上下文参与事务,则托管实体的内存状态将同步到数据库。

    • 分离(临时)实体不能与任何需要托管实例的实体管理器操作一起使用。

    • 实体管理器类型决定了持久化上下文的生命周期。

    • JPA 定义了 3 种实体管理器,每种都需要不同的持久化上下文管理:

    • 容器管理的EntityManager:

      • 事务范围:

        • @PersistenceContext // 默认为 type=PersistenceContextType.TRANSACTION
        • 使用事务作为跟踪/传播持久性上下文的一种方式。
        • 是无国籍的;它可以安全地存储在任何 Java EE 组件中。
        • 容器代理持久性提供者的实体管理器以管理其生命周期。
        • 当应用调用实体管理器方法时,容器的实体管理器代理会检查现有的持久性上下文 与当前事务相关联。如果代理找到一个,实体管理器将使用现有的持久性上下文。如果 如果找不到,代理会创建一个新的持久性上下文并将其与事务相关联。
        • 当事务结束时,持久性上下文“消失”,由上下文管理的所有实体都将分离。
        • 同一事务中的所有容器管理实体管理器必须共享相同的传播持久性上下文。
      • 扩展范围:

        • @PersistenceContext(type=PersistenceContextType.EXTENDED)
        • 一个单一的持久性上下文,其范围为有状态会话 bean 的生命周期,可能跨越多个事务。
        • 防止实体在单个事务结束时分离。
    • 应用程序管理的 EntityManager:

      • App 管理实体管理器的生命周期;必须致电close()
      • 创建自己的私有持久性上下文,持续到实体管理器关闭。
      • 如果在事务处于活动状态时创建,则自动与事务同步;否则不。
      • 在 Java SE 中可用:EntityManagerFactory.createEntityManager()
      • 在 Java EE 中可用:@PersistenceUnit
      • 支持无容器的单元测试。
      • 应用程序必须明确关闭EntityManagerFactory 实例。 Java EE 容器会自动关闭工厂。
      • 唯一可以为资源本地事务配置的实体管理器类型
      • 可以使用 JTA 或资源本地 XA

【讨论】:

    猜你喜欢
    • 2014-06-29
    • 2016-02-24
    • 2013-08-19
    • 2012-02-27
    • 1970-01-01
    • 1970-01-01
    • 2014-10-26
    相关资源
    最近更新 更多