object引用未保存的瞬态实例 - 在刷新之前保存瞬态实例

时间:2010-02-20 16:17:46

标签: java hibernate orm

使用Hibernate保存对象时收到以下错误

object references an unsaved transient instance - save the transient instance before flushing

31 个答案:

答案 0 :(得分:669)

您应该在集合映射中包含cascade="all"(如果使用xml)或cascade=CascadeType.ALL(如果使用注释)。

这是因为您的实体中有一个集合,并且该集合包含一个或多个数据库中不存在的项目。通过指定上述选项,您可以告诉hibernate在保存父项时将它们保存到数据库中。

答案 1 :(得分:193)

我相信这可能只是重复回答,但为了澄清,我在@OneToOne映射和@OneToMany上得到了这个。在这两种情况下,我添加到Child的{​​{1}}对象尚未保存在数据库中。因此,当我将Parent添加到Child,然后保存Parent时,Hibernate会在保存父级时抛出Parent消息。

"object references an unsaved transient instance - save the transient instance before flushing"的{​​{1}}引用上添加cascade = {CascadeType.ALL}解决了两种情况下的问题。这保存了Parent'sChild

对于任何重复的答案感到抱歉,只是想为人们进一步澄清。

Child

答案 2 :(得分:54)

当Hibernate认为需要保存与您正在保存的对象关联的对象时保存对象时会发生这种情况。

我遇到了这个问题,并且不想保存对引用对象的更改,所以我希望级联类型为NONE。

诀窍是确保引用对象中的ID和VERSION设置为使Hibernate不认为引用的对象是需要保存的新对象。这对我有用。

查看要保存的类中的所有关系,以计算关联对象(以及关联对象的关联对象),并确保在对象树的所有对象中设置ID和VERSION。

答案 3 :(得分:25)

或者,如果您想使用最小的“权力”(例如,如果您不想要级联删除)来实现您想要的目标,请使用

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;

...

@Cascade({CascadeType.SAVE_UPDATE})
private Set<Child> children;

答案 4 :(得分:20)

在我的情况下,这是由于双向关系的CascadeType侧没有@ManyToOne造成的。更确切地说,我在CascadeType.ALL方面@OneToMany并且在@ManyToOne上没有CascadeType.ALL。将@ManyToOne添加到@OneToMany(cascade = CascadeType.ALL, mappedBy="globalConfig", orphanRemoval = true) private Set<GlobalConfigScope>gcScopeSet; 可以解决问题。 一对多方:

@ManyToOne
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;

多对一方(导致问题)

CascadeType.PERSIST

多对一(通过添加@ManyToOne(cascade = CascadeType.PERSIST) @JoinColumn(name="global_config_id") private GlobalConfig globalConfig; 修复)

     # -*- coding: utf-8 -*-

    from scrapy.spiders import Spider
    from scrapy.selector import Selector

    from scraper_app.items import GenomeCanadaGrants

    class GCSpider(Spider):
        """Spider for Genome Canada Awards page."""
        name = 'GCSpider'
        allowed_domains = ["http://genomereports.ca"]
        start_urls= ["http://genomereports.ca/section.php?Action=List2&Lang=En&addnew=&Report=consolidated_commitments.php&Report_Text=Funding+Commitments&Nav=Section&ID=3&Login=&Password=&Consolidated_Centre=ALL&Consolidated_Category=ALL&Consolidated_Sector=ALL&Consolidated_Competition=ALL&Consolidated_FY=ALL&Consolidated_Status=ALL"]


