วิธีการแก้ไข "วัตถุของไฮเบอร์เนตอ้างอิงอินสแตนซ์ชั่วคราวที่ไม่ได้บันทึก - บันทึกอินสแตนซ์ชั่วคราวก่อนที่จะล้าง" ข้อผิดพลาด


610

ฉันได้รับข้อผิดพลาดต่อไปนี้เมื่อฉันบันทึกวัตถุโดยใช้ Hibernate

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

1
ในบริบทที่ต้องการข้อผิดพลาดนี้คืออะไร? มันมีหรือไม่มีตัวแปรชั่วคราว
วีเจย์

คำตอบ:


803

คุณควรรวมcascade="all"(หากใช้ xml) หรือcascade=CascadeType.ALL(หากใช้คำอธิบายประกอบ) ในการจับคู่คอลเล็กชันของคุณ

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


7
นี่ไม่ใช่นัยหรือ คุณไม่ต้องการให้ไฮเบอร์เนตบันทึกไว้หรือไม่
Marcus Leon

29
@ Marcus - ไม่มันไม่ใช่ คุณอาจต้องการจัดการด้วยตนเอง
Bozho

5
Bozho ถูกต้อง ฉันพบสถานการณ์ที่ฉันมีคอลเลกชันที่ฉันต้องการจัดการด้วยตนเองเนื่องจากขนาดของพวกเขาหรือเนื่องจากกฎเกณฑ์ทางธุรกิจที่ไม่อนุญาตให้วัตถุทั้งหมดในคอลเลกชันได้รับการบันทึกในเวลาเดียวกัน
Alex Marshall

26
มันไม่ได้เกิดขึ้นเฉพาะกับคอลเลกชัน แต่ยังทำการแมปแบบหนึ่งต่อหนึ่งอย่างง่าย ๆ
Sebastien Lorber

12
จะดีกว่าไหมถ้าเริ่มต้นด้วย CascadeType.PERSIST และใช้การมีอยู่เพื่อบันทึก
Sergii Shevchyk

248

ผมเชื่อว่านี่อาจจะเป็นคำตอบเพียงแค่ทำซ้ำ แต่เพียงชี้แจงผมได้รับนี้ในการทำแผนที่เช่นเดียวกับ@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แก้ปัญหาในทั้งสองกรณี นี้ที่บันทึกไว้และChildParent

ขออภัยสำหรับคำตอบที่ทำซ้ำเพียงต้องการชี้แจงเพิ่มเติมสำหรับคน

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

7
ถ้าฉันไม่ต้องการเรียงซ้อนบันทึกบนความสัมพันธ์ @OneToOne ล่ะ เมื่อสร้างวัตถุทั้งสองเป็นครั้งแรกฉันจะบันทึกลงในฐานข้อมูลโดยไม่ทริกเกอร์ข้อยกเว้นได้อย่างไร
xtian

4
ถ้าฉันต้องการช่วยเด็กในบางกรณีและไม่ใช่เพื่อบางคน
Omaruchan

@xtian: ถ้าอย่างนั้นคุณต้องจัดการลำดับที่ถูกต้องในการบันทึกลงในฐานข้อมูลด้วยการคงวัตถุไว้ด้วย EntityManager โดยพื้นฐานแล้วคุณเพียงแค่พูด em.persist (object1); em.persist (object2); ฯลฯ
kaba713

ฉันได้รับปัญหานี้โดยเฉพาะเมื่อฉันใช้ @ การสืบทอดในกรณีนี้ TABLE_PER_CLASS ฉันกำลังอ้างอิงคลาสย่อย CascadeType.ALL แก้ไขแล้ว
Jim ReesPotter

หรือคุณสร้างวัตถุเอนทิตีของคุณด้วยnew MyEntity(โดยไม่ต้องซิงโครไนซ์กับฐานข้อมูล - ฟลัชชิง) แทนที่จะได้รับอินสแตนซ์ที่ซิงโครไนซ์จากฐานข้อมูล การทำแบบสอบถามไฮเบอร์เนตโดยใช้อินสแตนซ์นั้นจะแจ้งให้คุณทราบว่าสิ่งที่คุณคาดหวังว่าจะอยู่ในฐานข้อมูลนั้นแตกต่างจากสิ่งที่คุณมีในหน่วยความจำของแอป ในกรณีนี้ - เพียงแค่ซิงโครไนซ์ / รับเอนทิตีของคุณจากฐานข้อมูลและใช้มัน ไม่จำเป็นต้องใช้ CascadeType.ALL
Zon

