Hibernate Error: มีการเชื่อมโยงวัตถุอื่นที่มีค่าตัวระบุเดียวกันกับเซสชันแล้ว


94

ฉันมีวัตถุบางอย่างในการกำหนดค่านี้เป็นหลัก (แบบจำลองข้อมูลจริงซับซ้อนกว่าเล็กน้อย):

  • A มีความสัมพันธ์แบบกลุ่มต่อกลุ่มกับ B (B มีinverse="true")
  • B มีความสัมพันธ์แบบกลุ่มต่อหนึ่งกับ C (ฉันcascadeตั้งค่าเป็น"save-update")
  • C คือตารางประเภท / หมวดหมู่

นอกจากนี้ฉันควรพูดถึงว่าคีย์หลักถูกสร้างขึ้นโดยฐานข้อมูลเมื่อบันทึก

กับข้อมูลของฉันบางครั้งฉันพบปัญหาที่ A มีชุดของวัตถุ B ที่แตกต่างกันและวัตถุ B เหล่านี้อ้างถึงวัตถุ C เดียวกัน

เมื่อฉันโทรหาsession.saveOrUpdate(myAObject)ฉันได้รับข้อผิดพลาดในการจำศีลโดยแจ้งว่า: "a different object with the same identifier value was already associated with the session: C". ฉันรู้ว่าไฮเบอร์เนตไม่สามารถแทรก / อัปเดต / ลบอ็อบเจ็กต์เดียวกันสองครั้งในเซสชันเดียวกัน แต่มีวิธีแก้ไขบ้างไหม ดูเหมือนจะไม่ใช่เรื่องแปลกของสถานการณ์

ในระหว่างการค้นคว้าปัญหานี้ฉันได้เห็นผู้คนแนะนำให้ใช้session.merge()แต่เมื่อฉันทำเช่นนั้นวัตถุที่ "ขัดแย้ง" ใด ๆ จะถูกแทรกลงในฐานข้อมูลเป็นวัตถุว่างโดยมีค่าทั้งหมดที่ตั้งค่าเป็นโมฆะ เห็นได้ชัดว่านั่นไม่ใช่สิ่งที่เราต้องการ

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


ดูว่าคำตอบนี้ช่วยคุณได้หรือไม่ ..
joaonlima

คำตอบ:


98

ส่วนใหญ่อาจเป็นเพราะวัตถุ B ไม่ได้อ้างถึงอินสแตนซ์วัตถุ Java C เดียวกัน พวกเขาอ้างถึงแถวเดียวกันในฐานข้อมูล (เช่นคีย์หลักเดียวกัน) แต่เป็นสำเนาที่ต่างกัน

ดังนั้นสิ่งที่เกิดขึ้นคือเซสชันไฮเบอร์เนตซึ่งกำลังจัดการเอนทิตีจะคอยติดตามว่าวัตถุ Java ใดที่สอดคล้องกับแถวที่มีคีย์หลักเดียวกัน

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


3
ขอบคุณ jbx ดังที่คุณกล่าวไว้ปรากฎว่าวัตถุ B อ้างถึงอินสแตนซ์ C หลายตัวในหน่วยความจำ โดยพื้นฐานแล้วสิ่งที่เกิดขึ้นคือส่วนหนึ่งของโปรแกรมของฉันกำลังอ่านใน C และแนบไปกับ B อีกส่วนหนึ่งกำลังโหลด B อื่นที่มี C เดียวกันจากฐานข้อมูล ทั้งสองกำลังแนบกับ A ซึ่งทำให้เกิดข้อผิดพลาดในการบันทึก ฉันได้ตั้งค่า <pre> cascade </pre> สำหรับความสัมพันธ์ B-> C เป็น "<pre> none </pre>" แต่ฉันยังคงได้รับข้อผิดพลาดเดิม ในความสัมพันธ์แบบกลุ่มต่อหนึ่งหรือแบบหนึ่งต่อกลุ่มมีวิธีที่จะบอกให้ไฮเบอร์เนตเพียงเปลี่ยนคีย์ต่างประเทศและไม่ต้องกังวลกับส่วนที่เหลือหรือไม่?
จอห์น

1
คีย์หลักของ C มีกลยุทธ์การสร้าง ID หรือไม่? เช่นเครื่องกำเนิดไฟฟ้าตามลำดับหรือสิ่งที่คล้ายกัน?
jbx

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