def parse(self, response):

    sel = Selector(response)
    grants = sel.css('div#content_frame table tr')
    items = []

    for response in grants:
        item = GenomeCanadaGrants()
        item['Province'] = response.xpath('.//th[1]//text()').extract()  # Column Header: CENTRE
        item['Sector'] = response.xpath('.//th[2]//text()').extract()  # Column Header: SECTOR
        item['Fund'] = response.xpath('.//th[3]//text()').extract()  # Column Header: PROGRAM & Fiscal Yr Awarded
        item['Date'] = response.xpath('.//th[3]//text()').re('\d+\d-\d+\d')  # Column Header: PROGRAM & Fiscal Yr Awarded
        item['Status'] = response.xpath('.//th[4]//text()').extract  # Column Header: STATUS
        item['Principal_Investigator'] = response.xpath('.//th[5]//text()').extract()  # Column Header: PROJECT LEADER(S)
        item['Project_Title'] = response.xpath(".//th[6]//text()").extract  # Column Header: PROJECT TITLE
        item['Additional_Information'] = response.xpath(".//th[6]//a[@href='url']").extract  # Link to PDF with Project Details
        item['Amount'] = response.xpath('.//th[7]//text()').extract  # Column Header: APPROVED BUDGET
        item['GC_Contribution'] = response.xpath('.//th[8]//text()').extract  # Column Header: GC CONTRIBUTION
        # Perhaps this line should be yield item?
        items.append(item)

    return items

答案 5 :(得分:15)

当持久化实体时,我发生这种情况,其中数据库中的现有记录对于使用@Version注释的字段具有NULL值(用于乐观锁定)。在数据库中将NULL值更新为0更正了这一点。

答案 6 :(得分:10)

这不是导致错误的唯一原因。我刚刚在编码中遇到错字错误,我相信它设置了一个已经保存的实体的值。

X x2 = new X();
x.setXid(memberid); // Error happened here - x was a previous global entity I created earlier
Y.setX(x2);

我通过找到导致错误的确切变量(在本例中为String xid)来发现错误。我在保存实体并打印痕迹的整个代码块周围使用catch

{
   code block that performed the operation
} catch (Exception e) {
   e.printStackTrace(); // put a break-point here and inspect the 'e'
   return ERROR;
}

答案 7 :(得分:6)

如果您的收藏品可以为空,请尝试:object.SetYouColection(null);

答案 8 :(得分:5)

在真正需要之前,请勿使用Cascade.AllRolePermission具有双向manyToMany关系。然后下面的代码可以正常工作

    Permission p = new Permission();
    p.setName("help");
    Permission p2 = new Permission();
    p2.setName("self_info");
    p = (Permission)crudRepository.save(p); // returned p has id filled in.
    p2 = (Permission)crudRepository.save(p2); // so does p2.
    Role role = new Role();
    role.setAvailable(true);
    role.setDescription("a test role");
    role.setRole("admin");
    List<Permission> pList = new ArrayList<Permission>();
    pList.add(p);
    pList.add(p2);
    role.setPermissions(pList);
    crudRepository.save(role);

如果对象只是一个“新”对象,那么它会抛出相同的错误。

答案 9 :(得分:5)

为了加上我的2美分,当我不小心发送null作为ID时,我遇到了同样的问题。下面的代码描述了我的场景(OP没有提到任何特定场景)

Employee emp = new Employee();
emp.setDept(new Dept(deptId)); // --> when deptId PKID is null, same error will be thrown
// calls to other setters...
em.persist(emp);

这里我将现有的部门ID设置为新的员工实例,而不是先实际获取部门实体,因为我不想要另一个选择的查询来解雇。

在某些情况下,deptId PKID将从调用方法null开始,我遇到同样的错误。

因此,请注意PK ID的null

答案 10 :(得分:3)

除了所有其他好的答案之外,如果您使用merge来持久化对象并意外忘记使用父类中对象的合并引用,则可能会发生这种情况。考虑以下示例

merge(A);
B.setA(A);
persist(B);

在这种情况下,您合并A但忘记使用A的合并对象。要解决这个问题,你必须像这样重写代码。

A=merge(A);//difference is here
B.setA(A);
persist(B);

答案 11 :(得分:3)

我使用

时出现此错误
getSession().save(object)

但是当我使用

时它没有问题
getSession().saveOrUpdate(object) 

答案 12 :(得分:2)

我也面临着同样的情况。通过在属性上方设置以下注释,可以解决提示的异常。

我遇到的异常。

Exception in thread "main" java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.model.Car_OneToMany

要克服,我使用了注释。

    @OneToMany(cascade = {CascadeType.ALL})
    @Column(name = "ListOfCarsDrivenByDriver")
    private List<Car_OneToMany> listOfCarsBeingDriven = new ArrayList<Car_OneToMany>();