66

สิ่งนี้จะเกิดขึ้นเมื่อบันทึกวัตถุเมื่อ Hibernate คิดว่าต้องบันทึกวัตถุที่เกี่ยวข้องกับวัตถุที่คุณกำลังบันทึก

ฉันมีปัญหานี้และไม่ต้องการบันทึกการเปลี่ยนแปลงกับวัตถุที่อ้างอิงดังนั้นฉันต้องการให้ชนิดของการเรียงซ้อนเป็นไม่มี

เคล็ดลับคือเพื่อให้แน่ใจว่า ID และ VERSION ในวัตถุที่อ้างอิงถูกตั้งค่าเพื่อให้ไฮเบอร์เนตไม่คิดว่าวัตถุที่อ้างอิงเป็นวัตถุใหม่ที่ต้องการการบันทึก สิ่งนี้ใช้ได้สำหรับฉัน

ดูความสัมพันธ์ทั้งหมดในคลาสที่คุณกำลังบันทึกเพื่อทำงานกับวัตถุที่เกี่ยวข้อง (และวัตถุที่เกี่ยวข้องของวัตถุที่เกี่ยวข้อง) และตรวจสอบให้แน่ใจว่า ID และ VERSION ถูกตั้งค่าในวัตถุทั้งหมดของแผนผังวัตถุ


4
ความคิดเห็นนี้ทำให้ฉันในการติดตามที่ถูกต้อง ฉันกำลังกำหนดอินสแตนซ์ใหม่ของผู้ปกครองให้เป็นคุณสมบัติของลูก ดังนั้น NH จึงคิดว่าพวกมันต่างกัน
elvin

2
ใช่. สิ่งนี้จะเกิดขึ้นหากไม่รวมรหัสของวัตถุที่เกี่ยวข้อง (เช่น @JsonIgnore ถูกเพิกเฉย) Hibernate ไม่มีวิธีการระบุเอนทิตีที่เกี่ยวข้องดังนั้นจึงต้องการบันทึก
Rori Stumpf

36

บทนำ

ตามที่ฉันอธิบายในบทความนี้เมื่อใช้ JPA และ Hibernate เอนทิตีสามารถอยู่ในสถานะใดสถานะหนึ่งใน 4 สถานะต่อไปนี้:

  • ใหม่ - วัตถุที่สร้างขึ้นใหม่ที่ไม่เคยเกี่ยวข้องกับ Hibernate Session (aka Persistence Context) และไม่ได้ถูกแมปกับแถวของตารางฐานข้อมูลใด ๆ จะถือว่าอยู่ในสถานะใหม่หรือชั่วคราว

    ในการที่จะคงอยู่เราต้องเรียกpersistวิธีการอย่างชัดเจนหรือใช้กลไกการคงอยู่ของสกรรมกริยา

  • Persistent - เอนทิตีแบบถาวรนั้นเชื่อมโยงกับแถวตารางฐานข้อมูลและมีการจัดการโดย Persistence Context ที่กำลังรันอยู่

    การเปลี่ยนแปลงใด ๆ ที่เกิดขึ้นกับเอนทิตีดังกล่าวจะถูกตรวจพบและแพร่กระจายไปยังฐานข้อมูล (ในระหว่างช่วงเวลาล้างเซสชัน)

  • Detached - เมื่อบริบทการคงอยู่ที่รันอยู่ในปัจจุบันถูกปิดเอนทิตีที่ได้รับการจัดการก่อนหน้านี้ทั้งหมดจะถูกแยกออก การเปลี่ยนแปลงที่สำเร็จจะไม่ถูกติดตามอีกต่อไปและจะไม่มีการซิงโครไนซ์ฐานข้อมูลอัตโนมัติเกิดขึ้น

  • ลบออก - แม้ว่า JPA จะต้องการให้ลบเอนทิตีที่ได้รับการจัดการเท่านั้น แต่ไฮเบอร์เนตยังสามารถลบเอนทิตีที่แยกออกได้ (แต่ผ่านremoveการเรียกเมธอดเท่านั้น)