14
ฉันใช้ merge () แทน saveOrUpdate () และ BOOM! มันใช้งานได้ :)
Lahiru Ruhunage


13

คุณต้องทำสิ่งเดียวเท่านั้น เรียกใช้session_object.clear()แล้วบันทึกวัตถุใหม่ การดำเนินการนี้จะล้างเซสชัน (ตามชื่อ aptly) และลบอ็อบเจ็กต์ที่ซ้ำกันที่ละเมิดออกจากเซสชันของคุณ


10

ฉันเห็นด้วยกับ @Hemant Kumar ขอบคุณมาก ตามวิธีการแก้ปัญหาของเขาฉันแก้ไขปัญหาของฉัน

ตัวอย่างเช่น:

@Test
public void testSavePerson() {
    try (Session session = sessionFactory.openSession()) {
        Transaction tx = session.beginTransaction();
        Person person1 = new Person();
        Person person2 = new Person();
        person1.setName("222");
        person2.setName("111");
        session.save(person1);
        session.save(person2);
        tx.commit();
    }
}

Person.java

public class Person {
    private int id;
    private String name;

    @Id
    @Column(name = "id")
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Basic
    @Column(name = "name")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

รหัสนี้มักจะผิดพลาดในแอปพลิเคชันของฉัน: A different object with the same identifier value was already associated with the sessionหลังจากนั้นฉันพบว่าฉันลืมที่จะเพิ่มคีย์หลักของฉันโดยอัตโนมัติ!

ทางออกของฉันคือเพิ่มรหัสนี้ในคีย์หลักของคุณ:

@GeneratedValue(strategy = GenerationType.AUTO)

6

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

ตรวจสอบคุณสมบัติรหัสของเอนทิตีคลาสของคุณ

@Id
private Integer id;

ถึง

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(unique = true, nullable = false)
private Integer id;

1
ไม่ใช่กรณีที่มีคำถามตามรายละเอียดที่ระบุ
Sudip Bhandari

5

ถ่ายโอนงานในการกำหนด ID อ็อบเจ็กต์จาก Hibernate ไปยังฐานข้อมูลโดยใช้:

<generator class="native"/>

สิ่งนี้ช่วยแก้ปัญหาให้ฉันได้



3

วิธีหนึ่งในการแก้ปัญหาข้างต้นคือการลบล้างไฟล์hashcode().
ล้างเซสชันไฮเบอร์เนตก่อนและหลังบันทึกด้วย

getHibernateTemplate().flush();

การตั้งค่าวัตถุแยกอย่างชัดเจนเพื่อnullช่วยด้วย


2

เพิ่งเจอข้อความนี้ แต่อยู่ในรหัส c # ไม่แน่ใจว่าเกี่ยวข้องหรือไม่ (ข้อความแสดงข้อผิดพลาดเดียวกันทุกประการ)

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

ตัวโค้ดถูกห่อหุ้มด้วยธุรกรรมที่ค่อนข้างซับซ้อนซึ่งสามารถอัปเดตเรกคอร์ดจำนวนมากและการอ้างอิงจำนวนมากซึ่งเป็นส่วนหนึ่งของธุรกรรมนั้น (กระบวนการนำเข้า)

หวังว่าจะเป็นเบาะแสให้กับทุกคนที่พบปัญหานี้


2

ค้นหา atribute "Cascade" ใน Hibernate แล้วลบออก เมื่อคุณตั้งค่า "Cascade" ไว้ระบบจะเรียกการดำเนินการอื่น ๆ (บันทึกอัปเดตและลบ) ในเอนทิตีอื่นที่มีความสัมพันธ์กับคลาสที่เกี่ยวข้อง ค่าอัตลักษณ์เดียวกันจะเกิดขึ้น มันได้ผลกับฉัน


1

ฉันพบข้อผิดพลาดนี้ไม่กี่วันต่อมาและฉันใช้เวลามากเกินไปในการแก้ไขข้อผิดพลาดนี้

