Hibernate - การอัพเดตแบบกลุ่มส่งคืนจำนวนแถวที่ไม่คาดคิดจากการอัพเดต: 0 จำนวนแถวจริง: 0 คาดว่า: 1


141

ฉันได้รับข้อผิดพลาดการจำศีล ฉันสามารถระบุฟังก์ชันที่ทำให้เกิดปัญหาได้ น่าเสียดายที่มีการเรียก DB หลายครั้งในฟังก์ชัน ฉันไม่พบบรรทัดที่ทำให้เกิดปัญหาเนื่องจากจำศีลลบเซสชันในตอนท้ายของธุรกรรม ข้อผิดพลาดการไฮเบอร์เนตที่กล่าวถึงด้านล่างดูเหมือนว่าข้อผิดพลาดทั่วไป ไม่ได้กล่าวถึงว่า Bean เป็นสาเหตุของปัญหา ใครที่คุ้นเคยกับข้อผิดพลาดจำศีลนี้?

org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1
        at org.hibernate.jdbc.BatchingBatcher.checkRowCount(BatchingBatcher.java:93)
        at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:79)
        at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:58)
        at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:195)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:142)
        at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:297)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:985)
        at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:333)
        at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
        at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:584)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransacti
onManager.java:500)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManag
er.java:473)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.doCommitTransactionAfterReturning(Transaction
AspectSupport.java:267)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:176)

ขอบคุณ @Peter Mortensen ฉันได้อัปเดตอีเมลแล้ว
Sujee

ผมมีปัญหาเดียวกัน. นี่ไม่ใช่ปัญหาใหญ่เพราะมันเกิดขึ้นน้อยมาก การใช้ show_sql ไม่สามารถใช้งานได้จริงเนื่องจากการทำซ้ำพฤติกรรมนี้ต้องใช้ธุรกรรมเป็นล้าน อย่างไรก็ตามเนื่องจากสิ่งนี้เกิดขึ้นซ้ำ ๆ ในระหว่างการทดสอบระบบฉันเรียกใช้ (ที่มี gazillions ของธุรกรรม) ฉันสงสัยว่ามีเหตุผลเฉพาะ
daniel_or_else

ฉันพบปัญหานี้ในขณะที่ฉันพยายามอัปเดตแถวที่ไม่มีการเปลี่ยนแปลง อย่าอัปเดตบางอย่างหากไม่มีความแตกต่าง
Fifman

คำตอบ:


62

หากไม่มีรหัสและการแมปสำหรับธุรกรรมของคุณคุณจะไม่สามารถตรวจสอบปัญหาได้

อย่างไรก็ตามหากต้องการจัดการที่ดีขึ้นเกี่ยวกับสาเหตุของปัญหาให้ลองปฏิบัติดังนี้:

  • ในการกำหนดค่า hibernate ให้ตั้งค่า hibernate.show_sql เป็น true สิ่งนี้จะแสดงให้คุณเห็น SQL ที่ถูกเรียกใช้งานและทำให้เกิดปัญหา
  • ตั้งค่าระดับการบันทึกสำหรับ Spring และ Hibernate เป็น DEBUG อีกครั้งซึ่งจะทำให้คุณมีความคิดที่ดีขึ้นว่าบรรทัดใดเป็นสาเหตุของปัญหา
  • สร้างการทดสอบหน่วยซึ่งทำซ้ำปัญหาโดยไม่มีการกำหนดค่าตัวจัดการธุรกรรมใน Spring สิ่งนี้จะทำให้คุณมีความคิดที่ดีขึ้นเกี่ยวกับบรรทัดของโค้ดที่ละเมิด

หวังว่าจะช่วย


21
hibernate.show_sql> ฉันอยากจะแนะนำให้ตั้งค่าระดับหมวดหมู่บันทึกของ org.hibernate.SQL เป็น DEBUG วิธีนี้คุณไม่จำเป็นต้องแก้ไขการกำหนดค่าไฮเบอร์เนตเพียงบันทึก
ธีรี่ร์

