PersistentObjectException: เอนทิตีที่แยกออกมาถูกส่งผ่านเพื่อคงอยู่โดย JPA และ Hibernate


237

ฉันมีรูปแบบวัตถุ JPA-ยืนกรานที่มีหลายต่อหนึ่งความสัมพันธ์: การมีจำนวนมากAccount มีหนึ่งTransactionsTransactionAccount

นี่คือตัวอย่างของรหัส:

@Entity
public class Transaction {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne(cascade = {CascadeType.ALL},fetch= FetchType.EAGER)
    private Account fromAccount;
....

@Entity
public class Account {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @OneToMany(cascade = {CascadeType.ALL},fetch= FetchType.EAGER, mappedBy = "fromAccount")
    private Set<Transaction> transactions;

ฉันสามารถสร้างAccountวัตถุเพิ่มธุรกรรมไปยังAccountวัตถุนั้นและคงวัตถุไว้อย่างถูกต้อง แต่เมื่อฉันสร้างธุรกรรมโดยใช้บัญชีที่มีอยู่แล้วและยืนยันการทำธุรกรรมฉันจะได้รับข้อยกเว้น:

เกิดจาก: org.hibernate.PersistentObjectException: เอนทิตีเดี่ยวที่ส่งผ่านไปยังคงอยู่: com.paulsanwald.Account ที่ org.hibernate.event.internal.DefaultPersistEventListener.onPersist (DefaultPersistEventListener.java:141)

ดังนั้นฉันสามารถที่จะยังคงมีอยู่Accountที่มีการทำธุรกรรม Accountแต่ไม่ได้ทำธุรกรรมที่มี ฉันคิดว่าเป็นเพราะAccountอาจจะไม่ได้แนบ แต่รหัสนี้ยังให้ฉันข้อยกเว้นเดียวกัน:

if (account.getId()!=null) {
    account = entityManager.merge(account);
}
Transaction transaction = new Transaction(account,"other stuff");
 // the below fails with a "detached entity" message. why?
entityManager.persist(transaction);

ฉันจะบันทึก a ที่Transactionเกี่ยวข้องกับAccountวัตถุที่คงอยู่แล้วได้อย่างถูกต้องได้อย่างไร?


15
ในกรณีของฉันฉันกำลังตั้งค่ารหัสของเอนทิตีที่ฉันพยายามคงอยู่โดยใช้ Entity Manager เมื่อฉันลบ setter สำหรับ id มันเริ่มทำงานได้ดี
Rushi Shah

ในกรณีของฉันฉันไม่ได้ตั้งรหัส แต่มีผู้ใช้สองคนที่ใช้บัญชีเดียวกันหนึ่งในนั้นยังคงมีเอนทิตี (อย่างถูกต้อง) และข้อผิดพลาดเกิดขึ้นเมื่อคนที่สองหลังพยายามยืนยันเอนทิตีเดียวกันที่มีอยู่แล้ว งานอยู่
sergioFC

คำตอบ:


129

นี่เป็นปัญหาความสอดคล้องแบบสองทิศทางโดยทั่วไป มันถูกกล่าวถึงอย่างดีในลิงค์นี้รวมถึงลิงค์นี้

ตามบทความในลิงค์ 2 ก่อนหน้านี้คุณต้องแก้ไข setters ของคุณทั้งสองด้านของความสัมพันธ์แบบสองทิศทาง ตัวอย่าง setter สำหรับด้านหนึ่งอยู่ในลิงค์นี้

ตัวอย่าง setter สำหรับหลาย ๆ ด้านอยู่ในลิงค์นี้

หลังจากที่คุณแก้ไข setters ของคุณคุณต้องการประกาศประเภทการเข้าถึง Entity ให้เป็น "คุณสมบัติ" วิธีปฏิบัติที่ดีที่สุดในการประกาศประเภทการเข้าถึง "คุณสมบัติ" คือการย้ายคำอธิบายประกอบทั้งหมดจากคุณสมบัติของสมาชิกไปยัง getters ที่เกี่ยวข้อง คำเตือนที่สำคัญคือไม่ควรผสมประเภทการเข้าถึง "Field" และ "Property" ภายในคลาสเอนทิตีมิฉะนั้นพฤติกรรมจะไม่ได้กำหนดโดยข้อกำหนด JSR-317


2
ps: คำอธิบายประกอบ @Id เป็นสิ่งที่จำศีลใช้เพื่อระบุประเภทการเข้าถึง
Diego Plentz

2
ข้อยกเว้นคือ: detached entity passed to persistเหตุใดการปรับปรุงความสอดคล้องจึงทำให้ใช้งานได้ ตกลงความสอดคล้องได้รับการซ่อมแซม แต่วัตถุยังถูกแยกออก
Gilgamesz

1
@ Sam ขอบคุณมากสำหรับคำอธิบายของคุณ แต่ฉันยังไม่เข้าใจ ฉันเห็นว่าไม่มีตัวตั้งค่า 'พิเศษ' ความสัมพันธ์แบบสองทิศทางจะไม่พอใจ แต่ฉันไม่เห็นว่าทำไมวัตถุถึงแยกออก
Gilgamesz

28
โปรดอย่าโพสต์คำตอบที่ตอบเพียงลิงค์เท่านั้น
El Mac

6
ไม่เห็นว่าคำตอบนี้เกี่ยวข้องกับคำถามหรือไม่
Eugen Labun

270

การแก้ปัญหาคือง่ายเพียงแค่ใช้CascadeType.MERGEแทนหรือCascadeType.PERSISTCascadeType.ALL

ฉันมีปัญหาเดียวกันและCascadeType.MERGEทำงานให้ฉัน

ฉันหวังว่าคุณจะถูกจัดเรียง


5
น่าแปลกใจที่คนหนึ่งทำงานให้ฉันด้วย มันไม่สมเหตุสมผลเนื่องจาก CascadeType.ALL รวมถึงประเภทของน้ำตกอื่นทั้งหมด ... WTF? ฉันมีสปริง 4.0.4, สปริงข้อมูล jpa 1.8.0 และไฮเบอร์เนต 4.X .. มีใครคิดบ้างไหมว่าทำไมทั้งหมดถึงไม่ทำงาน แต่ MERGE ทำอะไรได้บ้าง
Vadim Kirilchuk

22
@VadimKirilchuk นี่ใช้ได้สำหรับฉันด้วยและมันก็สมเหตุสมผลด้วย เนื่องจากธุรกรรมเป็น PERSISTED จึงพยายามใช้บัญชี PERSIST เช่นกันและไม่ทำงานเนื่องจากบัญชีอยู่ใน db อยู่แล้ว แต่ด้วย CascadeType.MERGE บัญชีจะถูกรวมเข้าด้วยกันโดยอัตโนมัติแทน
Gunslinger

4
สิ่งนี้อาจเกิดขึ้นได้หากคุณไม่ใช้ธุรกรรม
lanoxx

1
วิธีแก้ไขปัญหาอื่น: พยายามที่จะไม่แทรกวัตถุที่มีอยู่แล้ว :)
Andrii Plotnikov

3
ขอบคุณชาย เป็นไปไม่ได้ที่จะหลีกเลี่ยงการแทรกวัตถุที่คงอยู่หากคุณมีข้อ จำกัด สำหรับรหัสอ้างอิงที่จะไม่เป็นโมฆะ นี่คือทางออกเดียว ขอขอบคุณอีกครั้ง.
makkasi

22

ลบการเรียงซ้อนออกจากเอนทิตีTransactionรองควรเป็นเพียง:

@Entity class Transaction {
    @ManyToOne // no cascading here!
    private Account account;
}

( FetchType.EAGERสามารถลบได้เช่นเดียวกับค่าเริ่มต้น@ManyToOne)

นั่นคือทั้งหมด!

ทำไม? โดยกล่าวว่า "น้ำตก ALL" เด็กกิจการTransactionที่คุณจำเป็นต้องให้การดำเนินการทุก DB Accountได้รับการแพร่กระจายไปยังนิติบุคคลผู้ปกครอง จากนั้นถ้าคุณทำpersist(transaction), persist(account)จะถูกเรียกเป็นอย่างดี

แต่เอนทิตีชั่วคราว (ใหม่) อาจถูกส่งผ่านไปยังpersist( Transactionในกรณีนี้) สถานะที่ถูกแยกออก (หรือสถานะที่ไม่ใช่ชั่วคราวอื่น ๆ ) อาจไม่ได้ ( Accountในกรณีนี้เนื่องจากอยู่ในฐานข้อมูลแล้ว)

ดังนั้นคุณจะได้รับการยกเว้น"นิติบุคคลแฝดส่งผ่านไปยังคงมีอยู่" Accountนิติบุคคลที่มีความหมาย! ไม่ได้เป็นTransactionคุณโทรpersistได้ที่


โดยทั่วไปคุณไม่ต้องการเผยแพร่จากเด็กไปยังผู้ปกครอง น่าเสียดายที่มีตัวอย่างโค้ดมากมายในหนังสือ (แม้จะเป็นของดี) และผ่านเน็ตซึ่งทำสิ่งนั้นอย่างแน่นอน ฉันไม่รู้ว่าทำไม ... บางทีบางครั้งก็คัดลอกซ้ำไปซ้ำมาโดยไม่คิดมาก ...

คาดเดาสิ่งที่เกิดขึ้นถ้าคุณremove(transaction)ยังคงมี "น้ำตกทั้งหมด" ใน @ManyToOne account(BTW, กับการทำธุรกรรมอื่น ๆ ทั้งหมด!) จะถูกลบออกจากฐานข้อมูลเช่นกัน แต่นั่นไม่ใช่ความตั้งใจของคุณใช่ไหม?


เพียงแค่ต้องการเพิ่มถ้าคุณตั้งใจที่จะบันทึกเด็กพร้อมกับผู้ปกครองและลบผู้ปกครองพร้อมกับเด็กเช่นบุคคล (ผู้ปกครอง) และที่อยู่ (เด็ก) พร้อมที่อยู่ ID ที่สร้างอัตโนมัติโดย DB จากนั้นก่อนโทรบันทึกบุคคล การโทรเพื่อบันทึกที่อยู่ในวิธีการทำธุรกรรมของคุณ วิธีนี้จะถูกบันทึกพร้อมกับรหัสที่สร้างโดย DB ไม่มีผลกระทบต่อประสิทธิภาพขณะที่ Hibenate ยังคงสร้างแบบสอบถาม 2 รายการเราเพียงแค่เปลี่ยนลำดับของแบบสอบถาม
Vikky

หากเราไม่สามารถผ่านสิ่งใดไปได้เราต้องใช้ค่าเริ่มต้นเท่าใด
Dhwanil Patel

15

การใช้การผสานมีความเสี่ยงและยุ่งยากดังนั้นจึงเป็นวิธีแก้ปัญหาที่สกปรกในกรณีของคุณ คุณต้องจำไว้อย่างน้อยว่าเมื่อคุณส่งวัตถุเอนทิตีเพื่อผสานมันจะหยุดการแนบกับการทำธุรกรรมและจะส่งคืนเอนทิตีที่แนบมาตอนนี้ใหม่แทน ซึ่งหมายความว่าหากใครก็ตามที่มีวัตถุเอนทิตีเก่ายังคงอยู่ในความครอบครองของตนการเปลี่ยนแปลงจะถูกเพิกเฉยอย่างเงียบ ๆ และถูกโยนทิ้งไป

คุณไม่ได้แสดงรหัสที่สมบูรณ์ดังนั้นฉันจึงไม่สามารถตรวจสอบรูปแบบการทำธุรกรรมของคุณอีกครั้งได้ วิธีหนึ่งในการรับสถานการณ์เช่นนี้คือถ้าคุณไม่มีธุรกรรมที่ใช้งานอยู่เมื่อดำเนินการผสานและคงอยู่ ในกรณีนั้นผู้ให้บริการคงอยู่คาดว่าจะเปิดธุรกรรมใหม่สำหรับการดำเนินการ JPA ทุกครั้งที่คุณดำเนินการและยอมรับและปิดทันทีก่อนที่การโทรจะส่งกลับ หากเป็นกรณีนี้การผสานจะถูกเรียกใช้ในธุรกรรมแรกแล้วหลังจากวิธีการผสานส่งคืนธุรกรรมจะเสร็จสมบูรณ์และปิดและเอนทิตีที่ส่งคืนจะถูกแยกออกในขณะนี้ การคงอยู่ด้านล่างมันจะเปิดธุรกรรมที่สองและพยายามอ้างถึงเอนทิตีที่ถูกถอดออกโดยให้ข้อยกเว้น ห่อรหัสของคุณไว้ในธุรกรรมทุกครั้งเว้นแต่ว่าคุณรู้ดีว่ากำลังทำอะไรอยู่

การใช้ธุรกรรมที่มีการจัดการคอนเทนเนอร์จะมีลักษณะเช่นนี้ หมายเหตุ: สิ่งนี้จะถือว่าวิธีอยู่ใน session bean และเรียกผ่าน Local หรือ Remote interface

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void storeAccount(Account account) {
    ...

    if (account.getId()!=null) {
        account = entityManager.merge(account);
    }

    Transaction transaction = new Transaction(account,"other stuff");

    entityManager.persist(account);
}

ฉันกำลังเผชิญกับปัญหาเดียวกันฉันใช้@Transaction(readonly=false)at service layer. แต่ฉันยังคงได้รับปัญหาเดียวกัน
Senthil Arumugam SP

ฉันไม่สามารถพูดได้ว่าฉันเข้าใจอย่างถ่องแท้ว่าทำไมสิ่งต่าง ๆ จึงทำงานเช่นนี้ แต่การวางวิธีการคงอยู่และดูการแมปเอนทิตีเข้าด้วยกันภายในคำอธิบายประกอบธุรกรรมเพื่อแก้ไขปัญหาของฉันดังนั้นขอบคุณ
Deadron

นี่เป็นทางออกที่ดีกว่าการ upvoted ที่สุด
Aleksei Maide

13

อาจเป็นไปได้ว่าในกรณีนี้คุณได้รับaccountวัตถุของคุณโดยใช้ตรรกะการรวมและpersistใช้ในการคงอยู่วัตถุใหม่และมันจะบ่นว่าลำดับชั้นจะมีวัตถุคงอยู่แล้ว คุณควรใช้ในกรณีดังกล่าวแทนsaveOrUpdatepersist


3
มันคือ JPA ดังนั้นฉันคิดว่าวิธีการเปรียบเทียบคือ. merge () แต่นั่นให้ข้อยกเว้นแบบเดียวกันกับฉัน เพื่อความชัดเจนการทำธุรกรรมเป็นวัตถุใหม่บัญชีไม่ใช่
Paul Sanwald

@PaulSanwald ใช้mergeบนtransactionวัตถุที่คุณได้รับข้อผิดพลาดเดียวกันได้หรือไม่
แดน

จริง ๆ แล้วฉันพูดผิด ถ้าฉัน. ผสาน (ธุรกรรม) ดังนั้นการทำธุรกรรมจะไม่คงอยู่ตลอดไป
Paul Sanwald

@ PaulSanwald อืมคุณแน่ใจหรือtransactionไม่ว่ายังไม่ยืนยัน คุณตรวจสอบอย่างไร โปรดทราบว่าmergeจะส่งคืนการอ้างอิงไปยังวัตถุที่ยังคงอยู่
แดน

วัตถุที่ส่งกลับโดย. merge () มี id เป็นศูนย์ นอกจากนี้ฉันกำลังทำ. findAll () หลังจากนั้นและวัตถุของฉันไม่ได้อยู่ที่นั่น
พอล Sanwald

12

อย่าผ่าน id (pk) เพื่อคงอยู่ของเมธอดหรือลองบันทึก () แทนการคงอยู่ ()


2
คำปรึกษาที่ดี! แต่ถ้าสร้างรหัสแล้ว ในกรณีที่ได้รับมอบหมายมันเป็นเรื่องปกติที่จะตั้งรหัส
Aldian

สิ่งนี้ใช้ได้สำหรับฉัน นอกจากนี้คุณสามารถใช้TestEntityManager.persistAndFlush()เพื่อสร้างอินสแตนซ์ที่มีการจัดการและถาวรจากนั้นซิงโครไนซ์บริบทการมีอยู่กับฐานข้อมูลที่อยู่ภายใต้ ส่งคืนเอนทิตีต้นทางต้นฉบับ
Anyul Rivas

7

เนื่องจากนี่เป็นคำถามที่พบบ่อยมากฉันจึงเขียนบทความนี้ขึ้นมาซึ่งคำตอบนี้ขึ้นอยู่กับ

ในการแก้ไขปัญหาคุณต้องทำตามขั้นตอนเหล่านี้:

1. การลบการเชื่อมโยงย่อยชายน์

ดังนั้นคุณต้องลบออก@CascadeType.ALLจากการ@ManyToOneเชื่อมโยง เอนทิตีลูกไม่ควรเชื่อมโยงกับการเชื่อมโยงหลัก เอนทิตีพาเรนต์เท่านั้นควรเรียงซ้อนกับเอนทิตีรอง

@ManyToOne(fetch= FetchType.LAZY)

แจ้งให้ทราบว่าผมตั้งค่าfetchแอตทริบิวต์FetchType.LAZYเพราะกำลังนำความกระตือรือร้นที่ไม่ดีมากสำหรับการทำงาน

2. ตั้งค่าความสัมพันธ์ทั้งสองด้าน

เมื่อใดก็ตามที่คุณมีความสัมพันธ์แบบสองทิศทางคุณต้องซิงโครไนซ์ทั้งสองฝั่งโดยใช้addChildและremoveChildวิธีการในเอนทิตีหลัก:

public void addTransaction(Transaction transaction) {
    transcations.add(transaction);
    transaction.setAccount(this);
}

public void removeTransaction(Transaction transaction) {
    transcations.remove(transaction);
    transaction.setAccount(null);
}

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับเหตุผลที่มันเป็นสิ่งสำคัญที่จะประสานทั้งสองด้านของความสัมพันธ์แบบสองทิศทางตรวจสอบบทความนี้


4

ในความหมายนิติบุคคลของคุณคุณไม่ได้ระบุ@JoinColumnสำหรับเชื่อมต่อกับAccount Transactionคุณจะต้องการสิ่งนี้:

@Entity
public class Transaction {
    @ManyToOne(cascade = {CascadeType.ALL},fetch= FetchType.EAGER)
    @JoinColumn(name = "accountId", referencedColumnName = "id")
    private Account fromAccount;
}

แก้ไข: ดีฉันเดาว่าจะเป็นประโยชน์ถ้าคุณใช้@Tableคำอธิบายประกอบในชั้นเรียนของคุณ หึ :)