是什么使Hibernate抛出异常:

此异常在您的控制台上抛出,因为那时数据库中不存在我附加到父对象的子对象。

通过提供@OneToMany(cascade = {CascadeType.ALL}),它告诉Hibernate在保存父对象的同时将它们保存到数据库。

答案 13 :(得分:1)

另一个可能的原因:在我的情况下,我试图在保存父母之前将孩子保存在一个全新的实体上。

User.java模型中的代码是这样的:

this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.setNewPassword(password);
this.timeJoin = new Date();
create();

setNewPassword()方法创建PasswordHistory记录并将其添加到User中的历史记录集合中。由于尚未为父级执行create()语句,因此它尝试保存到尚未创建的实体的集合。我需要做的就是在调用create()之后移动setNewPassword()调用。

this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.timeJoin = new Date();
create();
this.setNewPassword(password);

答案 14 :(得分:1)

还有另一种可能导致休眠时出现此错误的可能性。您可以将对象A的未保存引用设置为附加实体B,并希望保留对象C。即使在这种情况下,您也会收到上述错误。

答案 15 :(得分:1)

如果您使用的是Spring Data JPA,那么在服务实现中添加@Transactional注释就可以解决问题。

答案 16 :(得分:1)

就我而言,问题完全不同。我有两个课程,分别是c1和c2。 C1和C2之间的依赖关系是OneToMany。现在,如果我将C1保存在DB中,则会抛出上述错误。

此问题的解决方案是从使用者请求中获取第一个C2的ID,并通过存储库调用找到C2。然后将c2保存到C1对象中。现在如果我要保存C1,则可以正常工作。

答案 17 :(得分:1)

为了完整起见:A

org.hibernate.TransientPropertyValueException 

带有消息

object references an unsaved transient instance - save the transient instance before flushing
当您尝试将实体与对已分离的其他实体的引用持久化合并时,也会发生

答案 18 :(得分:0)

当您有 OneToMany 关系并且您尝试将子实体添加到父实体的列表中,然后通过父实体检索此列表(在保存此父实体之前)时,也会发生这种情况, 不保存子实体本身,例如:

Child childEntity = new Child();
parentEntity.addChild(childEntity);
parentEntity.getChildren(); // I needed the retrieval for logging, but one may need it for other reasons.
parentRepository.save(parentEntity);

当我保存父实体时抛出错误。如果我去掉了前一行的检索,那么就没有抛出错误,当然这不是解决方案。

解决方案是保存 childEntity 并添加保存的子实体到父实体,如下所示:

Child childEntity = new Child();
Child savedChildEntity = childRepository.save(childEntity);
parentEntity.addChild(savedChildEntity);
parentEntity.getChildren();
parentRepository.save(parentEntity);

答案 19 :(得分:0)

在我的情况下,使用Cascade.ALL在表中添加了不必要的条目,该表已经具有相同的对象(生成没有id的子值就可以了)。 因此,我将不得不先从存储库中获取子对象,然后将其设置为主对象,而不是引起问题的主对象。

Student s = studentRepo.findByName(generatedObject.getStudent().getName())
generatedObject.student(s);   

答案 20 :(得分:0)

在引入乐观锁定(@Version)后,所有PUT HTTP事务都面临相同的错误

在更新实体时,必须发送该实体的ID和版本。如果任何实体字段与其他实体相关,那么我们也应该为该字段提供id和版本值,否则JPA会尝试先将该相关实体作为新实体保存

示例:我们有两个实体-> 车辆(id,汽车,版本); 汽车(ID,版本,品牌);要更新/保留车辆实体,请确保车辆实体中的汽车字段提供了ID和版本字段

答案 21 :(得分:0)

只需在基类中构造映射的构造方法。 就像您想要实体A,实体B中的一对一关系一样。 如果您将A作为基类,则A必须具有一个以B作为参数的构造函数。

答案 22 :(得分:0)

我的问题与JUnit的@BeforeEach有关。即使我保存了相关实体(在我的情况下为@ManyToOne),我也遇到了相同的错误。