การใช้ "show_sql" นั้นมีความละเอียดมากและไม่สามารถนำไปใช้ในการผลิตได้ สำหรับสิ่งนี้ฉันได้แก้ไข catch clause ที่บรรทัด 74 ของคลาส BatchingBatcher เพื่อพิมพ์คำสั่งด้วย ps.toString () เพื่อให้มีเฉพาะคำสั่งที่มีปัญหา
Orden

71

ฉันได้รับข้อยกเว้นเดียวกันในขณะที่ลบระเบียนด้วย Id ที่ไม่มีอยู่เลย ดังนั้นตรวจสอบว่าคุณกำลังอัพเดท / ลบบันทึกอยู่ในฐานข้อมูลจริง


12
ฉันมีปัญหานี้เมื่อฉันลบเด็กออกจากความสัมพันธ์ระหว่างผู้ปกครองกับเด็กบันทึกผู้ปกครอง (ซึ่งจะเป็นการลบเด็ก) จากนั้นพยายามลบเด็กด้วยตนเอง
เดฟ thieben

นี่เป็นการแก้ไขปัญหาของฉัน ไม่มีเรคคอร์ดและบริการของฉันเรียกเมธอด updateAll () ในขณะที่มันจำเป็นต้องใช้เพื่อเรียกเมธอด createOrUpdateAll () ขอบคุณ
Mital Pritmani

3
ดังนั้นวิธีการแก้ปัญหาได้อย่างไร ถ้าฉันได้รับบันทึกแล้วลบมัน แต่ถ้าระบบลบไปแล้วก่อนที่ฉันจะลบอีกแอปพลิเคชันของฉันจะส่งข้อยกเว้น
Stony

@davethieben นี่คือข้อมูลที่ฉันต้องการ ฉันไม่ได้ตระหนักถึงความสัมพันธ์ระหว่างพ่อแม่และลูกที่ยังคงมีอยู่ในการลบ
snowe

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

55

โซลูชัน: ในไฟล์การแมปไฮเบอร์เนตสำหรับคุณสมบัติ id ถ้าคุณใช้คลาสตัวสร้างใด ๆ สำหรับคุณสมบัตินั้นคุณไม่ควรตั้งค่าอย่างชัดเจนโดยใช้เมธอด setter

หากคุณตั้งค่าของคุณสมบัติ Id อย่างชัดเจนจะนำไปสู่ข้อผิดพลาดด้านบน ทำเครื่องหมายที่นี่เพื่อหลีกเลี่ยงข้อผิดพลาดนี้ หรือมันแสดงข้อผิดพลาดเมื่อคุณพูดถึงในไฟล์การแมปตัวสร้างฟิลด์ = "เนทีฟ" หรือ "ส่วนเพิ่ม" และในฐานข้อมูลของคุณตารางที่แมปไม่ได้เป็น auto_incremented Solution: ไปที่ฐานข้อมูลของคุณและอัปเดตตารางของคุณเพื่อตั้งค่า auto_increment


2
ถูกต้องที่สุด. นี่จะเป็นคำตอบที่ถูกต้อง Thanks @ RēdaBiramanē
Kuldeep Verma

1
อย่างไรก็ตามคุณสามารถตั้งค่า ID เป็น '0' แล้วไฮเบอร์เนตจะสามารถเขียนทับได้
forresthopkinsa

1
ขอบคุณ! ไม่แน่ใจว่าทำไมนี่ไม่ใช่คำตอบอันดับสูงสุด
Stuart McIntyre

18

เรื่องนี้เกิดขึ้นกับฉันครั้งเดียวโดยบังเอิญเมื่อฉันกำหนดรหัสเฉพาะให้กับวัตถุบางอย่าง (ทดสอบ) แล้วฉันพยายามบันทึกไว้ในฐานข้อมูล ปัญหาคือว่าในฐานข้อมูลมีนโยบายเฉพาะสำหรับการตั้งค่า ID ของวัตถุ อย่าเพิ่งกำหนด ID หากคุณมีนโยบายในระดับไฮเบอร์เนต


15