2
ใช่ฉันไม่คิดว่ามันจะเป็นแบบเดียวกันทั้งหมดฉันเพิ่ม @JoinColumn (name = "fromAccount_id", referencedColumnName = "id") และมันไม่ทำงาน :)
พอล Sanwald

ใช่ฉันมักจะไม่ใช้ไฟล์การแมป xml สำหรับเอนทิตีการแมปกับตารางดังนั้นฉันมักจะถือว่ามันเป็นคำอธิบายประกอบตาม แต่ถ้าฉันต้องเดาคุณกำลังใช้ hibernate.xml ในการแมปเอนทิตีกับตารางใช่ไหม
NemesisX00

ไม่ฉันกำลังใช้ JPA ข้อมูลสปริงดังนั้นจึงเป็นคำอธิบายประกอบทั้งหมด ฉันมีคำอธิบายประกอบ "mappedBy" อยู่อีกด้าน: @OneToMany (cascade = {CascadeType.ALL}, fetch = FetchType.EAGER, mappedBy = "fromAccount")
Paul Sanwald

4

แม้ว่าคำอธิบายประกอบของคุณจะได้รับการประกาศอย่างถูกต้องเพื่อจัดการความสัมพันธ์แบบหนึ่งต่อหลายคนอย่างเหมาะสมคุณยังอาจพบข้อยกเว้นที่ชัดเจนนี้ เมื่อมีการเพิ่มวัตถุเด็กใหม่Transactionเพื่อรูปแบบข้อมูลที่แนบมาคุณจะต้องจัดการค่าคีย์หลัก - ยกเว้นกรณีที่คุณไม่ควรจะ หากคุณระบุค่าคีย์หลักสำหรับเอนทิตีลูกที่ประกาศดังต่อไปนี้ก่อนโทรpersist(T)คุณจะพบกับข้อยกเว้นนี้