问题某种程度上与我父母的顺序有关。 如果将值分配给该属性,则问题得以解决。

例如 如果我有可以具有某些类别(一个或多个)的实体问题,并且实体问题具有序列:

@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "feedbackSeq")
@Id
private Long id;

我必须分配值question.setId(1L);

答案 23 :(得分:0)

简介

正如我在this article中解释的那样,在使用JPA和Hibernate时,实体可以处于以下4种状态之一:

  • 新建-从未与Hibernate会话(也称为持久性上下文)相关联且未映射到任何数据库表行的新创建的对象被视为位于处于新状态或瞬态。

    要变得持久,我们需要显式调用persist方法或使用传递持久性机制。

  • 持久性-持久性实体已与数据库表行关联,并由当前运行的持久性上下文进行管理。

    对这样一个实体所做的任何更改都将被检测到并传播到数据库(在会话刷新期间)。

  • 已分离-关闭当前运行的持久性上下文后,所有先前管理的实体都将分离。不再跟踪连续的更改,也不会自动进行数据库同步。

  • 已删除-尽管JPA要求仅允许删除托管实体,但是Hibernate也可以删除已分离的实体(但只能通过remove方法调用)。

  • p>

实体状态转换

要将实体从一种状态转移到另一种状态,可以使用persistremovemerge方法。

JPA entity states

解决问题

您在问题中描述的问题:

object references an unsaved transient instance - save the transient instance before flushing

是由于将处于 New 状态的实体与处于 Managed 状态的实体相关联。

当您将子实体与父实体中的一对多集合相关联并且该集合没有cascade实体状态转换时,会发生这种情况。

因此,正如我在this article中所述,您可以通过将级联添加到触发此失败的实体关联中来解决此问题,如下所示:

@OneToOne关联

@OneToOne(
    mappedBy = "post",
    orphanRemoval = true,
    cascade = CascadeType.ALL)
private PostDetails details;
  

请注意,我们为CascadeType.ALL属性添加了cascade值。

@OneToMany关联