ในกรณีของฉันฉันมาถึงข้อยกเว้นนี้ในสองกรณีที่คล้ายกัน:

  • ในวิธีการอธิบายด้วย @Transactionalฉันมีการโทรไปยังบริการอื่น (มีการตอบสนองนาน) เมธอดอัพเดตคุณสมบัติบางอย่างของเอนทิตี (หลังจากเมธอดยังคงมีเอนทิตีอยู่ในฐานข้อมูล) หากผู้ใช้ร้องขอวิธีสองครั้ง (ตามที่เขาคิดว่าจะไม่ทำงานในครั้งแรก) เมื่อออกจากวิธีการทำธุรกรรมในครั้งที่สองไฮเบอร์เนตจะพยายามอัปเดตเอนทิตีที่เปลี่ยนสถานะจากจุดเริ่มต้นของธุรกรรม เมื่อไฮเบอร์เนตค้นหาเอนทิตีในสถานะและพบเอนทิตีเดียวกัน แต่เปลี่ยนไปแล้วโดยคำขอแรกจะส่งข้อยกเว้นเนื่องจากไม่สามารถอัปเดตเอนทิตีได้ มันเหมือนความขัดแย้งใน GIT
  • ฉันมีคำขออัตโนมัติ (สำหรับการตรวจสอบแพลตฟอร์ม) ซึ่งอัพเดตเอนทิตี (และการย้อนกลับแบบแมนนวลในไม่กี่วินาทีต่อมา) แต่ทีมทดสอบใช้แพลตฟอร์มนี้แล้ว เมื่อผู้ทดสอบทำการทดสอบในเอนทิตีเดียวกันกับคำขออัตโนมัติ (ภายในหนึ่งร้อยมิลลิวินาที) ฉันได้รับการยกเว้น เช่นเดียวกับในกรณีก่อนหน้าเมื่อออกจากธุรกรรมครั้งที่สองเอนทิตีที่ดึงข้อมูลมาก่อนหน้านี้ได้เปลี่ยนไปแล้ว

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

วิธีแก้ไข: ในการแก้ปัญหาคุณจะต้องเล่นกับ Hibernate LockModeเพื่อค้นหาสิ่งที่ตรงกับความต้องการของคุณมากที่สุด


สวัสดีขอบคุณสำหรับคำตอบที่เป็นประโยชน์ของคุณ คุณช่วยบอกได้ไหมว่า LockMode ที่คุณใช้ไปในที่สุดเพื่อกำจัดข้อผิดพลาด ฉันกำลังเผชิญกับปัญหาที่คล้ายกันเมื่อ API ได้รับผลกระทบอย่างต่อเนื่องในระยะเวลาที่แตกต่างกันเนื่องจากผู้ใช้คลิกสองครั้งโดยไม่ได้ตั้งใจ การล็อคในแง่ร้ายทำร้ายประสิทธิภาพ; ดังนั้นจะสนใจที่จะรู้ว่าสิ่งที่คุณไปในที่สุด?
Madhur Bhaiya

1
มันเป็นเวลานานตั้งแต่ผมมีปัญหาและฉันจำไม่ได้ดีวิธีการแก้ปัญหาที่แน่นอนฉันได้ใส่ในสถานที่ แต่มันเป็นหรือLockMode.READ LockMode.OPTIMISTIC
Sergio Lema

13

ฉันเพิ่งพบปัญหานี้และพบว่าฉันลบบันทึกและพยายามอัปเดตหลังจากนั้นในธุรกรรมไฮเบอร์เนต


9

สิ่งนี้สามารถเกิดขึ้นได้เมื่อทริกเกอร์เรียกใช้คิวรี DML เพิ่มเติม (การแก้ไขข้อมูล) ซึ่งมีผลต่อจำนวนแถว ทางออกของฉันคือการเพิ่มต่อไปนี้ที่ด้านบนของทริกเกอร์ของฉัน:

SET NOCOUNT ON;

1
คำตอบนี้ส่งฉันไปในทิศทางที่ถูกต้อง ฉันทำงานกับฐานข้อมูลดั้งเดิมที่มีทริกเกอร์อยู่ - โชคดีที่ฉันแทนที่สิ่งที่ทริกเกอร์ทำด้วยรหัสดังนั้นฉันสามารถลบได้
S. Baggy

