Hibernate: simpan, pertahankan, perbarui, gabung, simpanOrUpdate

1. Perkenalan

Pada artikel ini kita akan membahas perbedaan antara beberapa metode antarmuka Sesi : save , persist , update , merge , saveOrUpdate .

Ini bukan pengantar Hibernate dan Anda harus sudah mengetahui dasar-dasar konfigurasi, pemetaan relasional objek, dan bekerja dengan instance entitas. Untuk artikel pengantar tentang Hibernate, kunjungi tutorial kami tentang Hibernate 4 dengan Spring.

2. Sesi sebagai Implementasi Konteks Ketekunan

The Session antarmuka memiliki beberapa metode yang akhirnya mengakibatkan menyimpan data ke database: bertahan , menyimpan , pembaruan , merge , SaveOrUpdate . Untuk memahami perbedaan antara metode ini, pertama-tama kita harus membahas tujuan Sesi sebagai konteks persistensi dan perbedaan antara status instance entitas dalam kaitannya dengan Sesi .

Kita juga harus memahami sejarah pengembangan Hibernate yang menyebabkan beberapa metode API diduplikasi sebagian.

2.1. Mengelola Instans Entitas

Terlepas dari pemetaan objek-relasional itu sendiri, salah satu masalah yang hendak diselesaikan oleh Hibernate adalah masalah pengelolaan entitas selama runtime. Gagasan tentang "konteks persistensi" adalah solusi Hibernate untuk masalah ini. Konteks persistensi dapat dianggap sebagai wadah atau cache tingkat pertama untuk semua objek yang Anda muat atau simpan ke database selama sesi.

Sesi adalah transaksi logis, yang batasannya ditentukan oleh logika bisnis aplikasi Anda. Saat Anda bekerja dengan database melalui konteks ketekunan, dan semua instance entitas Anda dilampirkan ke konteks ini, Anda harus selalu memiliki satu instance entitas untuk setiap rekaman database yang Anda gunakan untuk berinteraksi selama sesi.

Dalam Hibernate, konteks ketekunan diwakili oleh contoh org.hibernate.Session . Untuk JPA, ini adalah javax.persistence.EntityManager . Saat kami menggunakan Hibernate sebagai penyedia JPA dan beroperasi melalui antarmuka EntityManager , implementasi antarmuka ini pada dasarnya membungkus objek Sesi yang mendasarinya . Namun, Hibernate Session menyediakan antarmuka yang lebih kaya dengan lebih banyak kemungkinan sehingga terkadang berguna untuk bekerja dengan Session secara langsung .

2.2. Serikat Contoh Entitas

Setiap instance entitas dalam aplikasi Anda muncul di salah satu dari tiga status utama dalam kaitannya dengan konteks persistensi Sesi :

  • sementara - instance ini tidak, dan tidak pernah, dilampirkan ke Sesi ; instance ini tidak memiliki baris yang sesuai dalam database; biasanya hanya objek baru yang Anda buat untuk disimpan ke database;
  • persisten - instance ini dikaitkan dengan objek Sesi unik ; setelah mengosongkan Sesi ke database, entitas ini dijamin memiliki catatan konsisten yang sesuai di database;
  • terlepas - instance ini pernah dilampirkan ke Sesi (dalam keadaan tetap ), tetapi sekarang tidak; sebuah instance memasuki status ini jika Anda mengeluarkannya dari konteks, menghapus atau menutup Sesi, atau meletakkan instance tersebut melalui proses serialisasi / deserialisasi.

Berikut adalah diagram status yang disederhanakan dengan komentar tentang metode Sesi yang membuat transisi status terjadi.

Saat instance entitas berada dalam status persisten , semua perubahan yang Anda buat pada bidang yang dipetakan dari instance ini akan diterapkan ke rekaman dan bidang database yang sesuai saat membersihkan Sesi . The persisten contoh dapat dianggap sebagai “online”, sedangkan terpisah misalnya telah pergi “offline” dan tidak dipantau untuk perubahan.

Ini berarti bahwa saat Anda mengubah bidang objek persisten , Anda tidak perlu memanggil simpan , perbarui , atau salah satu metode tersebut untuk mendapatkan perubahan ini ke database: yang Anda butuhkan hanyalah melakukan transaksi, atau membersihkan atau menutup sesi , setelah selesai.