การเปลี่ยนสถานะเอนทิตี

ที่จะย้ายกิจการจากรัฐหนึ่งไปที่อื่น ๆ คุณสามารถใช้persist, removeหรือmergeวิธีการ

สถานะเอนทิตี JPA

แก้ไขปัญหา

ปัญหาที่คุณอธิบายในคำถามของคุณ:

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

เกิดจากการเชื่อมโยงนิติบุคคลในรัฐที่ ใหม่ไปยังหน่วยงานที่อยู่ในรัฐของการบริหารจัดการ

สิ่งนี้สามารถเกิดขึ้นได้เมื่อคุณเชื่อมโยงเอนทิตีลูกกับคอลเลกชันแบบหนึ่งต่อหลายในเอนทิตีหลักและการรวบรวมไม่ได้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จะเป็นการเผยแพร่การเปลี่ยนสถานะลบเอนทิตีจากพาเรนต์หนึ่งไปยังเอนทิตีหลัก

ดังนั้นสำหรับการ@ManyToManyเชื่อมโยงคุณมักจะเรียงซ้อนCascadeType.PERSISTหรือCascadeType.MERGEดำเนินการ หรือคุณสามารถขยายที่หรือDETACHREFRESH

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับวิธีที่ดีที่สุดในการจับคู่@ManyToManyสมาคมลองดูบทความนี้เช่นกัน


คำอธิบายที่สมบูรณ์และสอดคล้องกัน! การทำงานที่ดี!
เย็น

คุณสามารถค้นหาหลายร้อยคำอธิบายรายละเอียดดังกล่าวในของฉันกวดวิชาไฮเบอร์เนต
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})( แต่นำออก) ไม่น้ำตกการปรับปรุงเช่นของ Hibernate CascadeType.SAVE_UPDATEไม่
jcsahnwaldt พูดว่า GoFundMonica

25

ในกรณีของฉันมันก็เกิดจากการที่ไม่ได้มีCascadeTypeใน@ManyToOneด้านของความสัมพันธ์แบบสองทิศทาง จะแม่นยำมากขึ้นผมก็มีCascadeType.ALLอยู่บนด้านข้างและไม่ได้มีไว้ใน@OneToMany @ManyToOneการเพิ่มCascadeType.ALLเพื่อ@ManyToOneแก้ไขปัญหา หนึ่งต่อหลายด้าน:

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

ถ้าฉันทำเช่นนี้และบันทึกเอนทิตีหลักสิ่งที่เกิดขึ้นคือมีสองแทรกในตารางแม่ของฉันที่เป็นสองแถว ฉันคิดว่ามันเป็นเพราะเรามีน้ำตกทั้งในหน่วยงานผู้ปกครองและเด็ก?
theprogrammer

18

สิ่งนี้เกิดขึ้นกับฉันเมื่อยังคงเอนทิตีที่ระเบียนที่มีอยู่ในฐานข้อมูลมีค่า NULL สำหรับฟิลด์ที่ใส่คำอธิบายประกอบด้วย @Version (สำหรับการล็อกในแง่ดี) การอัพเดตค่า 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

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 ทั้งหมดเป็นโซลูชันที่ขี้เกียจและควรใช้เมื่อจำเป็นเท่านั้น ก่อนอื่นถ้ามีเอนทิตีอยู่แล้วให้ตรวจสอบว่าโหลดในตัวจัดการเอนทิตีปัจจุบันหรือไม่ถ้าไม่โหลด
Renato Mendes

7

หากคอลเลกชันของคุณเป็นโมฆะลอง: object.SetYouColection(null);


นี่เป็นปัญหาของฉันทั้งหมด ฉันไม่เคยจะเดาได้เลยว่าฉันต้องตั้งค่ามันด้วยตนเอง
Deadron

นี่ก็เป็นปัญหาของฉันเช่นกัน ฉันไม่ได้ใช้คอลเล็กชันดังนั้นฉันไม่ได้ลองตอนนี้ แต่ฉันตั้งค่าวัตถุเป็นโมฆะและตอนนี้ก็ใช้ได้
อาหารสัตว์