2
+1 นั่นก็เป็นปัญหาของฉันเช่นกัน ฉันใช้ postgresql ดังนั้นจำเป็นต้องใช้คำอธิบายประกอบ @SQLInsert เพื่อปิดการตรวจสอบการนับแถว: technology-ebay.de/the-teams/mobile-de/blog/ …
s1mm0t

SET NOCOUNT OFF *
G. Ciardini

7

ฉันกำลังเผชิญกับปัญหาเดียวกัน รหัสกำลังทำงานในสภาพแวดล้อมการทดสอบ แต่มันไม่ทำงานในสภาพแวดล้อมการจัดเตรียม

org.hibernate.jdbc.BatchedTooManyRowsAffectedException: Batch update returned unexpected row count from update [0]; actual row count: 3; expected: 1

ปัญหาคือตารางมีรายการเดียวสำหรับแต่ละคีย์หลักในการทดสอบตารางฐานข้อมูล แต่ในการจัดเตรียมฐานข้อมูลมีหลายรายการสำหรับคีย์หลักเดียวกัน (ปัญหาอยู่ในการจัดเตรียมฐานข้อมูลตารางไม่มีข้อ จำกัด คีย์หลักใด ๆ และมีหลายรายการ)

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

หลังจากฉันลบระเบียนทั้งหมดที่มีคีย์หลักที่ซ้ำกันและเพิ่มข้อ จำกัด คีย์หลัก มันทำงานได้ดี

Hibernate - Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1

จำนวนแถวที่แท้จริง: 0 // หมายถึงไม่พบระเบียนที่จะอัปเดตอัปเด
ต: 0 // หมายถึงไม่พบระเบียนดังนั้นจึงไม่มีการอัปเดตที่
คาดหวัง: 1 // หมายถึงที่คาดไว้อย่างน้อย 1 ระเบียนที่มีคีย์ในตาราง db

นี่คือปัญหาที่แบบสอบถามพยายามอัปเดตระเบียนสำหรับบางคีย์ แต่ไฮเบอร์เนตไม่พบระเบียนใด ๆ ด้วยคีย์


สวัสดี @ParagFlume ฉันได้รับข้อผิดพลาดเดียวกัน "การอัปเดตแบบกลุ่มกลับจำนวนแถวที่ไม่คาดคิดจากการอัปเดต: 0 จำนวนแถวจริง: 0 คาดว่า: 1" เมื่อฉันพยายามเลือกหนึ่งวัตถุจากฐานข้อมูลของฉัน แนวคิดเกี่ยวกับวิธีแก้ไขปัญหานี้ฉันอยู่ในสถานการณ์วิกฤติ ความนับถือ !
James

ฉันคิดว่าข้อความค้นหาไม่พบระเบียนใด ๆ ในฐานข้อมูล แต่คาดว่าจะพบระเบียนหนึ่งในฐานข้อมูลซึ่งกำลังพยายามอัปเดต คุณสามารถตรวจสอบด้วยตนเอง / แบบสอบถามเบราว์เซอร์ถ้าบันทึกมีอยู่จริงบน db
ParagFlume

ใช่บันทึกมีอยู่ แต่ปัญหาของฉันคือทำไมพยายามไฮเบอร์เนตเพื่ออัปเดตฉันแค่ใช้บริการที่เลือกเพื่อรับวัตถุจากฐานข้อมูล!
James

อาจเป็นเพราะคุณกำลังพยายามเปลี่ยนสถานะของวัตถุ และเซสชันยังคงเปิดอยู่
ParagFlume

ฉันจะตรวจสอบ behaivor นี้ได้อย่างไรเพราะฉันไม่ใช่คนที่ developp บริการ ...
James

5

