Hibernateの「オブジェクトが未保存の一時インスタンスを参照する-フラッシュする前に一時インスタンスを保存する」エラーを修正する方法


610

Hibernateを使用してオブジェクトを保存すると、次のエラーが表示されます

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

1
必要なコンテキストでこのエラーはありますか?一時的な変数の有無は?
vijay 2017年

回答:


803

コレクションマッピングに(cascade="all"xmlを使用する場合)またはcascade=CascadeType.ALL(注釈を使用する場合)を含める必要があります。

これは、エンティティにコレクションがあり、そのコレクションにデータベースに存在しないアイテムが1つ以上含まれているために発生します。上記のオプションを指定することにより、Hibernateに親を保存するときにそれらをデータベースに保存するように指示します。


7
これは暗黙的ではありませんか?常にHibernateに保存させたいと思いませんか?
マーカスレオン

29
@マーカス-いいえ、そうではありません。それらを手動で処理したい場合があります。
Bozho 2010

5
Bozhoは正しいです。サイズが原因で、またはコレクション内のすべてのオブジェクトを同時に保存することを許可しないビジネスルールが原因で、コレクションを手動で管理したいという状況に遭遇しました。
Alex Marshall

26
これは、コレクションだけでなく、単純な1対1のマッピングでも発生します
Sebastien Lorber 2012

12
CascadeType.PERSISTから開始して、persistを使用して保存する方がよいのではないでしょうか。
Sergii Shevchyk 2012

248

私はこれは単なる繰り返しの答えかもしれないと思いますが、明確にするために、これを@OneToOneマッピングとで取得しました@OneToMany。どちらの場合も、Child私が追加しているオブジェクトがParentまだデータベースに保存されていないことが原因でした。したがって、をに追加してChildParent保存するとParent、Hibernateは"object references an unsaved transient instance - save the transient instance before flushing"親を保存するときにメッセージを投げます。

両方のケースで、解決済みの問題へcascade = {CascadeType.ALL}Parent's参照を追加しChildます。これにより、Childおよびが保存されましたParent

繰り返し答えてごめんなさい、人々のためにさらに明確にしたかっただけです。

@OneToOne(cascade = {CascadeType.ALL})
@JoinColumn(name = "performancelog_id")
public PerformanceLog getPerformanceLog() {
    return performanceLog;
}

7
@OneToOne関係でセーブをカスケードしたくない場合はどうなりますか?両方のオブジェクトを初めて作成するときに、例外をトリガーせずにどちらかをデータベースに保存するにはどうすればよいですか?
xtian

4
子供を救いたい場合とそうでない場合はどうなりますか?
おまるちゃん2016

@xtian:それでは、EntityManagerでオブジェクトを永続化して、データベースに保存する正しい順序に注意する必要があります。基本的には、em.persist(object1);と言うだけです。em.persist(object2); その他
kaba713

特に@Inheritanceを使用したときにこの問題が発生しました。この場合はTABLE_PER_CLASSで、サブクラスを参照していました。CascadeType.ALLで修正されました。
ジムリースポッター2018年

またはnew MyEntity、データベースから同期されたインスタンスを取得する代わりに、エンティティデータベースを(データベースに同期せずに-フラッシュして)作成しました。そのインスタンスを使用してHibernateクエリを作成すると、データベースにあると予想されるものがアプリのメモリにあるものとは異なることが通知されます。この場合-エンティティのインスタンスをDBから同期/取得して使用します。その場合、CascadeType.ALLは必要ありません。
Zon

67

これは、保存しようとしているオブジェクトに関連付けられているオブジェクトを保存する必要があるとHibernateが判断したときに、オブジェクトを保存するときに発生します。

この問題があり、参照オブジェクトへの変更を保存したくなかったので、カスケードタイプをNONEにしたいと思いました。

コツは、参照されたオブジェクトのIDとVERSIONが確実に設定されるようにすることです。これにより、Hibernateは、参照されたオブジェクトが保存が必要な新しいオブジェクトであるとは見なしません。これでうまくいきました。

保存しているクラスのすべての関係を調べて、関連オブジェクト(および関連オブジェクトの関連オブジェクト)を調べ、オブジェクトツリーのすべてのオブジェクトにIDとVERSIONが設定されていることを確認します。


4
このコメントは私を正しい軌道に乗せました。親の新しいインスタンスをその子のプロパティに割り当てていました。したがって、NHはそれらが異なるインスタンスであると考えました。
elvin、2014年

2
はい。これは、たとえば、関連付けられたオブジェクトのIDが含まれていない場合(たとえば、@ JsonIgnoreによって無視された場合)に発生します。Hibernateは関連するエンティティを識別する方法がないため、保存する必要があります。
Rori Stumpf、2014

36

前書き