@Entity
public class Transaction {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
....

ในกรณีนี้หมายเหตุประกอบจะประกาศว่าฐานข้อมูลจะจัดการการสร้างค่าคีย์หลักของเอนทิตีเมื่อทำการแทรก การระบุตัวคุณเอง (เช่นผ่านตัวตั้งค่าของ Id) ทำให้เกิดข้อยกเว้นนี้

อีกทางหนึ่ง แต่อย่างมีประสิทธิภาพเดียวกันการประกาศคำอธิบายประกอบนี้ส่งผลให้เกิดข้อยกเว้นเดียวกัน:

@Entity
public class Transaction {
    @Id
    @org.hibernate.annotations.GenericGenerator(name="system-uuid", strategy="uuid")
    @GeneratedValue(generator="system-uuid")
    private Long id;
....

ดังนั้นอย่าตั้งidค่าในรหัสแอปพลิเคชันของคุณเมื่อจัดการแล้ว


4

หากไม่มีสิ่งใดที่ช่วยได้และคุณยังคงได้รับข้อยกเว้นนี้ให้ตรวจสอบequals()วิธีการของคุณ- และไม่รวมการเก็บรวบรวมลูกไว้ในนั้น โดยเฉพาะอย่างยิ่งถ้าคุณมีโครงสร้างที่ลึกของคอลเลกชันที่ฝังตัว (เช่น A มี Bs, B มี Cs ฯลฯ )

ในตัวอย่างของAccount -> Transactions:

  public class Account {

    private Long id;
    private String accountName;
    private Set<Transaction> transactions;

    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (!(obj instanceof Account))
        return false;
      Account other = (Account) obj;
      return Objects.equals(this.id, other.id)
          && Objects.equals(this.accountName, other.accountName)
          && Objects.equals(this.transactions, other.transactions); // <--- REMOVE THIS!
    }
  }

ในตัวอย่างข้างต้นลบธุรกรรมออกจากequals()เช็ค นี่เป็นเพราะไฮเบอร์เนตจะบอกเป็นนัยว่าคุณไม่ได้พยายามอัปเดตวัตถุเก่า แต่คุณส่งวัตถุใหม่ให้คงอยู่เมื่อใดก็ตามที่คุณเปลี่ยนองค์ประกอบในคอลเลกชันลูก
แน่นอนว่าโซลูชันนี้จะไม่เหมาะกับแอปพลิเคชันทั้งหมดและคุณควรออกแบบสิ่งที่คุณต้องการรวมไว้ในequalsและhashCodeวิธีการอย่างระมัดระวัง


1

อาจเป็นข้อผิดพลาดของ OpenJPA เมื่อย้อนกลับจะรีเซ็ตฟิลด์ @Version แต่ pcVersionInit เป็นจริง ฉันมี AbstraceEntity ซึ่งประกาศฟิลด์ @Version ฉันสามารถแก้ไขได้โดยรีเซ็ตฟิลด์ pcVersionInit แต่มันไม่ใช่ความคิดที่ดี ฉันคิดว่ามันไม่ทำงานเมื่อมีการสานต่อเอนทิตี