ดังที่Juliusกล่าวว่าสิ่งนี้เกิดขึ้นเมื่อมีการอัปเดตบนวัตถุที่มีลูก ๆ ถูกลบ (อาจเป็นเพราะมีความต้องการการอัปเดตสำหรับวัตถุพ่อทั้งหมดและบางครั้งเราชอบที่จะลบเด็ก ๆ และแทรกสิ่งเหล่านั้นลงบนพระบิดา (ใหม่ไม่เก่าไม่สำคัญ) พร้อมกับการอัปเดตอื่น ๆ ที่พ่อมี เขตข้อมูลธรรมดาอื่น ๆ ของมัน) ดังนั้น ... เพื่อให้สิ่งนี้ทำงานลบลูก ๆ (ภายในทรานแซคชัน) โดยการโทรchildrenList.clear()(อย่าวนซ้ำผ่านลูก ๆ และลบแต่ละอันด้วยบางส่วนที่ด้านข้างของวัตถุพ่อจากนั้นอัพเดตพ่อ ( fatherDAO.update (father)). (ทำซ้ำสำหรับวัตถุของพ่อทุกคน) ผลลัพธ์คือเด็กมีการเชื่อมโยงไปยังพ่อของพวกเขาถูกปลดออกจากนั้นพวกเขาก็ถูกลบออกเป็นกรอบกำพร้าchildDAO.delete(childrenList.get(i).delete()))และการตั้งค่า @OneToMany(cascade = CascadeType.XXX ,orphanRemoval=true)


5

ฉันพบปัญหานี้ซึ่งเรามีความสัมพันธ์แบบหนึ่งเดียว

ในไฟล์การแมป hibernate hbm สำหรับต้นแบบสำหรับวัตถุที่มีการจัดเรียงชุดการตั้งค่าเพิ่มcascade="save-update"และมันทำงานได้ดี

หากไม่มีสิ่งนี้ค่าเริ่มต้นของโหมดไฮเบอร์เนตจะพยายามอัปเดตสำหรับระเบียนที่ไม่มีอยู่จริงและโดยการทำเช่นนั้นจะเป็นการแทรกแทน



3

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



2

มันเกิดขึ้นเมื่อคุณพยายามที่จะลบวัตถุเดียวกันและจากนั้นอัปเดตวัตถุเดียวกันใช้สิ่งนี้หลังจากลบ

session.clear ();


2

สิ่งนี้เกิดขึ้นกับฉันเช่นกันเพราะฉันมี ID ของฉันเป็น Long และฉันได้รับจากมุมมองค่า 0 และเมื่อฉันพยายามบันทึกในฐานข้อมูลฉันพบข้อผิดพลาดนี้จากนั้นฉันแก้ไขด้วยการตั้งค่า id เป็น null


1

@Transactionalฉันวิ่งเข้าไปในปัญหานี้เมื่อผมได้ด้วยตนเองเริ่มต้นและการกระทำธุรกรรมภายในของวิธีการเป็นข้อเขียน ฉันแก้ไขปัญหาด้วยการตรวจพบว่ามีธุรกรรมที่ใช้งานอยู่แล้ว

//Detect underlying transaction
if (session.getTransaction() != null && session.getTransaction().isActive()) {
    myTransaction = session.getTransaction();
    preExistingTransaction = true;
} else {
    myTransaction = session.beginTransaction();
}

จากนั้นฉันอนุญาตให้สปริงจัดการการทำธุรกรรม

private void finishTransaction() {
    if (!preExistingTransaction) {
        try {
            tx.commit();
        } catch (HibernateException he) {
            if (tx != null) {
                tx.rollback();
            }
            log.error(he);
        } finally {
            if (newSessionOpened) {
                SessionFactoryUtils.closeSession(session);
                newSessionOpened = false;
                maxResults = 0;
            }
        }
    }
}

1

สิ่งนี้จะเกิดขึ้นเมื่อคุณประกาศ JSF Managed Bean เป็น

@RequestScoped;

เมื่อใดที่คุณควรประกาศว่า

@SessionScoped;

ความนับถือ;


1

ฉันได้รับข้อผิดพลาดนี้เมื่อพยายามอัปเดตวัตถุที่มีรหัสที่ไม่มีอยู่ในฐานข้อมูล เหตุผลข้อผิดพลาดของฉันคือฉันได้กำหนดคุณสมบัติด้วยตนเองด้วยชื่อ 'id' ไปยังฝั่งไคลเอ็นต์ JSON- แทนวัตถุแล้วเมื่อ deserializing วัตถุในฝั่งเซิร์ฟเวอร์คุณสมบัติ 'id' นี้จะเขียนทับตัวแปรอินสแตนซ์ ( เรียกอีกอย่างว่า 'id') ที่ไฮเบอร์เนตควรจะสร้างขึ้น ดังนั้นควรระมัดระวังการตั้งชื่อการชนหากคุณใช้ Hibernate เพื่อสร้างตัวระบุ


