1. Gambaran keseluruhan
Salah satu kelebihan lapisan abstraksi pangkalan data seperti kerangka kerja ORM (pemetaan objek-hubungan) adalah kemampuan mereka untuk menyimpan data secara cache yang diambil dari kedai yang mendasari. Ini membantu menghilangkan kos akses pangkalan data untuk data yang sering diakses.
Keuntungan prestasi boleh menjadi ketara jika nisbah membaca / menulis kandungan yang dicache tinggi, terutama bagi entiti yang terdiri daripada grafik objek besar.
Dalam artikel ini kita meneroka Hibernate cache tahap kedua.
Kami menerangkan beberapa konsep asas dan seperti biasa kami menggambarkan semuanya dengan contoh mudah. Kami menggunakan JPA dan kembali ke Hibernate API asli hanya untuk ciri-ciri yang tidak diseragamkan dalam JPA.
2. Apakah Cache Tahap Kedua?
Seperti kebanyakan kerangka kerja ORM yang lengkap, Hibernate mempunyai konsep cache tahap pertama. Ini adalah cache scoped sesi yang memastikan bahawa setiap entiti entiti dimuat hanya sekali dalam konteks berterusan.
Setelah sesi ditutup, cache tahap pertama juga dihentikan. Ini sebenarnya wajar, kerana ia membolehkan sesi bersamaan berfungsi dengan keadaan entiti secara terpisah antara satu sama lain.
Sebaliknya, cache tahap kedua adalah SessionFactory -scoped, yang bermaksud ia dikongsi oleh semua sesi yang dibuat dengan kilang sesi yang sama. Apabila entiti entiti dicari oleh idnya (sama ada dengan logik aplikasi atau oleh Hibernate secara dalaman, misalnya ketika memuat perkaitan ke entiti itu dari entiti lain), dan jika cache tahap kedua diaktifkan untuk entiti tersebut, perkara berikut berlaku:
- Sekiranya contoh sudah ada di cache tahap pertama, ia dikembalikan dari situ
- Sekiranya instance tidak dijumpai di cache tingkat pertama, dan keadaan instance yang sesuai disimpan di cache tingkat kedua, maka data diambil dari sana dan instance dipasang dan dikembalikan
- Jika tidak, data yang diperlukan dimuat dari pangkalan data dan contoh dipasang dan dikembalikan
Setelah instance disimpan dalam konteks kegigihan (cache tahap pertama), ia dikembalikan dari sana dalam semua panggilan berikutnya dalam sesi yang sama sehingga sesi ditutup atau kejadian diusir secara manual dari konteks kegigihan. Juga, keadaan instance yang dimuat disimpan dalam cache L2 jika belum ada.
3. Kilang Wilayah
Cache tahap kedua hibernate dirancang untuk tidak menyedari penyedia cache sebenar yang digunakan. Hibernate hanya perlu diberikan pelaksanaan antara muka org.hibernate.cache.spi.RegionFactory yang merangkumi semua butiran khusus untuk penyedia cache sebenar. Pada dasarnya, ia berfungsi sebagai penghubung antara penyedia Hibernate dan cache.
Dalam artikel ini kami menggunakan Ehcache sebagai penyedia cache , yang merupakan cache yang matang dan banyak digunakan. Anda boleh memilih penyedia lain tentu saja, selagi ada pelaksanaan RegionFactory untuknya.
Kami menambah pelaksanaan kilang wilayah Ehcache ke classpath dengan ketergantungan Maven berikut:
org.hibernate hibernate-ehcache 5.2.2.Final
Lihat di sini untuk versi terbaru hibernate-ehcache . Namun, pastikan bahawa versi hibernate-ehcache sama dengan versi Hibernate yang anda gunakan dalam projek anda, misalnya jika anda menggunakan hibernate-ehcache 5.2.2 . Akhir seperti dalam contoh ini, maka versi Hibernate juga harus 5.2.2. Akhir .
The pendam-ehcache artifak mempunyai pergantungan kepada pelaksanaan Ehcache itu sendiri, yang dengan itu transitively termasuk dalam classpath juga.
4. Mengaktifkan Caching Tahap Kedua
Dengan dua sifat berikut, kami memberitahu Hibernate bahawa cache L2 diaktifkan dan kami memberikannya nama kelas kilang wilayah:
hibernate.cache.use_second_level_cache=true hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory
Contohnya, dalam persistence.xml kelihatan seperti:
... ...
Untuk melumpuhkan cache tahap kedua (misalnya untuk tujuan penyahpepijatan), tetapkan sifat hibernate.cache.use_second_level_cache ke false.
5. Menjadikan Entiti Cacheable
Untuk menjadikan entiti layak untuk cache tahap kedua , kami memberi anotasinya dengan Hibernate @ org.hibernate.annotations khusus. Anotasi cache dan tentukan strategi bersamaan cache.
Beberapa pembangun menganggap bahawa adalah konvensyen yang baik untuk menambahkan @ javax.persistence standard. Anotasi yang boleh disimpan juga (walaupun tidak diperlukan oleh Hibernate), jadi pelaksanaan kelas entiti mungkin kelihatan seperti ini:
@Entity @Cacheable @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE) public class Foo { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "ID") private long id; @Column(name = "NAME") private String name; // getters and setters }
Untuk setiap kelas entiti, Hibernate akan menggunakan wilayah cache yang berasingan untuk menyimpan keadaan keadaan untuk kelas tersebut. Nama wilayah adalah nama kelas yang berkelayakan sepenuhnya.
Contohnya, contoh Foo disimpan dalam cache bernama com.baeldung.hibernate.cache.model.Foo di Ehcache.
Untuk mengesahkan bahawa caching berfungsi, kami mungkin menulis ujian pantas seperti ini:
Foo foo = new Foo(); fooService.create(foo); fooService.findOne(foo.getId()); int size = CacheManager.ALL_CACHE_MANAGERS.get(0) .getCache("com.baeldung.hibernate.cache.model.Foo").getSize(); assertThat(size, greaterThan(0));
Di sini kami menggunakan Ehcache API secara langsung untuk mengesahkan bahawa com.baeldung.hibernate.cache.model.Foo cache tidak kosong setelah kami memuatkan instance Foo .
Anda juga boleh mengaktifkan log SQL yang dihasilkan oleh Hibernate dan memanggil fooService.findOne (foo.getId ()) berkali-kali dalam ujian untuk mengesahkan bahawa pernyataan pilih untuk memuatkan Foo dicetak hanya sekali (pertama kali), yang bermaksud bahawa selepas memanggil entiti entiti diambil dari cache.
6. Strategi Penyesuaian Cache
Berdasarkan kes penggunaan, kami bebas memilih salah satu strategi serentak cache berikut:
- READ_ONLY : Digunakan hanya untuk entiti yang tidak pernah berubah (pengecualian dilemparkan jika percubaan untuk mengemas kini entiti tersebut dibuat). Ia sangat sederhana dan berprestasi. Sangat sesuai untuk beberapa data rujukan statik yang tidak berubah
- NONSTRICT_READ_WRITE : Cache dikemas kini setelah transaksi yang mengubah data yang terlibat telah dilakukan. Oleh itu, konsistensi yang kuat tidak dijamin dan terdapat jangka masa kecil di mana data basi dapat diperoleh dari cache. Strategi semacam ini sesuai untuk kes penggunaan yang boleh bertolak ansur dengan konsistensi akhirnya
- READ_WRITE: This strategy guarantees strong consistency which it achieves by using ‘soft' locks: When a cached entity is updated, a soft lock is stored in the cache for that entity as well, which is released after the transaction is committed. All concurrent transactions that access soft-locked entries will fetch the corresponding data directly from database
- TRANSACTIONAL: Cache changes are done in distributed XA transactions. A change in a cached entity is either committed or rolled back in both database and cache in the same XA transaction
7. Cache Management
If expiration and eviction policies are not defined, the cache could grow indefinitely and eventually consume all of available memory. In most cases, Hibernate leaves cache management duties like these to cache providers, as they are indeed specific to each cache implementation.
For example, we could define the following Ehcache configuration to limit the maximum number of cached Foo instances to 1000:
8. Collection Cache
Collections are not cached by default, and we need to explicitly mark them as cacheable. For example:
@Entity @Cacheable @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE) public class Foo { ... @Cacheable @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE) @OneToMany private Collection bars; // getters and setters }
9. Internal Representation of Cached State
Entities are not stored in second-level cache as Java instances, but rather in their disassembled (hydrated) state:
- Id (primary key) is not stored (it is stored as part of the cache key)
- Transient properties are not stored
- Collections are not stored (see below for more details)
- Non-association property values are stored in their original form
- Only id (foreign key) is stored for ToOne associations
This depicts general Hibernate second-level cache design in which cache model reflects the underlying relational model, which is space-efficient and makes it easy to keep the two synchronized.
9.1. Internal Representation of Cached Collections
We already mentioned that we have to explicitly indicate that a collection (OneToMany or ManyToMany association) is cacheable, otherwise it is not cached.
Actually, Hibernate stores collections in separate cache regions, one for each collection. The region name is a fully qualified class name plus the name of collection property, for example: com.baeldung.hibernate.cache.model.Foo.bars. This gives us the flexibility to define separate cache parameters for collections, e.g. eviction/expiration policy.
Also, it is important to mention that only ids of entities contained in a collection are cached for each collection entry, which means that in most cases it is a good idea to make the contained entities cacheable as well.
10. Cache Invalidation for HQL DML-Style Queries and Native Queries
When it comes to DML-style HQL (insert, update and delete HQL statements), Hibernate is able to determine which entities are affected by such operations:
entityManager.createQuery("update Foo set … where …").executeUpdate();
In this case all Foo instances are evicted from L2 cache, while other cached content remains unchanged.
However, when it comes to native SQL DML statements, Hibernate cannot guess what is being updated, so it invalidates the entire second level cache:
session.createNativeQuery("update FOO set … where …").executeUpdate();
This is probably not what you want! The solution is to tell Hibernate which entities are affected by native DML statements, so that it can evict only entries related to Foo entities:
Query nativeQuery = entityManager.createNativeQuery("update FOO set ... where ..."); nativeQuery.unwrap(org.hibernate.SQLQuery.class).addSynchronizedEntityClass(Foo.class); nativeQuery.executeUpdate();
We have too fall back to Hibernate native SQLQuery API, as this feature is not (yet) defined in JPA.
Note that the above applies only to DML statements (insert, update, delete and native function/procedure calls). Native select queries do not invalidate cache.
11. Query Cache
Results of HQL queries can also be cached. This is useful if you frequently execute a query on entities that rarely change.
To enable query cache, set the value of hibernate.cache.use_query_cache property to true:
hibernate.cache.use_query_cache=true
Then, for each query you have to explicitly indicate that the query is cacheable (via an org.hibernate.cacheable query hint):
entityManager.createQuery("select f from Foo f") .setHint("org.hibernate.cacheable", true) .getResultList();
11.1. Query Cache Best Practices
Here are a some guidelines and best practices related to query caching:
- As is case with collections, only ids of entities returned as a result of a cacheable query are cached, so it is strongly recommended that second-level cache is enabled for such entities.
- There is one cache entry per each combination of query parameter values (bind variables) for each query, so queries for which you expect lots of different combinations of parameter values are not good candidates for caching.
- Queries that involve entity classes for which there are frequent changes in the database are not good candidates for caching either, because they will be invalidated whenever there is a change related to any of the entity classed participating in the query, regardless whether the changed instances are cached as part of the query result or not.
- By default, all query cache results are stored in org.hibernate.cache.internal.StandardQueryCache region. As with entity/collection caching, you can customize cache parameters for this region to define eviction and expiration policies according to your needs. For each query you can also specify a custom region name in order to provide different settings for different queries.
- For all tables that are queried as part of cacheable queries, Hibernate keeps last update timestamps in a separate region named org.hibernate.cache.spi.UpdateTimestampsCache. Being aware of this region is very important if you use query caching, because Hibernate uses it to verify that cached query results are not stale. The entries in this cache must not be evicted/expired as long as there are cached query results for the corresponding tables in query results regions. It is best to turn off automatic eviction and expiration for this cache region, as it does not consume lots of memory anyway.
12. Conclusion
Dalam artikel ini, kami melihat cara mengatur cache tahap kedua Hibernate. Kami melihat bahawa cukup mudah untuk dikonfigurasi dan digunakan, kerana Hibernate melakukan semua pengangkatan berat di belakang tabir menjadikan penggunaan cache tahap kedua telus kepada logik perniagaan aplikasi.
Pelaksanaan Tutorial Cache Tahap Kedua Hibernate ini terdapat di Github. Ini adalah projek berasaskan Maven, jadi mudah diimport dan dijalankan sebagaimana adanya.