Hibernate Error: objek berbeda dengan nilai pengenal yang sama telah dikaitkan dengan sesi

94

Saya pada dasarnya memiliki beberapa objek dalam konfigurasi ini (model data sebenarnya sedikit lebih kompleks):

  • A memiliki hubungan banyak-ke-banyak dengan B. (B memiliki inverse="true")
  • B memiliki hubungan banyak-ke-satu dengan C. (saya telah cascademenyetel ke "save-update")
  • C adalah sejenis tabel tipe / kategori.

Juga, saya mungkin harus menyebutkan bahwa kunci utama dihasilkan oleh database saat menyimpan.

Dengan data saya, saya terkadang mengalami masalah di mana A memiliki sekumpulan objek B yang berbeda, dan objek B ini merujuk ke objek C yang sama.

Ketika saya menelepon session.saveOrUpdate(myAObject), saya mendapatkan error hibernasi mengatakan: "a different object with the same identifier value was already associated with the session: C". Saya tahu bahwa hibernasi tidak dapat menyisipkan / memperbarui / menghapus objek yang sama dua kali dalam sesi yang sama, tetapi adakah cara untuk mengatasi ini? Ini sepertinya tidak akan menjadi situasi yang tidak biasa.

Selama penelitian saya tentang masalah ini, saya telah melihat orang-orang menyarankan penggunaan session.merge(), tetapi ketika saya melakukan itu, setiap objek yang "bertentangan" dimasukkan ke dalam database sebagai objek kosong dengan semua nilai disetel ke nol. Jelas bukan itu yang kita inginkan.

[Sunting] Hal lain yang lupa saya sebutkan adalah (karena alasan arsitektur di luar kendali saya), setiap membaca atau menulis perlu dilakukan dalam sesi terpisah.

John
sumber
Lihat apakah jawaban ini membantu Anda ..
joaonlima

Jawaban:

98

Kemungkinan besar itu karena objek B tidak merujuk ke instance objek Java C yang sama. Mereka merujuk ke baris yang sama dalam database (yaitu kunci utama yang sama) tetapi salinannya berbeda.

Jadi yang terjadi adalah sesi Hibernate, yang mengelola entitas, akan melacak objek Java mana yang sesuai dengan baris dengan kunci utama yang sama.

Salah satu opsinya adalah memastikan bahwa Entitas objek B yang merujuk ke baris yang sama sebenarnya merujuk ke instance objek yang sama dari C. Alternatifnya, matikan cascading untuk variabel anggota tersebut. Dengan cara ini saat B bertahan, C tidak. Anda harus menyimpan C secara terpisah. Jika C adalah tabel tipe / kategori, maka mungkin masuk akal untuk seperti itu.

jbx
sumber
3
Terima kasih jbx. Seperti yang Anda katakan, ternyata objek B merujuk ke beberapa instance C dalam memori. Pada dasarnya apa yang terjadi adalah bahwa satu bagian dari program saya membaca dalam C, dan melampirkannya ke B. Bagian lainnya memuat B yang berbeda dengan C yang sama dari database. Keduanya dilampirkan ke A yang memicu kesalahan saat menyimpan. Saya telah menyetel <pre> cascade </pre> untuk hubungan B-> C ke "<pre> none </pre>", tetapi saya masih mendapatkan kesalahan yang sama. Dalam hubungan banyak-ke-satu atau satu-ke-banyak, apakah ada cara untuk memberi tahu Hibernate hanya untuk mengubah kunci asing dan tidak mengkhawatirkan sisanya?
Yohanes
1
Apakah kunci utama C memiliki strategi pembuatan ID? Seperti generator urutan atau yang serupa?
jbx
Ya, masing-masing memiliki urutannya sendiri di database. Seperti yang Anda sebutkan, cascading ternyata menjadi masalahnya. Kami menonaktifkan cascadeing untuk tabel tipe, dan untuk tabel lainnya kami menggunakan "merge" cascade, yang memungkinkan kami untuk memanggil merge () tanpa membuat semua baris null tersebut. Saya telah menandai jawaban Anda dengan benar, terima kasih!
Yohanes
14
Saya telah menggunakan merge () sebagai ganti saveOrUpdate () dan BOOM! berhasil :)
Lahiru Ruhunage
29

Cukup setel cascade ke MERGE, itu akan berhasil.

andy.codes
sumber
13

Anda hanya perlu melakukan satu hal. Jalankan session_object.clear()dan kemudian simpan objek baru tersebut. Ini akan menghapus sesi (sesuai namanya) dan menghapus objek duplikat yang menyinggung dari sesi Anda.

dsk
sumber
10

Saya setuju dengan @Hemant Kumar, terima kasih banyak. Menurut solusinya, saya menyelesaikan masalah saya.

Sebagai contoh:

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

}

Kode ini selalu membuat kesalahan dalam aplikasi saya:, A different object with the same identifier value was already associated with the sessionkemudian saya menemukan bahwa saya lupa menaikkan kunci utama saya secara otomatis!

Solusi saya adalah menambahkan kode ini pada kunci utama Anda:

@GeneratedValue(strategy = GenerationType.AUTO)
Es Biru
sumber
6

Ini berarti Anda mencoba menyimpan beberapa baris dalam tabel Anda dengan referensi ke objek yang sama.

periksa properti id Kelas Entitas Anda.

@Id
private Integer id;

untuk

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(unique = true, nullable = false)
private Integer id;
rex roy
sumber
1
tidak demikian halnya dengan pertanyaan sesuai deskripsi yang diberikan
Sudip Bhandari
5