1

ฉันก็เจอกับความท้าทายเดียวกัน hibernateTemplateในกรณีของฉันฉันถูกอัปเดตวัตถุที่ไม่ได้มีอยู่แม้กระทั่งการใช้

ที่จริงแล้วในแอปพลิเคชันของฉันฉันได้รับวัตถุ DB เพื่ออัปเดต และในขณะที่อัปเดตค่าของฉันฉันยังปรับปรุง ID โดยไม่ได้ตั้งใจและไปข้างหน้าเพื่ออัปเดตและพบข้อผิดพลาดดังกล่าว

ฉันใช้hibernateTemplateสำหรับการดำเนินการ CRUD


1

หลังจากอ่านคำตอบทั้งหมดแล้วก็ไม่พบใครที่จะพูดถึงการทำไฮเบอร์เนตแบบผกผัน

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

สมมติว่าเรามีสองตาราง:

principal_table , middle_table

กับความสัมพันธ์ของคนหนึ่งไปยังหลาย คลาสการแม็พ hiberntate คือPrincipalและMiddleตามลำดับ

ดังนั้นคลาสPrincipalจึงมี SET ของวัตถุกลาง ไฟล์การแม็พ xml ควรเป็นดังนี้:

<hibernate-mapping>
    <class name="path.to.class.Principal" table="principal_table" ...>
    ...
    <set name="middleObjects" table="middle_table" inverse="true" fetch="select">
        <key>
            <column name="PRINCIPAL_ID" not-null="true" />
        </key>
        <one-to-many class="path.to.class.Middel" />
    </set>
    ...

เนื่องจากการตั้งค่าผกผันเป็น "จริง" หมายความว่าคลาส "Middle" เป็นเจ้าของความสัมพันธ์ดังนั้นคลาสหลักจะไม่ปรับปรุงความสัมพันธ์

ดังนั้นขั้นตอนการอัปเดตอาจนำไปใช้ดังนี้:

session.beginTransaction();

Principal principal = new Principal();
principal.setSomething("1");
principal.setSomethingElse("2");


Middle middleObject = new Middle();
middleObject.setSomething("1");

middleObject.setPrincipal(principal);
principal.getMiddleObjects().add(middleObject);

session.saveOrUpdate(principal);
session.saveOrUpdate(middleObject); // NOTICE: you will need to save it manually

session.getTransaction().commit();

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


1

ในกรณีของเราในที่สุดเราก็พบสาเหตุของการ StaleStateException

ในความเป็นจริงเรากำลังลบแถวสองครั้งในเซสชั่นไฮเบอร์เนตเดียว ก่อนหน้านี้เราใช้ ojdbc6 lib และมันก็โอเคในเวอร์ชั่นนี้

แต่เมื่อเราอัปเกรดเป็น odjc7 หรือ ojdbc8 การลบบันทึกสองครั้งก็เกิดข้อยกเว้น มีข้อผิดพลาดในรหัสของเราที่เราลบสองครั้ง แต่ไม่ปรากฏชัดใน ojdbc6

เราสามารถทำซ้ำด้วยรหัสชิ้นนี้:

Detail detail = getDetail(Long.valueOf(1396451));
session.delete(detail);
session.flush();
session.delete(detail);
session.flush();

ในครั้งแรกที่ล้างจำศีลไปและทำการเปลี่ยนแปลงในฐานข้อมูล ในช่วงที่ 2 flush hibernate จะเปรียบเทียบออบเจ็กต์ของเซสชันกับเร็กคอร์ดของตารางจริง แต่ไม่สามารถหาหนึ่งได้ดังนั้นจึงเป็นข้อยกเว้น


1

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

