Hibernateを使用してオブジェクトを保存すると、次のようなエラーメッセージが表示されます。
object references an unsaved transient instance - save the transient instance before flushing
コレクションマッピングにcascade="all"
(xmlを使用している場合)またはcascade=CascadeType.ALL
(アノテーションを使用している場合)を含める必要があります。
これは、エンティティにコレクションがあり、そのコレクションにデータベースに存在しないアイテムが1つ以上あるために発生します。上記のオプションを指定することで、親を保存するときにそれらをデータベースに保存するようにHibernateに指示します。
これは単なる繰り返しの答えかもしれないと思いますが、明確にするために、@OneToOne
マッピングと@OneToMany
でこれを得ました。どちらの場合も、私がChild
に追加しようとしていたParent
オブジェクトがまだデータベースに保存されていなかったという事実です。そのため、Child
をParent
に追加してからParent
を保存すると、Hibernateは親を保存するときに"object references an unsaved transient instance - save the transient instance before flushing"
メッセージを投げます。
Child
へのcascade = {CascadeType.ALL}
参照のParent's
を追加することは両方の場合の問題を解決しました。これでChild
とParent
が保存されました。
繰り返し回答して申し訳ありませんが、人々をさらに明確にしたいと思いました。
@OneToOne(cascade = {CascadeType.ALL})
@JoinColumn(name = "performancelog_id")
public PerformanceLog getPerformanceLog() {
return performanceLog;
}
これは、保存しているオブジェクトに関連付けられているオブジェクトを保存する必要があるとHibernateが判断したときにオブジェクトを保存するときに発生します。
私はこの問題を抱えていて、参照されたオブジェクトへの変更を保存したくないので、カスケードタイプをNONEにしたかったのです。
秘訣は、Hibernateが参照オブジェクトが保存が必要な新しいオブジェクトであると考えないように、参照オブジェクトのIDとVERSIONが設定されることを確実にすることです。これは私のために働きました。
保存しているクラス内のすべての関係を調べて関連オブジェクト(および関連オブジェクトの関連オブジェクト)を見つけ出し、IDとVERSIONがオブジェクトツリーのすべてのオブジェクトに設定されていることを確認します。
あるいは、必要最小限の「累乗」を使用したい場合(たとえば、カスケード削除をしたくない場合)は、
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
...
@Cascade({CascadeType.SAVE_UPDATE})
private Set<Child> children;
私の場合は、双方向の関係の@ManyToOne
側にCascadeType
がないことが原因でした。もっと正確に言うと、CascadeType.ALL
側に@OneToMany
があり、@ManyToOne
側にはありませんでした。 CascadeType.ALL
に@ManyToOne
を追加することで問題は解決しました。 一対多 side:
@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;
データベース内の既存のレコードが@Versionでアノテーションが付けられたフィールドに対してNULL値を持つエンティティを永続化しているとき(オプティミスティックロック用)、これは私にとっては起こりました。データベースでNULL値を0に更新すると、これが修正されました。
これがエラーの唯一の理由ではありません。私は自分のコーディングのタイプミスエラーのために今それに遭遇しました、そして私はそれが信じています、既に保存された実体の価値を設定しました。
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;
}
あなたのコレクションがnull許容であるならば、ただ試みてください:object.SetYouColection(null);
2セント追加すると、誤ってIDとしてnull
を送信したときにも同じ問題が発生します。以下のコードは私のシナリオを表しています (そして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);
ここでは、別のselectクエリを実行したくないので、実際に部門エンティティを最初に取得せずに、既存の部門IDを新しい従業員インスタンスに設定します。
いくつかのシナリオでは、deptId
PKIDがメソッドを呼び出すことからnull
として来て、私は同じエラーを得ています。
PK IDのnull
の値に注意してください
本当に必要になるまで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);
オブジェクトが単なる「新しい」オブジェクトである場合は、同じエラーが発生します。
私が使用するとき私はこのエラーが出ます
getSession().save(object)
私が使用するときしかしそれは問題なく動作します
getSession().saveOrUpdate(object)
他の良い答えに加えて、オブジェクトを永続化するためにmerge
を使用していて、誤って親クラスでそのオブジェクトのマージされた参照を使用するのを忘れた場合にも、これが起こり得ます。次の例を見てください
merge(A);
B.setA(A);
persist(B);
この場合、A
をマージしますが、A
のマージ済みオブジェクトを使用することを忘れます。この問題を解決するには、このようにコードを書き直す必要があります。
A=merge(A);//difference is here
B.setA(A);
persist(B);
私も同じ状況に直面しました。プロパティの上に次のアノテーションを設定することで、プロンプトが表示された例外を解決しました。
私が直面した例外.
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に親オブジェクトを保存しながらそれらをデータベースに保存するように指示します。
完全を期すために:A
org.hibernate.TransientPropertyValueException
メッセージ付き
object references an unsaved transient instance - save the transient instance before flushing
あるエンティティを永続的に/別のエンティティへの参照とマージしようとしたときにも発生します( detached )。
Spring Data JPAを使用している場合は、サービス実装に@Transactional
アノテーションを追加すると問題が解決します。
もう一つの考えられる理由:私の場合、私は真新しい実体で、親を救う前に子供を救おうとしていました。
コードはUser.Javaモデルでは次のようになりました。
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.setNewPassword(password);
this.timeJoin = new Date();
create();
SetNewPassword()メソッドはPasswordHistoryレコードを作成し、それをUserの履歴コレクションに追加します。 create()ステートメントは、親に対してはまだ実行されていないので、まだ作成されていないエンティティのコレクションに保存しようとしていました。解決するためにしなければならなかったのは、setNewPassword()呼び出しをcreate()呼び出しの後に移動することだけでした。
this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.timeJoin = new Date();
create();
this.setNewPassword(password);
休止状態でこのエラーを引き起こす可能性がある別の可能性があります。あなたはあなたのオブジェクトの未保存の参照A
をアタッチされたエンティティB
に設定し、オブジェクトC
を永続化したいと思うかもしれません。この場合でも、前述のエラーが発生します。
私はあなたがまだ永続化されていない別のオブジェクトへの参照を持つオブジェクトを永続化しようとしているので、それは存在しない行への参照を置くために "DB側"で試してみるためです
ケース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;
.
.
}
上記の場合、親(Reference)と子(ReferenceAdditionalDetails)がOneToOneの関係にあり、Referenceエンティティを作成してからその子(ReferenceAdditionalDetails)を作成しようとすると、同じ例外が発生します。そのため、例外を回避するために、子クラスにnullを設定してから親クラスを作成する必要があります(サンプルコード)
.
.
reference.setRefAddDetails(null);
reference = referenceDao.create(reference);
entityManager.flush();
.
.
この問題を解決する簡単な方法は、両方のエンティティを保存することです。最初に子エンティティを保存してから、親エンティティを保存します。親エンティティは外部キー値について子エンティティに依存しているからです。
一対一の関係の下の簡単な試験
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);
このエラーには非常に多くの可能性があります他のいくつかの可能性も追加ページまたは編集ページにあります。私の場合は、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;
}
}
考えられるエラーの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);
親オブジェクトを永続化していないのに子を保存しているときに、この例外に直面しました。同じセッションで子と親の両方のオブジェクトを永続化し、親でCascadeType.ALLを使用して問題を解決しました。
@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);
}
修正するために、新しいエンティティを作成する前にクエリを実行しました。