この記事で JPAとHibernateを使用するときに説明したように、エンティティは次の4つの状態のいずれかになります。

  • 新規 -Hibernateセッション(別名永続コンテキスト)に関連付けられたことのない新しく作成されたオブジェクトで、データベーステーブルの行にマップされていないものは、新規または一時的な状態であると見なされます。

    永続化するには、persistメソッドを明示的に呼び出すか、推移的永続化メカニズムを利用する必要があります。

  • 永続 -永続エンティティはデータベーステーブル行に関連付けられており、現在実行中の永続コンテキストによって管理されています。

    このようなエンティティに加えられた変更はすべて検出され、データベースに伝達されます(セッションのフラッシュ時)。

  • デタッチ -現在実行中の永続コンテキストが閉じられると、以前に管理されていたすべてのエンティティがデタッチされます。継続的な変更は追跡されなくなり、自動データベース同期は行われません。

  • 削除 -JPAは管理対象エンティティのみを削除することを要求していますが、Hibernateは分離されたエンティティを削除することもできます(ただし、removeメソッド呼び出しによってのみ)。

エンティティの状態遷移

エンティティをある状態から別の状態に移動するにはpersistremoveまたはmergeメソッドを使用できます。

JPAエンティティーの状態

問題を修正する

質問で説明している問題:

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

Newの状態のエンティティをManagedの状態のエンティティに関連付けることにより発生し ます。

これは、子エンティティを親エンティティの1対多のコレクションに関連付けており、コレクションがcascadeエンティティの状態遷移を行わない場合に発生する可能性があります。

したがって、この記事で説明したように、次のように、この失敗をトリガーしたエンティティの関連付けにカスケードを追加することでこれを修正できます。

@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双方向の@OneToMany関連付けに適しています。

ここで、カスケードが双方向で適切に機能するためには、親と子の関連付けが同期していることも確認する必要があります。

チェックアウトこの記事をこの目標を達成するための最良の方法は何かの詳細については。

@ManyToMany協会

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

@ManyToMany協会、あなたが使用することはできませんCascadeType.ALLか、orphanRemovalこのよう別の親エンティティへの1人の親から削除するエンティティの状態遷移を伝播します。

したがって、@ManyToManyアソシエーションの場合、通常はCascadeType.PERSISTor CascadeType.MERGEオペレーションをカスケードします。または、それをDETACHまたはに展開することもできますREFRESH

@ManyToMany関連付けをマッピングする最適な方法の詳細については、こちらの記事もご覧ください。


完全で一貫した説明!よくできました!
コールド

あなたは私のHibernateチュートリアルで何百ものそのような詳細な説明を見つけることができます。
Vlad Mihalcea

30

または、必要なものを達成するために最小限の「パワー」を使用したい場合(たとえば、カスケード削除が不要な場合)は、

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

...

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

11
これは受け入れられる答えになるはずです。CascadeType.ALLは広すぎません
lilalinux

5
Hibernate 5.2.8以降、JPAアノテーションで同じ効果を実現する方法はないようです。たとえば、@ManyToMany(cascade={PERSIST, MERGE, REFRESH, DETACH})(REMOVEを除くすべて)は、Hibernateのように更新をカスケードしませんCascadeType.SAVE_UPDATE
jcsahnwaldtは、GoFundMonicaの2017年

25

私の場合はそれが持っていないことで引き起こされたCascadeType上で@ManyToOne双方向の関係の側面。より正確には、私は側にいCascadeType.ALLて、それをつけ@OneToManyていませんでした@ManyToOne。追加CascadeType.ALL@ManyToOneて問題を解決しました。 1対多の側:

@OneToMany(cascade = CascadeType.ALL, mappedBy="globalConfig", orphanRemoval = true)
private Set<GlobalConfigScope>gcScopeSet;

多対1の側(問題の原因)

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

多対1(を追加することで修正CascadeType.PERSIST

@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name="global_config_id")
private GlobalConfig globalConfig;

このようにして親エンティティを保存すると、親テーブルに2行の挿入が2つあります。親エンティティと子エンティティの両方にカスケードがあるからでしょうか。
プログラマ

18

これは、データベース内の既存のレコードに@Version(楽観的ロック用)と注釈が付けられたフィールドのNULL値が含まれるエンティティを永続化するときに発生しました。データベースでNULL値を0に更新すると、これが修正されました。


これは新しい質問であり、少なくとも誤解を招く例外としてバグとして追加する必要があります。これが私の問題の原因であることが判明しました。
BML

これは私の問題を修正します
Jad Chahine

11

これがエラーの唯一の理由ではありません。コーディングにタイプミスがあったため、このエラーが発生しました。これは、すでに保存されているエンティティの値を設定していると思います。

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;
}

1
私と同じ問題。結局のところ、エンティティをローカルで再読み込みし、プロパティを設定して保存すると、問題なく動作しました。
CsBalazsHungary 2014年

8

Cascade.All本当に必要になるまで使用しないでください。Roleそして、Permission双方向の持っている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);

一方、オブジェクトが「新しい」オブジェクトの場合は、同じエラーがスローされます。


1
100%正解です。Cascade.Allは遅延ソリューションであり、必要な場合にのみ適用する必要があります。まず、エンティティがすでに存在する場合は、現在のエンティティマネージャに読み込まれているかどうかを確認し、読み込まれていない場合は確認します。
Renato Mendes