@OneToMany(
    mappedBy = "post", 
    orphanRemoval = true,
    cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>();

同样,CascadeType.ALL适用于bidirectional @OneToMany关联。

现在,为了使级联在双向上正常工作,您还需要确保父级和子级关联是同步的。

  

请查看this article,以获得有关实现此目标的最佳方法的更多详细信息。

@ManyToMany关联

@ManyToMany(
    mappedBy = "authors",
    cascade = {
        CascadeType.PERSIST, 
        CascadeType.MERGE
    }
)
private List<Book> books = new ArrayList<>();

@ManyToMany关联中,您不能使用CascadeType.ALLorphanRemoval,因为这会将删除实体的状态转换从一个父实体传播到另一个父实体。

因此,对于@ManyToMany关联,您通常会级联CascadeType.PERSISTCascadeType.MERGE操作。或者,您可以将其扩展为DETACHREFRESH

  

有关映射@ManyToMany关联的最佳方法的更多详细信息,请同时查看this article

答案 24 :(得分:0)

当我在标记为@Transactional的方法中创建一个新实体和一个关联实体,然后在保存之前执行查询时,这个问题发生了。例如

@Transactional
public someService() {
    Entity someEntity = new Entity();
    AssocaiatedEntity associatedEntity = new AssocaitedEntity();
    someEntity.setAssociatedEntity(associatedEntity);
    associatedEntity.setEntity(someEntity);

    // Performing any query was causing hibernate to attempt to persist the new entity. It would then throw an exception
    someDao.getSomething();

    entityDao.create(someEntity);
}

要修复,我在创建新实体之前执行了查询。

答案 25 :(得分:0)

案例1: 当我尝试创建父项并将该父项引用保存到其子项以及其他一些DELETE / UPDATE查询(JPQL)时,我收到此异常。所以我只是在创建父项后使用相同的父引用创建子项后刷新()新创建的实体。它为我工作。

案例2:

父类

public class Reference implements Serializable {

    @Id
    @Column(precision=20, scale=0)
    private BigInteger id;

    @Temporal(TemporalType.TIMESTAMP)
    private Date modifiedOn;

    @OneToOne(mappedBy="reference")
    private ReferenceAdditionalDetails refAddDetails;
    . 
    .
    .
}

儿童班:

public class ReferenceAdditionalDetails implements Serializable{

    private static final long serialVersionUID = 1L;

    @Id
    @OneToOne
    @JoinColumn(name="reference",referencedColumnName="id")
    private Reference reference;

    private String preferedSector1;
    private String preferedSector2;
    .
    .

}

在上面的情况下,父(Reference)和子(ReferenceAdditionalDetails)具有OneToOne关系,当你尝试创建Reference实体然后它的子(ReferenceAdditionalDetails)时,它会给你相同的异常。因此,为了避免异常,您必须为子类设置null,然后创建父类。(示例代码)

.
.
reference.setRefAddDetails(null);
reference = referenceDao.create(reference);
entityManager.flush();
.
.

答案 26 :(得分:0)

我认为是因为你试图坚持一个对象,这个对象引用了另一个不存在的对象,所以它在&#34; DB方面尝试了#34;引用不存在的行

答案 27 :(得分:0)

当我没有保留父对象但是我正在保存孩子时,我遇到了这个异常。要解决此问题,请在同一会话中保留子对象和父对象,并在父对象上使用CascadeType.ALL。

答案 28 :(得分:0)

此错误有很多可能性,其他一些可能性也在添加页面或编辑页面上。在我的情况下,我试图保存一个对象AdvanceSalary。问题是在编辑AdvanceSalary employee.employee_id为null因为在编辑时我没有设置employee.employee_id。我做了一个隐藏的字段并设置它。我的代码工作得非常好。

    @Entity(name = "ic_advance_salary")
    @Table(name = "ic_advance_salary")
    public class AdvanceSalary extends BaseDO{

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

        @ManyToOne(fetch = FetchType.EAGER)
        @JoinColumn(name = "employee_id", nullable = false)
        private Employee employee;

        @Column(name = "employee_id", insertable=false, updatable=false)
        @NotNull(message="Please enter employee Id")
        private Long employee_id;

        @Column(name = "advance_date")
        @DateTimeFormat(pattern = "dd-MMM-yyyy")
        @NotNull(message="Please enter advance date")
        private Date advance_date;

        @Column(name = "amount")
        @NotNull(message="Please enter Paid Amount")
        private Double amount;

        @Column(name = "cheque_date")
        @DateTimeFormat(pattern = "dd-MMM-yyyy")
        private Date cheque_date;

        @Column(name = "cheque_no")
        private String cheque_no;

        @Column(name = "remarks")
        private String remarks;

        public AdvanceSalary() {
        }

        public AdvanceSalary(Integer advance_salary_id) {
            this.id = advance_salary_id;
        }

        public Integer getId() {
            return id;
        }

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

        public Employee getEmployee() {
            return employee;
        }

        public void setEmployee(Employee employee) {
            this.employee = employee;
        }


        public Long getEmployee_id() {
            return employee_id;
        }

        public void setEmployee_id(Long employee_id) {
            this.employee_id = employee_id;
        }

    }

答案 29 :(得分:0)

错误的一个可能原因是父实体的值设置不存在;例如,对于部门 - 员工关系,您必须编写此关系以修复错误:

<div style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; padding: 50px; background: rgba(0, 0, 0, 0.25);">
    <div style="transform: translate3d(0,0,0);">
        <input type="button" value="Input Button" />
        <button>Button</button>
    </div>

答案 30 :(得分:0)

解决此问题的简单方法是保存两个实体。 首先保存子实体,然后保存父实体。 因为父实体依赖于外键值的子实体。

以下简单的一对一关系考试

insert into Department (name, numOfemp, Depno) values (?, ?, ?)
Hibernate: insert into Employee (SSN, dep_Depno, firstName, lastName, middleName, empno) values (?, ?, ?, ?, ?, ?)

Session session=sf.openSession();
        session.beginTransaction();
        session.save(dep);
        session.save(emp);