 public boolean save(OrderHeader header) {
    Session session = sessionFactory.openSession();


    Transaction transaction = session.beginTransaction();

    try {
        session.save(header);

        for (OrderDetail detail : header.getDetails()) {
            session.save(detail);
        }

        transaction.commit();
        session.close();

        return true;
    } catch (HibernateException exception) {

        exception.printStackTrace();
        transaction.rollback();
        return false;
    }
}

ก่อนที่ฉันจะได้รับข้อผิดพลาดนี้ฉันไม่ได้กล่าวถึงประเภทการสร้างรหัสบนวัตถุ OrderDetil เมื่อไม่มีการสร้างรหัส Orderdetails จะทำให้ Id เป็น 0 สำหรับทุกออบเจ็กต์ OrderDetail นี่คือสิ่งที่ #jbx อธิบาย ใช่มันคือคำตอบที่ดีที่สุด ตัวอย่างนี้เกิดขึ้นได้อย่างไร


1

ลองวางโค้ดของแบบสอบถามของคุณดูก่อน นั่นช่วยแก้ปัญหาของฉัน เช่นเปลี่ยนสิ่งนี้:

query1 
query2 - get the error 
update

สำหรับสิ่งนี้:

query2
query1
update

0

คุณอาจไม่ได้ตั้งค่าตัวระบุของวัตถุก่อนที่จะเรียกการสอบถามการอัปเดต


3
ถ้าเขาไม่ใช่เขาก็จะไม่มีปัญหานี้ ปัญหาคือเขามีวัตถุสองชิ้นที่มีตัวระบุเดียวกัน
aalku

0

ฉันพบปัญหาเนื่องจากการสร้างคีย์หลักผิดเมื่อฉันแทรกแถวเช่นนี้:

public void addTerminal(String typeOfDevice,Map<Byte,Integer> map) {
        // TODO Auto-generated method stub
        try {
            Set<Byte> keySet = map.keySet();
            for (Byte byte1 : keySet) {
                Device device=new Device();
                device.setNumDevice(DeviceCount.map.get(byte1));
                device.setTimestamp(System.currentTimeMillis());
                device.setTypeDevice(byte1);
                this.getHibernateTemplate().save(device);
            }
            System.out.println("hah");
        }catch (Exception e) {
            // TODO: handle exception
            logger.warn("wrong");
            logger.warn(e.getStackTrace()+e.getMessage());
        }
}

ฉันเปลี่ยนคลาสตัวสร้างรหัสเป็นข้อมูลประจำตัว

<id name="id" type="int">
    <column name="id" />
    <generator class="identity"  />
 </id>

0

ในกรณีของฉันเพียงแค่ flush () ไม่ได้ผล ฉันต้องใช้ clear () หลังจากล้าง ()

public Object merge(final Object detachedInstance)
    {
        this.getHibernateTemplate().flush();
        this.getHibernateTemplate().clear();
        try
        {
            this.getHibernateTemplate().evict(detachedInstance);
        }
}


0

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


0

ตรวจสอบให้แน่ใจว่าเอนทิตีของคุณมี Generation Type เดียวกันกับเอนทิตีที่แมปทั้งหมด

เช่น UserRole

public class UserRole extends AbstractDomain {

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

private String longName;

private String shortName;

@Enumerated(EnumType.STRING)
private CommonStatus status;

private String roleCode;

private Long level;

@Column(columnDefinition = "integer default 0")
private Integer subRoleCount;

private String modification;

@ManyToOne(fetch = FetchType.LAZY)
private TypeOfUsers licenseType;

}

โมดูล :

public class Modules implements Serializable {

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

private String longName;

private String shortName;

}

เอนทิตีหลักที่มีการทำแผนที่

public class RoleModules implements Serializable{

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

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private UserRole role;

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
private Modules modules;

@Type(type = "yes_no")
private boolean isPrimaryModule;

public boolean getIsPrimaryModule() {
    return isPrimaryModule;
}

}


0

นอกเหนือจากคำตอบทั้งหมดก่อนหน้านี้การแก้ไขที่เป็นไปได้สำหรับปัญหานี้ในโปรเจ็กต์ขนาดใหญ่หากคุณใช้ Value Object สำหรับคลาสของคุณไม่ได้ตั้งค่าแอตทริบิวต์ id ในคลาส VO Transformer


0

เพียงแค่ทำธุรกรรมปัจจุบัน

currentSession.getTransaction().commit();

ตอนนี้คุณสามารถเริ่มธุรกรรมอื่นและทำอะไรก็ได้ในเอนทิตี


0

อีกกรณีหนึ่งเมื่อข้อความแสดงข้อผิดพลาดเดียวกันสามารถสร้างขึ้นเองallocationSize:

@Id
@Column(name = "idpar")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "paramsSequence")
@SequenceGenerator(name = "paramsSequence", sequenceName = "par_idpar_seq", allocationSize = 20)
private Long id;

โดยไม่ต้องจับคู่

alter sequence par_idpar_seq increment 20;

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

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