Hibernate: simpan, bertahan, kemas kini, gabungkan, saveOrUpdate

1. Pengenalan

Dalam artikel ini kita akan membincangkan perbezaan antara beberapa kaedah antara muka Sesi : simpan , bertahan , kemas kini , gabungkan , saveOrUpdate .

Ini bukan pengenalan kepada Hibernate dan anda semestinya sudah mengetahui asas-asas konfigurasi, pemetaan hubungan objek dan bekerja dengan contoh entiti. Untuk artikel pengenalan kepada Hibernate, kunjungi tutorial kami mengenai Hibernate 4 with Spring.

2. Sesi sebagai Pelaksanaan Konteks Kegigihan

The Sesi muka mempunyai beberapa kaedah yang akhirnya menyebabkan menyimpan data kepada pangkalan data: berterusan , menyimpan , maklumat , merge , saveOrUpdate . Untuk memahami perbezaan antara kaedah ini, kita mesti terlebih dahulu membincangkan tujuan Sesi sebagai konteks kegigihan dan perbezaan antara keadaan keadaan entiti yang berkaitan dengan Sesi .

Kita juga harus memahami sejarah perkembangan Hibernate yang menyebabkan beberapa kaedah API digandakan.

2.1. Menguruskan Contoh Entiti

Selain dari pemetaan objek-relasional itu sendiri, salah satu masalah yang ingin diselesaikan oleh Hibernate adalah masalah menguruskan entiti semasa menjalankan. Pengertian "konteks kegigihan" adalah penyelesaian Hibernate untuk masalah ini. Konteks kegigihan dapat dianggap sebagai wadah atau cache tingkat pertama untuk semua objek yang anda muatkan atau simpan ke pangkalan data semasa sesi.

Sesi itu adalah transaksi yang logik, batas mana yang ditentukan oleh logik perniagaan aplikasi anda. Apabila anda bekerja dengan pangkalan data melalui konteks kegigihan, dan semua keadaan entiti anda dilampirkan pada konteks ini, anda harus selalu mempunyai satu entiti tunggal untuk setiap rekod pangkalan data yang anda telah berinteraksi selama sesi tersebut.

Di Hibernate, konteks kegigihan diwakili oleh contoh org.hibernate.Session . Untuk JPA, ia adalah javax.persistence.EntityManager . Apabila kita menggunakan Hibernate sebagai penyedia JPA dan beroperasi melalui antara muka EntityManager , pelaksanaan antara muka ini pada dasarnya membungkus objek Sesi yang mendasari . Walau bagaimanapun, Hibernate Session menyediakan antara muka yang lebih kaya dengan lebih banyak kemungkinan sehingga kadang-kadang berguna untuk bekerja dengan Sesi secara langsung .

2.2. Contoh Keadaan Entiti

Segala entiti entiti dalam aplikasi anda muncul di salah satu daripada tiga keadaan utama berkaitan dengan konteks kegigihan Sesi :

  • sementara - contoh ini tidak, dan tidak pernah, dilampirkan pada Sesi ; contoh ini tidak mempunyai baris yang sesuai dalam pangkalan data; biasanya hanya objek baru yang telah anda buat untuk disimpan ke pangkalan data;
  • berterusan - contoh ini dikaitkan dengan objek Sesi yang unik ; semasa memasukkan Sesi ke pangkalan data, entiti ini dijamin mempunyai rekod yang konsisten dalam pangkalan data;
  • terpisah - contoh ini pernah dilampirkan ke Sesi (dalam keadaan berterusan ), tetapi sekarang tidak; contoh memasuki keadaan ini jika anda mengusirnya dari konteks, membersihkan atau menutup Sesi, atau memasukkan contoh melalui proses serialisasi / deserialisasi.

Berikut adalah rajah keadaan yang dipermudahkan dengan komen mengenai kaedah Sesi yang menjadikan peralihan keadaan berlaku.

Apabila entiti entiti berada dalam keadaan berterusan , semua perubahan yang Anda buat pada medan yang dipetakan dari contoh ini akan diterapkan pada catatan dan bidang pangkalan data yang sesuai setelah pembuangan Sesi . Yang berterusan misalnya boleh dianggap sebagai "online", manakala berkembar misalnya telah pergi "offline" dan tidak dipantau untuk perubahan.