7

コレクションがnull可能である場合は、以下を試してください。 object.SetYouColection(null);


これは完全に私の問題でした。手動でnullに設定する必要があるとは思いもしませんでした。
Deadron、2015

これも私の問題でした。私はコレクションを使用していなかったので、最初はこれを試していませんでしたが、オブジェクトをnullに設定すると動作します。
Fodder

5

私の2セントを追加するために、誤ってnullIDとして送信したときにも同じ問題が発生しました。以下のコードは私のシナリオを示しています(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を新しい従業員インスタンスに設定しています。

一部のシナリオでは、deptIdPKIDがnullメソッドの呼び出しから来ており、同じエラーが発生します。

したがって、nullPK IDの値に注意してください


null可能である場合はどうなりますか?
vipin cp

同様の問題があります。deptIDが0の場合、例外が発生します。0より大きい他の値は機能します。おかしい、私はとDEPTを持っているということであるID = 0
グスタボ

5

この問題は、とマークされたメソッドで新しいエンティティと関連するエンティティを作成し、@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);
}

修正するために、新しいエンティティを作成する前にクエリを実行しました。


4

他のすべての良い答えに加えて、これは、を使用mergeしてオブジェクトを永続化し、誤って親クラスのオブジェクトのマージされた参照を使用することを忘れた場合に発生する可能性があります。次の例を検討してください

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

この場合、マージしましたAが、のマージされたオブジェクトを使用するのを忘れていますA。問題を解決するには、このようにコードを書き直す必要があります。

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

3

使用時にこのエラーが発生します

getSession().save(object)

しかし、私が使用すると問題なく動作します

getSession().saveOrUpdate(object) 

3

私も同じ状況に直面しました。プロパティの上に次のアノテーションを設定することにより、プロンプトされた例外を解決しました。

私が直面した例外。

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に、親オブジェクトを保存しながらデータベースに保存するように指示します。


2

完全を期すために:A

org.hibernate.TransientPropertyValueException 

メッセージ付き

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

また、たまたま切り離された別のエンティティへの参照を持つエンティティを永続化またはマージしようとしたときにも発生します。


1

考えられるもう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);

1

休止状態でこのエラーを引き起こす可能性のある別の可能性があります。オブジェクトの未保存の参照をA添付エンティティに設定し、オブジェクトBを永続化したい場合がありますC。この場合でも、前述のエラーが発生します。


1

Spring Data JPAを使用している場合@Transactional、サービス実装にアノテーションを追加すると問題が解決します。


1

まだ永続化されていない別のオブジェクトへの参照を持つオブジェクトを永続化しようとしているため、「DB側」で存在しない行への参照を配置しようとしているためと思います


0

この問題を解決する簡単な方法は、両方のエンティティを保存することです。最初に子エンティティを保存してから、親エンティティを保存します。親エンティティは、外部キー値を子エンティティに依存しているためです。

以下の1対1の関係の簡単な試験

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

1
それは逆です-子エンティティはFK値を保持し、親に依存するため、最初に親を保存する必要があります!コードブロックでは、あなたはそれを正しく持っています。
Soberの

0

エラーの考えられる原因の1つは、親エンティティの値の設定が存在しないことです。たとえば、部門と従業員の関係では、エラーを修正するために次のように記述する必要があります。

Department dept = (Department)session.load(Department.class, dept_code); // dept_code is from the jsp form which you get in the controller with @RequestParam String department
employee.setDepartment(dept);

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;
        }

    }

0

親オブジェクトを永続化しないときにこの例外に直面しましたが、子を保存していました。この問題を解決するために、同じセッションで子オブジェクトと親オブジェクトの両方を永続化し、親でCascadeType.ALLを使用しました。


0

ケース1:親を作成し、その親への参照を子に保存してから、他のDELETE / UPDATEクエリ(JPQL)を保存しようとしたときに、この例外が発生しました。したがって、親を作成した後、同じ親参照を使用して子を作成した後、新しく作成したエンティティを単にflush()します。それは私のために働いた。

ケース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;
    .
    .

}

上記のケースで、親(参照)と子(参照追加の詳細)がOneToOne関係にあり、参照エンティティを作成してからその子(参照追加の詳細)を作成しようとすると、同じ例外が発生します。したがって、例外を回避するには、子クラスにnullを設定してから親を作成する必要があります。(サンプルコード)

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

0

私の問題は@BeforeEachJUnit に関連していました。関連するエンティティを保存したとしても(私の場合@ManyToOne)、同じエラーが発生しました。

問題はどういうわけか私が私の親に持っているシーケンスに関連しています。その属性に値を割り当てると、問題は解決します。

例 いくつかのカテゴリ(1つ以上)を持つことができるエンティティQuestionがあり、エンティティQuestionにシーケンスがある場合:

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

値を割り当てる必要があります question.setId(1L);


0

基本クラスでマッピングのコンストラクターを作成するだけです。エンティティA、エンティティBで1対1の関係が必要な場合と同様に、Aを基本クラスとして使用する場合、AはコンストラクタとしてBを引数として持つ必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.