ฉันใช้ session.evict (); หากต้องการลบแคชที่เก็บไว้ในโหมดไฮเบอร์เนตก่อนหรือหากคุณไม่ต้องการเสี่ยงต่อการสูญเสียข้อมูลคุณควรสร้างวัตถุอื่นสำหรับการจัดเก็บอุณหภูมิข้อมูล

     try
    {
        if(!session.isOpen())
        {
            session=EmployeyDao.getSessionFactory().openSession();
        }
            tx=session.beginTransaction();

        session.evict(e);
        session.saveOrUpdate(e);
        tx.commit();;
        EmployeyDao.shutDown(session);
    }
    catch(HibernateException exc)
    {
        exc.printStackTrace();
        tx.rollback();
    }

1

Hibernate 5.4.1 และปัญหา HHH-12878

ก่อนที่จะไฮเบอร์เนต 5.4.1 ข้อยกเว้นความล้มเหลวในการล็อคในแง่ดี (เช่นStaleStateExceptionหรือOptimisticLockException) ไม่ได้รวมคำสั่งที่ล้มเหลว

ปัญหาHHH-12878ถูกสร้างขึ้นเพื่อปรับปรุง Hibernate ดังนั้นเมื่อโยนข้อยกเว้นการล็อคในแง่ดีการPreparedStatementใช้JDBC จะถูกบันทึกเช่นกัน:

if ( expectedRowCount > rowCount ) {
    throw new StaleStateException(
            "Batch update returned unexpected row count from update ["
                    + batchPosition + "]; actual row count: " + rowCount
                    + "; expected: " + expectedRowCount + "; statement executed: "
                    + statement
    );
}

เวลาทดสอบ

ฉันสร้างที่BatchingOptimisticLockingTestเก็บ GitHub Persistence Java ที่มีประสิทธิภาพสูงของฉันเพื่อสาธิตวิธีการทำงานของพฤติกรรมใหม่

อันดับแรกเราจะกำหนดPostเอนทิตีที่กำหนด@Versionคุณสมบัติดังนั้นเปิดใช้งานกลไกการล็อกในแง่ดีโดยนัย :

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    private String title;

    @Version
    private short version;

    public Long getId() {
        return id;
    }

    public Post setId(Long id) {
        this.id = id;
        return this;
    }

    public String getTitle() {
        return title;
    }

    public Post setTitle(String title) {
        this.title = title;
        return this;
    }

    public short getVersion() {
        return version;
    }
}

เราจะเปิดใช้งานการแบตช์ JDBC โดยใช้คุณสมบัติการกำหนดค่า 3 รายการต่อไปนี้:

properties.put("hibernate.jdbc.batch_size", "5");
properties.put("hibernate.order_inserts", "true");
properties.put("hibernate.order_updates", "true");

เราจะสร้าง 3 Postเอนทิตี:

doInJPA(entityManager -> {
    for (int i = 1; i <= 3; i++) {
        entityManager.persist(
            new Post()
                .setTitle(String.format("Post no. %d", i))
        );
    }
});

และไฮเบอร์เนตจะดำเนินการแทรกแบทช์ JDBC:

SELECT nextval ('hibernate_sequence')
SELECT nextval ('hibernate_sequence')
SELECT nextval ('hibernate_sequence')

Query: [
    INSERT INTO post (title, version, id) 
    VALUES (?, ?, ?)
], 
Params:[
    (Post no. 1, 0, 1), 
    (Post no. 2, 0, 2), 
    (Post no. 3, 0, 3)
]

ดังนั้นเรารู้ว่าการแบตช์ JDBC ทำงานได้ดี

ตอนนี้เรามาจำลองปัญหาการล็อคในแง่ดี:

doInJPA(entityManager -> {
    List<Post> posts = entityManager.createQuery("""
        select p 
        from Post p
        """, Post.class)
    .getResultList();

    posts.forEach(
        post -> post.setTitle(
            post.getTitle() + " - 2nd edition"
        )
    );

    executeSync(
        () -> doInJPA(_entityManager -> {
            Post post = _entityManager.createQuery("""
                select p 
                from Post p
                order by p.id
                """, Post.class)
            .setMaxResults(1)
            .getSingleResult();

            post.setTitle(post.getTitle() + " - corrected");
        })
    );
});