Ini bermakna bahawa apabila anda menukar bidang objek yang berterusan , anda tidak perlu memanggil simpan , kemas kini atau salah satu kaedah tersebut untuk mendapatkan perubahan ini ke pangkalan data: yang anda perlukan hanyalah melakukan transaksi, atau tutup atau tutup sesi , apabila anda selesai dengannya.

2.3. Kesesuaian dengan Spesifikasi JPA

Hibernate adalah pelaksanaan ORM Java yang paling berjaya. Tidak hairanlah bahawa spesifikasi API kegigihan Java (JPA) sangat dipengaruhi oleh Hibernate API. Malangnya, terdapat juga banyak perbezaan: ada yang utama, ada yang lebih halus.

Untuk bertindak sebagai pelaksanaan standard JPA, Hibernate API harus disemak semula. Beberapa kaedah ditambahkan ke antara muka Sesi agar sesuai dengan antara muka EntityManager. Kaedah ini mempunyai tujuan yang sama dengan kaedah "asli", tetapi sesuai dengan spesifikasi dan dengan demikian mempunyai beberapa perbezaan.

3. Perbezaan Antara Operasi

Penting untuk difahami sejak awal bahawa semua kaedah ( berterusan , simpan , kemas kini , gabungkan , saveOrUpdate ) tidak langsung menghasilkan pernyataan SQL UPDATE atau INSERT yang sesuai . Penyimpanan data yang sebenarnya ke pangkalan data berlaku semasa melakukan transaksi atau pembuangan Sesi .

Kaedah yang disebutkan pada dasarnya menguruskan keadaan keadaan entiti dengan mengalihkannya antara keadaan yang berlainan sepanjang kitaran hidup.

Sebagai entiti contoh, kami akan menggunakan Orang entiti yang dipetakan anotasi sederhana :

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

3.1. Berterusan

Yang berterusan kaedah bertujuan untuk menambah entiti contoh baru dengan konteks kegigihan, iaitu peralihan contoh dari fana untuk berterusan negeri.

Kami biasanya memanggilnya ketika kami ingin menambahkan catatan ke pangkalan data (meneruskan entiti entiti):

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

Apa yang berlaku selepas kaedah berterusan dipanggil? The orang objek telah beralih daripada fana untuk berterusan negeri. Objek ini berada dalam konteks kegigihan sekarang, tetapi belum disimpan ke pangkalan data. Penjanaan penyataan INSERT hanya akan berlaku setelah melakukan transaksi, memusingkan atau menutup sesi.

Perhatikan bahawa kaedah berterusan mempunyai jenis pengembalian yang tidak sah . Ia beroperasi pada objek yang dilalui "di tempat", mengubah keadaannya. The orang rujukan pembolehubah objek berterusan sebenar.

Kaedah ini adalah tambahan kemudian pada antara muka Sesi. Ciri pembezaan utama kaedah ini adalah sesuai dengan spesifikasi JSR-220 (kegigihan EJB). Semantik kaedah ini ditakrifkan dengan ketat dalam spesifikasi, yang pada dasarnya menyatakan, bahawa:

  • yang fana contoh menjadi berterusan (dan lata operasi untuk semua hubungan dengan = lata berterusan atau lata = ALL ),
  • jika suatu kejadian sudah berterusan , maka panggilan ini tidak berpengaruh untuk kejadian tertentu ini (tetapi ia tetap berkaitan dengan hubungannya dengan lata = PERSIST atau lata = SEMUA ),
  • if an instance is detached, you should expect an exception, either upon calling this method, or upon committing or flushing the session.

Notice that there is nothing here that concerns the identifier of an instance. The spec does not state that the id will be generated right away, regardless of the id generation strategy. The specification for the persist method allows the implementation to issue statements for generating id on commit or flush, and the id is not guaranteed to be non-null after calling this method, so you should not rely upon it.

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 membincangkan tujuan kaedah Sesi Hibernate yang berbeza berkaitan dengan menguruskan entiti berterusan dalam jangka masa. Kami telah mengetahui bagaimana kaedah ini transistiti entiti melalui kitaran hayatnya dan mengapa sebilangan kaedah ini mempunyai fungsi pendua.

Kod sumber untuk artikel tersebut terdapat di GitHub.