回答:
コレクションマッピングに(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"
親を保存するときにメッセージを投げます。
両方のケースで、解決済みの問題へcascade = {CascadeType.ALL}
のParent's
参照を追加しChild
ます。これにより、Child
およびが保存されましたParent
。
繰り返し答えてごめんなさい、人々のためにさらに明確にしたかっただけです。
@OneToOne(cascade = {CascadeType.ALL})
@JoinColumn(name = "performancelog_id")
public PerformanceLog getPerformanceLog() {
return performanceLog;
}
new MyEntity
、データベースから同期されたインスタンスを取得する代わりに、エンティティデータベースを(データベースに同期せずに-フラッシュして)作成しました。そのインスタンスを使用してHibernateクエリを作成すると、データベースにあると予想されるものがアプリのメモリにあるものとは異なることが通知されます。この場合-エンティティのインスタンスをDBから同期/取得して使用します。その場合、CascadeType.ALLは必要ありません。
これは、保存しようとしているオブジェクトに関連付けられているオブジェクトを保存する必要があるとHibernateが判断したときに、オブジェクトを保存するときに発生します。
この問題があり、参照オブジェクトへの変更を保存したくなかったので、カスケードタイプをNONEにしたいと思いました。
コツは、参照されたオブジェクトのIDとVERSIONが確実に設定されるようにすることです。これにより、Hibernateは、参照されたオブジェクトが保存が必要な新しいオブジェクトであるとは見なしません。これでうまくいきました。
保存しているクラスのすべての関係を調べて、関連オブジェクト(および関連オブジェクトの関連オブジェクト)を調べ、オブジェクトツリーのすべてのオブジェクトにIDとVERSIONが設定されていることを確認します。
この記事で JPAとHibernateを使用するときに説明したように、エンティティは次の4つの状態のいずれかになります。
新規 -Hibernateセッション(別名永続コンテキスト)に関連付けられたことのない新しく作成されたオブジェクトで、データベーステーブルの行にマップされていないものは、新規または一時的な状態であると見なされます。
永続化するには、persist
メソッドを明示的に呼び出すか、推移的永続化メカニズムを利用する必要があります。
永続 -永続エンティティはデータベーステーブル行に関連付けられており、現在実行中の永続コンテキストによって管理されています。
このようなエンティティに加えられた変更はすべて検出され、データベースに伝達されます(セッションのフラッシュ時)。
デタッチ -現在実行中の永続コンテキストが閉じられると、以前に管理されていたすべてのエンティティがデタッチされます。継続的な変更は追跡されなくなり、自動データベース同期は行われません。
削除 -JPAは管理対象エンティティのみを削除することを要求していますが、Hibernateは分離されたエンティティを削除することもできます(ただし、remove
メソッド呼び出しによってのみ)。
エンティティをある状態から別の状態に移動するにはpersist
、remove
またはmerge
メソッドを使用できます。
質問で説明している問題:
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.PERSIST
or CascadeType.MERGE
オペレーションをカスケードします。または、それをDETACH
またはに展開することもできますREFRESH
。
@ManyToMany
関連付けをマッピングする最適な方法の詳細については、こちらの記事もご覧ください。
または、必要なものを達成するために最小限の「パワー」を使用したい場合(たとえば、カスケード削除が不要な場合)は、
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
...
@Cascade({CascadeType.SAVE_UPDATE})
private Set<Child> children;
@ManyToMany(cascade={PERSIST, MERGE, REFRESH, DETACH})
(REMOVEを除くすべて)は、Hibernateのように更新をカスケードしませんCascadeType.SAVE_UPDATE
。
私の場合はそれが持っていないことで引き起こされた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;
これは、データベース内の既存のレコードに@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;
}
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);
一方、オブジェクトが「新しい」オブジェクトの場合は、同じエラーがスローされます。
コレクションがnull可能である場合は、以下を試してください。 object.SetYouColection(null);
私の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
メソッドの呼び出しから来ており、同じエラーが発生します。
したがって、null
PK IDの値に注意してください
この問題は、とマークされたメソッドで新しいエンティティと関連するエンティティを作成し、@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);
}
修正するために、新しいエンティティを作成する前にクエリを実行しました。
私も同じ状況に直面しました。プロパティの上に次のアノテーションを設定することにより、プロンプトされた例外を解決しました。
私が直面した例外。
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に、親オブジェクトを保存しながらデータベースに保存するように指示します。
考えられるもう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);
まだ永続化されていない別のオブジェクトへの参照を持つオブジェクトを永続化しようとしているため、「DB側」で存在しない行への参照を配置しようとしているためと思います
この問題を解決する簡単な方法は、両方のエンティティを保存することです。最初に子エンティティを保存してから、親エンティティを保存します。親エンティティは、外部キー値を子エンティティに依存しているためです。
以下の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つは、親エンティティの値の設定が存在しないことです。たとえば、部門と従業員の関係では、エラーを修正するために次のように記述する必要があります。
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);
このエラーには非常に多くの可能性があり、ページの追加または編集ページにも他の可能性があります。私の場合、オブジェクト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:親を作成し、その親への参照を子に保存してから、他の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();
.
.
私の問題は@BeforeEach
JUnit に関連していました。関連するエンティティを保存したとしても(私の場合@ManyToOne
)、同じエラーが発生しました。
問題はどういうわけか私が私の親に持っているシーケンスに関連しています。その属性に値を割り当てると、問題は解決します。
例 いくつかのカテゴリ(1つ以上)を持つことができるエンティティQuestionがあり、エンティティQuestionにシーケンスがある場合:
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "feedbackSeq")
@Id
private Long id;
値を割り当てる必要があります question.setId(1L);