    private static Field PC_VERSION_INIT = null;
    static {
        try {
            PC_VERSION_INIT = AbstractEntity.class.getDeclaredField("pcVersionInit");
            PC_VERSION_INIT.setAccessible(true);
        } catch (NoSuchFieldException | SecurityException e) {
        }
    }

    public T call(final EntityManager em) {
                if (PC_VERSION_INIT != null && isDetached(entity)) {
                    try {
                        PC_VERSION_INIT.set(entity, false);
                    } catch (IllegalArgumentException | IllegalAccessException e) {
                    }
                }
                em.persist(entity);
                return entity;
            }

            /**
             * @param entity
             * @param detached
             * @return
             */
            private boolean isDetached(final Object entity) {
                if (entity instanceof PersistenceCapable) {
                    PersistenceCapable pc = (PersistenceCapable) entity;
                    if (pc.pcIsDetached() == Boolean.TRUE) {
                        return true;
                    }
                }
                return false;
            }

1

คุณต้องตั้งค่าการทำธุรกรรมสำหรับทุกบัญชี

foreach(Account account : accounts){
    account.setTransaction(transactionObj);
}

หรือมัน colud เพียงพอ (ถ้าเหมาะสม) เพื่อตั้งรหัสเป็นโมฆะในหลาย ๆ ด้าน

// list of existing accounts
List<Account> accounts = new ArrayList<>(transactionObj.getAccounts());

foreach(Account account : accounts){
    account.setId(null);
}

transactionObj.setAccounts(accounts);

// just persist transactionObj using EntityManager merge() method.

1
cascadeType.MERGE,fetch= FetchType.LAZY

1
สวัสดีเจมส์และยินดีต้อนรับคุณควรลองและหลีกเลี่ยงคำตอบของรหัสเท่านั้น โปรดระบุว่าวิธีนี้แก้ปัญหาที่ระบุไว้ในคำถาม (และเมื่อมีการใช้หรือไม่ระดับ API ฯลฯ )
Maarten Bodewes

ความคิดเห็นที่ VLQ: ดูmeta.stackoverflow.com/questions/260411/... คำตอบที่เป็นรหัสเท่านั้นไม่ได้รับการลบการกระทำที่เหมาะสมคือการเลือก "ดูตกลง"
นาธานฮิวจ์

1

คำตอบตาม JPA ของฉันใน Spring Data: ฉันเพิ่งเพิ่ม@Transactionalหมายเหตุประกอบไปยังวิธีการด้านนอกของฉัน

ทำไมมันถึงได้ผล

เอนทิตีย่อยกลายเป็นถูกแยกทันทีเนื่องจากไม่มีบริบทเซสชันไฮเบอร์เนตที่ใช้งานอยู่ การทำธุรกรรม Spring (Data JPA) ช่วยให้มั่นใจว่า Hibernate Session นั้นมีอยู่

อ้างอิง:

https://vladmihalcea.com/a-beginners-guide-to-jpa-hibernate-entity-state-transitions/


0

ในกรณีของฉันฉันกำลังทำธุรกรรมเมื่อใช้วิธีการคงอยู่ เกี่ยวกับการเปลี่ยนยังคงมีการบันทึกวิธีการมันได้รับการแก้ไข


0

หากวิธีการแก้ปัญหาข้างต้นไม่ทำงานเพียงครั้งเดียวแสดงความคิดเห็นวิธีการ getter และ setter ของคลาสเอนทิตีและอย่าตั้งค่าของ id (คีย์หลัก) จากนั้นจะใช้งานได้



0

แก้ไขโดยการบันทึกออบเจกต์ตามก่อนหน้า

สิ่งนี้เกิดขึ้นกับฉันเพราะฉันไม่ได้ตั้งรหัส (ซึ่งไม่ได้สร้างขึ้นโดยอัตโนมัติ) และพยายามบันทึกด้วยความสัมพันธ์ @ManytoOne

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.