5

ในการเพิ่ม 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);

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

ในบางสถานการณ์deptIdPKID มาnullจากวิธีการโทรและฉันได้รับข้อผิดพลาดเดียวกัน

ดังนั้นดูnullค่า PK ID


เกิดอะไรขึ้นถ้ามันเป็นโมฆะ?
vipin cp

ฉันมีปัญหาที่คล้ายกัน ฉันได้รับการยกเว้นเมื่อ deptID ของฉันคือ 0 ค่าอื่นใดที่มากกว่า 0 ทำงาน ตลกคือฉันมีแผนกที่มี id = 0
Gustavo

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

สิ่งที่ทำให้ไฮเบอร์เนตส่งข้อยกเว้น:

ข้อยกเว้นนี้ถูกส่งไปที่คอนโซลของคุณเนื่องจากวัตถุลูกที่ฉันแนบกับวัตถุแม่ไม่มีอยู่ในฐานข้อมูลในขณะนั้น

โดยการให้@OneToMany(cascade = {CascadeType.ALL})มันบอก Hibernate เพื่อบันทึกลงในฐานข้อมูลในขณะที่บันทึกวัตถุแม่


2

เพื่อความสมบูรณ์: A

org.hibernate.TransientPropertyValueException 

พร้อมข้อความ

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

นอกจากนี้ยังจะเกิดขึ้นเมื่อคุณพยายามที่จะยังคงมีอยู่ / รวมกิจการกับการอ้างอิงไปยังหน่วยงานอื่นที่เกิดขึ้นจะถูกถอดออก


1

อีกสาเหตุหนึ่งที่เป็นไปได้: ในกรณีของฉันฉันพยายามบันทึกเด็กก่อนบันทึกผู้ปกครองบนเอนทิตีใหม่

รหัสคืออะไรเช่นนี้ในรูปแบบ User.java:

this.lastName = lastName;
this.isAdmin = isAdmin;
this.accountStatus = "Active";
this.setNewPassword(password);
this.timeJoin = new Date();
create();

เมธอด setNewPassword () สร้างเร็กคอร์ด PasswordHistory และเพิ่มลงในการรวบรวมประวัติใน User เนื่องจากคำสั่ง 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

วิธีง่ายๆในการแก้ไขปัญหานี้คือบันทึกเอนทิตีทั้งสอง ก่อนบันทึกเอนทิตีลูกแล้วบันทึกเอนทิตีหลัก เนื่องจากเอนทิตีหลักขึ้นอยู่กับเอนทิตีรองสำหรับค่าคีย์ต่างประเทศ

ด้านล่างการสอบง่าย ๆ ของความสัมพันธ์แบบหนึ่งต่อหนึ่ง

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

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

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 เป็นโมฆะเพราะในการแก้ไขฉันไม่ได้ตั้งค่า 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) ดังนั้นฉันเพียงแค่ล้าง () เอนทิตีที่สร้างขึ้นใหม่หลังจากสร้างพาเรนต์และหลังจากสร้างเด็กโดยใช้การอ้างอิงพาเรนต์เดียวกัน มันใช้งานได้สำหรับฉัน

กรณีที่ 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;
    .
    .

}

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

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

0

ปัญหาของฉันเกี่ยวข้องกับ@BeforeEachJUnit และแม้ว่าฉันบันทึกหน่วยงานที่เกี่ยวข้อง (ในกรณีของฉัน@ManyToOne) ฉันได้รับข้อผิดพลาดเดียวกัน

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

อดีต หากฉันมีคำถามเอนทิตีที่สามารถมีบางหมวดหมู่ (หนึ่งรายการขึ้นไป) และคำถามเอนทิตีมีลำดับ:

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

ฉันต้องกำหนดค่า question.setId(1L);


0

เพียงแค่สร้าง Constructor ของการแมปในคลาสพื้นฐานของคุณ เช่นถ้าคุณต้องการความสัมพันธ์แบบหนึ่งต่อหนึ่งใน Entity A, Entity B. ถ้าคุณใช้ A เป็นคลาสพื้นฐานดังนั้น A ต้องมี Constructor ที่มี B เป็นอาร์กิวเมนต์

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