Mentransfer tugas menetapkan ID objek dari Hibernate ke database dengan menggunakan:

<generator class="native"/>

Ini memecahkan masalah saya.

pengguna2845946
sumber
3

Tambahkan anotasi @GeneratedValue ke kacang yang Anda sisipkan.

Hemant kumar
sumber
Terima kasih! Ini persis masalah saya
DriLLFreAK100
3

Salah satu cara untuk mengatasi masalah di atas adalah dengan menimpa hashcode().
Juga bersihkan sesi hibernasi sebelum dan sesudah penyimpanan.

getHibernateTemplate().flush();

Menyetel objek yang terlepas secara eksplisit nulljuga membantu.

Waqar
sumber
2

Baru saja menemukan pesan ini tetapi dalam kode c #. Tidak yakin apakah itu relevan (meskipun pesan kesalahannya persis sama).

Saya sedang men-debug kode dengan breakpoint dan memperluas beberapa koleksi melalui anggota pribadi sementara debugger berada di titik putus. Setelah menjalankan kembali kode tanpa menggali melalui struktur membuat pesan kesalahan hilang. Sepertinya tindakan melihat ke dalam koleksi pribadi yang lambat dimuat telah membuat NHibernate memuat hal-hal yang seharusnya tidak dimuat pada saat itu (karena mereka adalah anggota pribadi).

Kode itu sendiri dibungkus dalam transaksi yang cukup rumit yang dapat memperbarui sejumlah besar catatan dan banyak ketergantungan sebagai bagian dari transaksi itu (proses impor).

Semoga menjadi petunjuk bagi siapa pun yang menemukan masalah ini.

Ales Potocnik Hahonina
sumber
2

Temukan atribut "Cascade" dalam Hibernate dan hapuslah. Ketika Anda menyetel "Cascade" tersedia, itu akan memanggil operasi lain (menyimpan, memperbarui dan menghapus) pada entitas lain yang memiliki hubungan dengan kelas terkait. Sehingga nilai identitas yang sama akan terjadi. Itu berhasil dengan saya.

Nguyen Vu Quang
sumber
1

Saya mengalami kesalahan ini beberapa hari yang lalu dan saya mempercepat terlalu banyak waktu untuk memperbaiki kesalahan ini.

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

Sebelum saya mendapatkan kesalahan ini, saya tidak menyebutkan tipe pembuatan ID pada Objek OrderDetil. ketika tanpa membuat id Orderdetails 'itu membuat Id sebagai 0 untuk setiap objek OrderDetail. ini yang dijelaskan #jbx. Ya itu jawaban terbaik. ini satu contoh bagaimana hal itu terjadi.

Buddhi
sumber
1

Coba tempatkan kode kueri Anda sebelumnya. Itu memperbaiki masalah saya. mis. ubah ini:

query1 
query2 - get the error 
update

untuk ini:

query2
query1
update
juldeh
sumber
0

Anda mungkin tidak menyetel pengenal objek sebelum memanggil kueri pembaruan.

Fawad Khaliq
sumber
3
Jika tidak, dia tidak akan memiliki masalah ini. Masalahnya adalah dia memiliki dua objek dengan pengenal yang sama.
aalku
0

Saya menemui masalah karena pembangkitan primary key salah, ketika saya menyisipkan baris seperti ini:

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

Saya mengubah kelas pembuat id menjadi identitas

<id name="id" type="int">
    <column name="id" />
    <generator class="identity"  />
 </id>
张云 风
sumber
0

Dalam kasus saya hanya flush () tidak berhasil. Saya harus menggunakan clear () setelah flush ().

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

jika Anda menggunakan EntityRepository, gunakan saveAndFlush alih-alih simpan

Jayen Chondigara
sumber
0

Jika meninggalkan tab ekspresi di IDE saya terbuka yang membuat panggilan hibernasi pada objek menyebabkan pengecualian ini. Saya mencoba menghapus objek yang sama ini. Juga saya mengalami breakpoint pada panggilan hapus yang tampaknya diperlukan untuk membuat kesalahan ini terjadi. Cukup membuat tab ekspresi lain menjadi tab depan atau mengubah pengaturan ide agar tidak berhenti di breakpoint memecahkan masalah ini.

Aaron Byrnes
sumber
0

Pastikan, entitas Anda memiliki Jenis Generasi yang sama dengan semua Entitas yang Dipetakan

Mis: Peran Pengguna

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;

}

Modul:

public class Modules implements Serializable {

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

private String longName;

private String shortName;

}

Entitas Utama dengan Pemetaan

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

}

Sandeep Patel
sumber
0

Selain semua jawaban sebelumnya, kemungkinan perbaikan untuk masalah ini dalam proyek skala besar, jika Anda menggunakan Objek Nilai untuk kelas Anda tidak menyetel atribut id di kelas Transformer VO.

George Michael
sumber
0

cukup lakukan transaksi saat ini.

currentSession.getTransaction().commit();

sekarang Anda dapat memulai Transaksi lain dan melakukan apa pun di entitas

Mahdi
sumber
0

Kasus lain ketika pesan kesalahan yang sama dapat dihasilkan, kustom allocationSize:

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

tanpa pencocokan

alter sequence par_idpar_seq increment 20;

dapat menyebabkan validasi batasan selama penyisipan (yang mudah dipahami) atau "objek berbeda dengan nilai pengenal yang sama telah dikaitkan dengan sesi" - kasus ini kurang jelas.

pengguna158037
sumber