ธุรกรรมแรกเลือกPostเอนทิตีทั้งหมดและปรับเปลี่ยนtitleคุณสมบัติ

อย่างไรก็ตามก่อนที่EntityManagerจะล้างข้อมูลแรกเราจะทำการประมวลผลครั้งที่สองโดยใช้executeSyncวิธีการ

ธุรกรรมที่สองแก้ไขรายการแรกPostดังนั้นมันversionจะเพิ่มขึ้น:

Query:[
    UPDATE 
        post 
    SET 
        title = ?, 
        version = ? 
    WHERE 
        id = ? AND 
        version = ?
], 
Params:[
    ('Post no. 1 - corrected', 1, 1, 0)
]

ตอนนี้เมื่อการทำธุรกรรมครั้งแรกพยายามที่จะล้างEntityManagerเราจะได้รับOptimisticLockException:

Query:[
    UPDATE 
        post 
    SET 
        title = ?, 
        version = ? 
    WHERE 
        id = ? AND 
        version = ?
], 
Params:[
    ('Post no. 1 - 2nd edition', 1, 1, 0), 
    ('Post no. 2 - 2nd edition', 1, 2, 0), 
    ('Post no. 3 - 2nd edition', 1, 3, 0)
]

o.h.e.j.b.i.AbstractBatchImpl - HHH000010: On release of batch it still contained JDBC statements

o.h.e.j.b.i.BatchingBatch - HHH000315: Exception executing batch [
    org.hibernate.StaleStateException: 
    Batch update returned unexpected row count from update [0]; 
    actual row count: 0; 
    expected: 1; 
    statement executed: 
        PgPreparedStatement [
            update post set title='Post no. 3 - 2nd edition', version=1 where id=3 and version=0
        ]
], 
SQL: update post set title=?, version=? where id=? and version=?

ดังนั้นคุณต้องอัปเกรดเป็น Hibernate 5.4.1 หรือใหม่กว่าเพื่อรับประโยชน์จากการปรับปรุงนี้


0

สิ่งนี้เกิดขึ้นถ้าคุณเปลี่ยนบางสิ่งในชุดข้อมูลโดยใช้การสืบค้น SQL ดั้งเดิม แต่วัตถุที่เก็บไว้สำหรับชุดข้อมูลเดียวกันนั้นมีอยู่ในแคชเซสชัน ใช้ session.evict (yourObject);


0

ไฮเบอร์เนตวัตถุแคชจากเซสชั่น หากมีการเข้าถึงและแก้ไขวัตถุโดยผู้ใช้มากกว่า 1 รายorg.hibernate.StaleStateExceptionอาจถูกโยนทิ้ง อาจแก้ไขได้ด้วยวิธีผสาน / รีเฟรชเอนทิตีก่อนบันทึกหรือใช้การล็อก ข้อมูลเพิ่มเติม: http://java-fp.blogspot.lt/2011/09/orghibernatestalestateexception-batch.html


0

หนึ่งในนั้น

SessionFactory sf=new Configuration().configure().buildSessionFactory();
Session session=sf.openSession();

UserDetails user=new UserDetails();

session.beginTransaction();
user.setUserName("update user agian");
user.setUserId(12);
session.saveOrUpdate(user);
session.getTransaction().commit();
System.out.println("user::"+user.getUserName());

sf.close();

0

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

หวังว่าจะช่วยให้ร่างกายใด ๆ


0

ฉันได้รับข้อผิดพลาดนี้เนื่องจากฉันทำแผนที่IDโดยใช้คอลัมน์อย่างไม่เหมาะสมId(x => x.Id, "id").GeneratedBy.**Assigned**();

ปัญหาได้รับการแก้ไขโดยใช้ Id(x => x.Id, "id").GeneratedBy.**Identity**();



0

ในกรณีของฉันมีปัญหากับฐานข้อมูลเนื่องจากหนึ่งใน Stored Procs กิน CPU ทั้งหมดทำให้เกิดการตอบสนองฐานข้อมูลสูง เมื่อสิ่งนี้ถูกฆ่าตายปัญหาได้รับการแก้ไข

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