2.3. Kesesuaian dengan Spesifikasi JPA

Hibernate adalah implementasi ORM Java yang paling sukses. Tidak heran jika spesifikasi Java persistence API (JPA) sangat dipengaruhi oleh Hibernate API. Sayangnya, ada juga banyak perbedaan: beberapa besar, beberapa lebih halus.

Untuk bertindak sebagai implementasi dari standar JPA, API Hibernasi harus direvisi. Beberapa metode ditambahkan ke antarmuka Sesi agar sesuai dengan antarmuka EntityManager. Metode ini memiliki tujuan yang sama dengan metode “asli”, tetapi sesuai dengan spesifikasi dan karenanya memiliki beberapa perbedaan.

3. Perbedaan Antara Operasi

Penting untuk dipahami sejak awal bahwa semua metode ( bertahan , simpan , perbarui , gabung , simpanOrUpdate ) tidak langsung menghasilkan pernyataan SQL UPDATE atau INSERT yang sesuai. Penyimpanan aktual data ke database terjadi saat melakukan transaksi atau menghapus Sesi .

Metode yang disebutkan pada dasarnya mengelola status instance entitas dengan mentransisikannya di antara berbagai status di sepanjang siklus proses.

Sebagai contoh entitas, kita akan menggunakan penjelasan-dipetakan sederhana entitas Orang :

@Entity public class Person { @Id @GeneratedValue private Long id; private String name; // ... getters and setters }

3.1. Bertahan

The bertahan Metode ini dimaksudkan untuk menambahkan entitas contoh baru dengan konteks kegigihan, yaitu transisi sebuah contoh dari transient ke persisten negara.

Kami biasanya menyebutnya ketika kami ingin menambahkan record ke database (persist an entity instance):

Person person = new Person(); person.setName("John"); session.persist(person);

Apa yang terjadi setelah metode persist dipanggil? The orang objek telah beralih dari transient ke persisten negara. Objek tersebut sekarang berada dalam konteks ketekunan, tetapi belum disimpan ke database. Pembuatan pernyataan INSERT hanya akan terjadi setelah melakukan transaksi, membilas atau menutup sesi.

Perhatikan bahwa metode persist memiliki tipe pengembalian kosong . Ini beroperasi pada objek yang lewat "di tempat", mengubah statusnya. The orang variabel referensi aktual objek bertahan.

Metode ini merupakan tambahan selanjutnya untuk antarmuka Sesi. Fitur pembeda utama dari metode ini adalah bahwa metode ini sesuai dengan spesifikasi JSR-220 (persistensi EJB). Semantik metode ini didefinisikan secara ketat dalam spesifikasi, yang pada dasarnya menyatakan, bahwa:

  • sebuah instance sementara menjadi persisten (dan operasi mengalir ke semua hubungannya dengan cascade = PERSIST atau cascade = ALL ),
  • jika sebuah instance sudah persisten , maka panggilan ini tidak berpengaruh untuk instance khusus ini (tetapi masih mengalir ke hubungannya dengan cascade = PERSIST atau cascade = ALL ),
  • jika sebuah instance dilepas , Anda harus mengharapkan pengecualian, baik saat memanggil metode ini, atau saat melakukan atau menghapus sesi.

Perhatikan bahwa tidak ada apa pun di sini yang menyangkut pengenal sebuah instance. Spesifikasi tidak menyatakan bahwa id akan langsung dibuat, terlepas dari strategi pembuatan id. Spesifikasi untuk metode persist memungkinkan implementasi untuk mengeluarkan pernyataan untuk menghasilkan id pada commit atau flush, dan id tidak dijamin menjadi non-null setelah memanggil metode ini, jadi Anda tidak boleh mengandalkannya.

You may call this method on an already persistent instance, and nothing happens. But if you try to persist a detached instance, the implementation is bound to throw an exception. In the following example we persist the entity, evict it from the context so it becomes detached, and then try to persist again. The second call to session.persist() causes an exception, so the following code will not work:

Person person = new Person(); person.setName("John"); session.persist(person); session.evict(person); session.persist(person); // PersistenceException!

3.2. Save

The save method is an “original” Hibernate method that does not conform to the JPA specification.

Its purpose is basically the same as persist, but it has different implementation details. The documentation for this method strictly states that it persists the instance, “first assigning a generated identifier”. The method is guaranteed to return the Serializable value of this identifier.

Person person = new Person(); person.setName("John"); Long id = (Long) session.save(person);

The effect of saving an already persisted instance is the same as with persist. Difference comes when you try to save a detached instance:

Person person = new Person(); person.setName("John"); Long id1 = (Long) session.save(person); session.evict(person); Long id2 = (Long) session.save(person);

The id2 variable will differ from id1. The call of save on a detached instance creates a new persistent instance and assigns it a new identifier, which results in a duplicate record in a database upon committing or flushing.

3.3. Merge

The main intention of the merge method is to update a persistent entity instance with new field values from a detached entity instance.

For instance, suppose you have a RESTful interface with a method for retrieving an JSON-serialized object by its id to the caller and a method that receives an updated version of this object from the caller. An entity that passed through such serialization/deserialization will appear in a detached state.

After deserializing this entity instance, you need to get a persistent entity instance from a persistence context and update its fields with new values from this detached instance. So the merge method does exactly that:

  • finds an entity instance by id taken from the passed object (either an existing entity instance from the persistence context is retrieved, or a new instance loaded from the database);
  • copies fields from the passed object to this instance;
  • returns newly updated instance.

In the following example we evict (detach) the saved entity from context, change the name field, and then merge the detached entity.

Person person = new Person(); person.setName("John"); session.save(person); session.evict(person); person.setName("Mary"); Person mergedPerson = (Person) session.merge(person);

Note that the merge method returns an object — it is the mergedPerson object that was loaded into persistence context and updated, not the person object that you passed as an argument. Those are two different objects, and the person object usually needs to be discarded (anyway, don't count on it being attached to persistence context).

As with persist method, the merge method is specified by JSR-220 to have certain semantics that you can rely upon:

  • if the entity is detached, it is copied upon an existing persistent entity;
  • if the entity is transient, it is copied upon a newly created persistent entity;
  • this operation cascades for all relations with cascade=MERGE or cascade=ALL mapping;
  • if the entity is persistent, then this method call does not have effect on it (but the cascading still takes place).

3.4. Update

As with persist and save, the update method is an “original” Hibernate method that was present long before the merge method was added. Its semantics differs in several key points:

  • it acts upon passed object (its return type is void); the update method transitions the passed object from detached to persistent state;
  • this method throws an exception if you pass it a transient entity.

In the following example we save the object, then evict (detach) it from the context, then change its name and call update. Notice that we don't put the result of the update operation in a separate variable, because the update takes place on the person object itself. Basically we're reattaching the existing entity instance to the persistence context — something the JPA specification does not allow us to do.

Person person = new Person(); person.setName("John"); session.save(person); session.evict(person); person.setName("Mary"); session.update(person);

Trying to call update on a transient instance will result in an exception. The following will not work:

Person person = new Person(); person.setName("John"); session.update(person); // PersistenceException!

3.5. SaveOrUpdate

This method appears only in the Hibernate API and does not have its standardized counterpart. Similar to update, it also may be used for reattaching instances.

Actually, the internal DefaultUpdateEventListener class that processes the update method is a subclass of DefaultSaveOrUpdateListener, just overriding some functionality. The main difference of saveOrUpdate method is that it does not throw exception when applied to a transient instance; instead, it makes this transient instance persistent. The following code will persist a newly created instance of Person:

Person person = new Person(); person.setName("John"); session.saveOrUpdate(person);

You may think of this method as a universal tool for making an object persistent regardless of its state wether it is transient or detached.

4. What to Use?

If you don't have any special requirements, as a rule of thumb, you should stick to the persist and merge methods, because they are standardized and guaranteed to conform to the JPA specification.

They are also portable in case you decide to switch to another persistence provider, but they may sometimes appear not so useful as the “original” Hibernate methods, save, update and saveOrUpdate.

5. Conclusion

Kami telah membahas tujuan berbagai metode Sesi Hibernasi dalam kaitannya dengan mengelola entitas persisten dalam runtime. Kami telah mempelajari bagaimana metode ini mentransisikan instance entitas melalui daur hidupnya dan mengapa beberapa metode ini memiliki fungsi duplikat.

Kode sumber untuk artikel tersebut tersedia di GitHub.