Fill in the TODO sections for the Locking and Caching chapters
This commit is contained in:
parent
f58c8eba9c
commit
d769308785
|
@ -55,6 +55,7 @@ dependencies {
|
|||
|
||||
testCompile( project(':hibernate-core') )
|
||||
testCompile( project(':hibernate-entitymanager') )
|
||||
testCompile( project(':hibernate-ehcache') )
|
||||
|
||||
testCompile( project(':hibernate-testing') )
|
||||
testCompile( project(path: ':hibernate-entitymanager', configuration: 'tests') )
|
||||
|
|
|
@ -1,81 +1,571 @@
|
|||
[[caching]]
|
||||
== Caching
|
||||
:sourcedir: ../../../../../test/java/org/hibernate/jpa/test/userguide/caching
|
||||
|
||||
At runtime, Hibernate handles moving data into and out of the second-level cache in response to the operations performed by the `Session`, which acts as a transaction-level cache of persistent data.
|
||||
Once an entity becomes managed, that object is added to the internal cache of the current persistence context (`EntityManager` or `Session`).
|
||||
The persistence context is also called the first-level cache, and it's enabled by default.
|
||||
|
||||
It is possible to configure a JVM-level (`SessionFactory`-level) or even a cluster cache on a class-by-class and collection-by-collection basis.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Be aware that caches are not aware of changes made to the persistent store by another applications.
|
||||
They can, however, be configured to regularly expire cached data.
|
||||
====
|
||||
|
||||
[[caching-config]]
|
||||
=== Configuring second-level caching
|
||||
|
||||
Hibernate defines the ability to integrate with pluggable providers for the purpose of caching data outside the context of a particular `Session`.
|
||||
This section defines the settings which control that behavior.
|
||||
Hibernate can integrate with various caching providers for the purpose of caching data outside the context of a particular `Session`.
|
||||
This section defines the settings which control this behavior.
|
||||
|
||||
[[caching-config-provider]]
|
||||
=== RegionFactory
|
||||
==== RegionFactory
|
||||
|
||||
`org.hibernate.cache.spi.RegionFactory` defines the integration between Hibernate and a pluggable caching provider.
|
||||
`hibernate.cache.region.factory_class` is used to declare the provider to use. Hibernate comes with support for 2 popular caching libraries: Ehcache and Infinispan.
|
||||
`hibernate.cache.region.factory_class` is used to declare the provider to use.
|
||||
Hibernate comes with built-in support for two popular caching libraries: http://www.ehcache.org/[Ehcache] and http://infinispan.org/[Infinispan].
|
||||
|
||||
[[caching-config-provider-ehcache]]
|
||||
=== Ehcache
|
||||
===== Ehcache
|
||||
|
||||
[IMPORTANT]
|
||||
[NOTE]
|
||||
====
|
||||
Use of the build-in integration for Ehcache requires that the hibernate-ehcache module jar (and all of its dependencies) are on the classpath.
|
||||
Use of the build-in integration for Ehcache requires that the `hibernate-ehcache` module jar (and all of its dependencies) are on the classpath.
|
||||
====
|
||||
|
||||
The hibernate-ehcache module defines 2 specific region factories: `EhCacheRegionFactory` and `SingletonEhCacheRegionFactory`.
|
||||
The hibernate-ehcache module defines two specific region factories: `EhCacheRegionFactory` and `SingletonEhCacheRegionFactory`.
|
||||
|
||||
[[caching-config-provider-ehcache-region-factory]]
|
||||
==== `EhCacheRegionFactory`
|
||||
====== `EhCacheRegionFactory`
|
||||
|
||||
TODO
|
||||
To use the `EhCacheRegionFactory`, you need to specify the following configuration property:
|
||||
|
||||
[[caching-config-provider-ehcache-region-factory-example]]
|
||||
.`EhCacheRegionFactory` configuration
|
||||
====
|
||||
[source, XML, indent=0]
|
||||
----
|
||||
<property
|
||||
name="hibernate.cache.region.factory_class"
|
||||
value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
|
||||
----
|
||||
====
|
||||
|
||||
The `EhCacheRegionFactory` configures a `net.sf.ehcache.CacheManager` for each `SessionFactory`,
|
||||
so the `CacheManager` is not shared among multiple `SessionFactory` instances in the same JVM.
|
||||
|
||||
[[caching-config-provider-ehcache-singleton-region-factory]]
|
||||
==== `SingletonEhCacheRegionFactory`
|
||||
====== `SingletonEhCacheRegionFactory`
|
||||
|
||||
TODO
|
||||
To use the `SingletonEhCacheRegionFactory`, you need to specify the following configuration property:
|
||||
|
||||
[[caching-config-provider-ehcache-singleton-region-factory-example]]
|
||||
.`SingletonEhCacheRegionFactory` configuration
|
||||
====
|
||||
[source, XML, indent=0]
|
||||
----
|
||||
<property
|
||||
name="hibernate.cache.region.factory_class"
|
||||
value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"/>
|
||||
----
|
||||
====
|
||||
|
||||
The `SingletonEhCacheRegionFactory` configures a singleton `net.sf.ehcache.CacheManager` (see http://www.ehcache.org/apidocs/2.8.4/net/sf/ehcache/CacheManager.html#create%28%29[CacheManager#create()]),
|
||||
shared among multiple `SessionFactory` instances in the same JVM.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
http://www.ehcache.org/documentation/2.8/integrations/hibernate#optional[Ehcache documentation] recommends using multiple non-singleton `CacheManager(s)` when there are multiple Hibernate `SessionFactory` instances running in the same JVM.
|
||||
====
|
||||
|
||||
[[caching-config-provider-infinispan]]
|
||||
=== Infinispan
|
||||
===== Infinispan
|
||||
|
||||
[IMPORTANT]
|
||||
[NOTE]
|
||||
====
|
||||
Use of the build-in integration for Infinispan requires that the hibernate-infinispan module jar (and all of its dependencies) are on the classpath.
|
||||
Use of the build-in integration for Infinispan requires that the `hibernate-infinispan module` jar (and all of its dependencies) are on the classpath.
|
||||
====
|
||||
|
||||
The hibernate-infinispan module defines 2 specific providers: `infinispan` and `infinispan-jndi`.
|
||||
The hibernate-infinispan module defines two specific providers: `infinispan` and `infinispan-jndi`.
|
||||
|
||||
TODO
|
||||
[[caching-config-provider-infinispan-region-factory]]
|
||||
===== `InfinispanRegionFactory`
|
||||
|
||||
[[caching-config-behavior]]
|
||||
=== Caching behavior
|
||||
If Hibernate and Infinispan are running in a standalone environment, the `InfinispanRegionFactory` should be configured as follows:
|
||||
|
||||
Besides specific provider configuration, there are a number of configurations options on the Hibernate side of the integration that control various caching behavior:
|
||||
[[caching-config-provider-infinispan-region-factory-example]]
|
||||
.`InfinispanRegionFactory` configuration
|
||||
====
|
||||
[source, XML, indent=0]
|
||||
----
|
||||
<property
|
||||
name="hibernate.cache.region.factory_class"
|
||||
value="org.hibernate.cache.infinispan.InfinispanRegionFactory" />
|
||||
----
|
||||
====
|
||||
|
||||
`hibernate.cache.use_second_level_cache`:: Enable or disable second level caching overall. Default is true, although the default region factory is `NoCachingRegionFactory`.
|
||||
`hibernate.cache.use_query_cache`:: Enable or disable second level caching of query results. Default is false.
|
||||
`hibernate.cache.query_cache_factory`:: Query result caching is handled by a special contract that deals with staleness-based invalidation of the results.
|
||||
The default implementation does not allow stale results at all. Use this for applications that would like to relax that.
|
||||
Names an implementation of `org.hibernate.cache.spi.QueryCacheFactory`
|
||||
`hibernate.cache.use_minimal_puts`:: Optimizes second-level cache operations to minimize writes, at the cost of more frequent reads. Providers typically set this appropriately.
|
||||
`hibernate.cache.region_prefix`:: Defines a name to be used as a prefix to all second-level cache region names.
|
||||
`hibernate.cache.default_cache_concurrency_strategy`:: In Hibernate second-level caching, all regions can be configured differently including the concurrency strategy to use when accessing the region.
|
||||
This setting allows to define a default strategy to be used.
|
||||
This setting is very rarely required as the pluggable providers do specify the default strategy to use.
|
||||
Valid values include:
|
||||
[[caching-config-provider-infinispan-jndi-region-factory]]
|
||||
===== `JndiInfinispanRegionFactory`
|
||||
|
||||
If the Infinispan `CacheManager` is bound to JNDI, then the `JndiInfinispanRegionFactory` should be used as a region factory:
|
||||
|
||||
[[caching-config-provider-infinispan-jndi-region-factory-example]]
|
||||
.`JndiInfinispanRegionFactory` configuration
|
||||
====
|
||||
[source, XML, indent=0]
|
||||
----
|
||||
<property
|
||||
name="hibernate.cache.region.factory_class"
|
||||
value="org.hibernate.cache.infinispan.JndiInfinispanRegionFactory" />
|
||||
|
||||
<property
|
||||
name="hibernate.cache.infinispan.cachemanager"
|
||||
value="java:CacheManager" />
|
||||
----
|
||||
====
|
||||
|
||||
For more information about Infinispan, see the http://infinispan.org/docs/8.0.x/user_guide/user_guide.html#_using_infinispan_as_jpa_hibernate_second_level_cache_provider[reference documentation].
|
||||
|
||||
[[caching-config-properties]]
|
||||
==== Caching configuration properties
|
||||
|
||||
Besides specific provider configuration, there are a number of configurations options on the Hibernate side of the integration that control various caching behaviors:
|
||||
|
||||
`hibernate.cache.use_second_level_cache`::
|
||||
Enable or disable second level caching overall. Default is true, although the default region factory is `NoCachingRegionFactory`.
|
||||
`hibernate.cache.use_query_cache`::
|
||||
Enable or disable second level caching of query results. Default is false.
|
||||
`hibernate.cache.query_cache_factory`::
|
||||
Query result caching is handled by a special contract that deals with staleness-based invalidation of the results.
|
||||
The default implementation does not allow stale results at all. Use this for applications that would like to relax that.
|
||||
Names an implementation of `org.hibernate.cache.spi.QueryCacheFactory`
|
||||
`hibernate.cache.use_minimal_puts`::
|
||||
Optimizes second-level cache operations to minimize writes, at the cost of more frequent reads. Providers typically set this appropriately.
|
||||
`hibernate.cache.region_prefix`::
|
||||
Defines a name to be used as a prefix to all second-level cache region names.
|
||||
`hibernate.cache.default_cache_concurrency_strategy`::
|
||||
In Hibernate second-level caching, all regions can be configured differently including the concurrency strategy to use when accessing that particular region.
|
||||
This setting allows to define a default strategy to be used.
|
||||
This setting is very rarely required as the pluggable providers do specify the default strategy to use.
|
||||
Valid values include:
|
||||
* read-only,
|
||||
* read-write,
|
||||
* nonstrict-read-write,
|
||||
* transactional
|
||||
hibernate.cache.use_structured_entries:: If `true`, forces Hibernate to store data in the second-level cache in a more human-friendly format.
|
||||
Can be useful if you'd like to be able to "browse" the data directly in your cache, but does have a performance impact.
|
||||
* hibernate.cache.auto_evict_collection_cache:: Enables or disables the automatic eviction of a bidirectional association's collection cache entry when the association is changed just from the owning side.
|
||||
This is disabled by default, as it has a performance impact to track this state.
|
||||
However if your application does not manage both sides of bidirectional association where the collection side is cached, the alternative is to have stale data in that collection cache.
|
||||
`hibernate.cache.use_structured_entries`::
|
||||
If `true`, forces Hibernate to store data in the second-level cache in a more human-friendly format.
|
||||
Can be useful if you'd like to be able to "browse" the data directly in your cache, but does have a performance impact.
|
||||
`hibernate.cache.auto_evict_collection_cache`::
|
||||
Enables or disables the automatic eviction of a bidirectional association's collection cache entry when the association is changed just from the owning side.
|
||||
This is disabled by default, as it has a performance impact to track this state.
|
||||
However if your application does not manage both sides of bidirectional association where the collection side is cached,
|
||||
the alternative is to have stale data in that collection cache.
|
||||
`hibernate.cache.use_reference_entries`::
|
||||
Enable direct storage of entity references into the second level cache for read-only or immutable entities.
|
||||
|
||||
[[caching-mappings]]
|
||||
=== Configuring second-level cache mappings
|
||||
|
||||
The cache mappings can be configured via JPA annotations or XML descriptors or using the Hibernate-specific mapping files.
|
||||
|
||||
By default, entities are not part of the second level cache and we recommend you to stick to this setting.
|
||||
However, you can override this by setting the `shared-cache-mode` element in your `persistence.xml` file
|
||||
or by using the `javax.persistence.sharedCache.mode` property in your configuration file.
|
||||
The following values are possible:
|
||||
|
||||
`ENABLE_SELECTIVE` (Default and recommended value)::
|
||||
Entities are not cached unless explicitly marked as cacheable (with the https://docs.oracle.com/javaee/7/api/javax/persistence/Cacheable.html[`@Cacheable`] annotation).
|
||||
`DISABLE_SELECTIVE`:
|
||||
Entities are cached unless explicitly marked as not cacheable.
|
||||
`ALL`:
|
||||
Entities are always cached even if marked as non cacheable.
|
||||
`NONE`:
|
||||
No entity is cached even if marked as cacheable.
|
||||
This option can make sense to disable second-level cache altogether.
|
||||
|
||||
The cache concurrency strategy used by default can be set globally via the `hibernate.cache.default_cache_concurrency_strategy` configuration property.
|
||||
The values for this property are:
|
||||
|
||||
read-only::
|
||||
If your application needs to read, but not modify, instances of a persistent class, a read-only cache is the best choice.
|
||||
This is the simplest and optimal performing strategy.
|
||||
It is even safe for use in a cluster.
|
||||
read-write::
|
||||
If the application needs to update data, a read-write cache might be appropriate.
|
||||
This cache strategy should never be used if serializable transaction isolation level is required.
|
||||
If the cache is used in a JTA environment, you must specify the `hibernate.transaction.jta.platform` property.
|
||||
In other environments, you should ensure that the transaction is completed when `Session.close()` or `Session.disconnect()` is called.
|
||||
If you want to use this strategy in a cluster, you should ensure that the underlying cache implementation supports locking.
|
||||
nonstrict-read-write::
|
||||
If the application only occasionally needs to update data
|
||||
(e.g. if it is extremely unlikely that two transactions would try to update the same item simultaneously)
|
||||
and strict transaction isolation is not required, a nonstrict-read-write cache might be appropriate.
|
||||
If the cache is used in a JTA environment, you must specify the `hibernate.transaction.jta.platform` property.
|
||||
In other environments, you should ensure that the transaction is completed when `Session.close()` or `Session.disconnect()` is called.
|
||||
transactional::
|
||||
The transactional cache strategy provides support for fully transactional cache providers (e.g. Ehcache, Infinispan).
|
||||
Such a cache can only be used in a JTA environment and you must specify `hibernate.transaction.jta.platform`.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Rather than using a global cache concurrency strategy, it is recommended to define this setting on a per entity basis.
|
||||
Use the https://docs.jboss.org/hibernate/stable/orm/javadocs/org/hibernate/annotations/Cache.html[`@org.hibernate.annotations.Cache`] annotation for that.
|
||||
====
|
||||
|
||||
The `@Cache` annotation define three attributes:
|
||||
|
||||
usage::
|
||||
Defines the `CacheConcurrencyStrategy`
|
||||
region::
|
||||
Defines a cache region where entries will be stored
|
||||
include::
|
||||
If lazy properties should be included in the second level cache.
|
||||
Default value is "all", so lazy properties are cacheable.
|
||||
The other possible value is "non-lazy", so lazy properties are not cacheable.
|
||||
|
||||
[[caching-query]]
|
||||
=== Entity cache
|
||||
|
||||
[[caching-entity-mapping-example]]
|
||||
.Entity cache mapping
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/NonStrictReadWriteCacheTest.java[tags=caching-entity-mapping-example]
|
||||
----
|
||||
====
|
||||
|
||||
Hibernate stores cached entities in a dehydrated forms, which is similar to the database representation.
|
||||
Aside from the foreign key column values of the `@ManyToOne` or `@OneToOne` child-side associations,
|
||||
entity relationships are not stored in the cache,
|
||||
|
||||
Once an entity is stored in the second-level cache, you can avoid a database hit and load the entity from the cache alone:
|
||||
|
||||
[[caching-entity-jpa-example]]
|
||||
.Loading entity using JPA
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-entity-jpa-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[caching-entity-native-example]]
|
||||
.Loading entity using Hibernate native API
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-entity-native-example]
|
||||
----
|
||||
====
|
||||
|
||||
The Hibernate second-level cache can also load entities by their <<chapters/domain/natural_id.adoc#naturalid,natural id>>:
|
||||
|
||||
[[caching-entity-natural-id-mapping-example]]
|
||||
.Hibernate natural id entity mapping
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-entity-natural-id-mapping-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[caching-entity-natural-id-example]]
|
||||
.Loading entity using Hibernate native natural id API
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-entity-natural-id-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[caching-collection]]
|
||||
=== Collection cache
|
||||
|
||||
Hibernate can also cache collections, and the `@Cache` annotation must be on added to the collection property.
|
||||
|
||||
If the collection is made of value types (basic or embeddables mapped with `@ElementCollection`),
|
||||
the collection is stored as such.
|
||||
If the collection contains other entities (`@OneToMany` or `@ManyToMany`),
|
||||
the collection cache entry will store the entity identifiers only.
|
||||
|
||||
[[caching-collection-mapping-example]]
|
||||
.Collection cache mapping
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/NonStrictReadWriteCacheTest.java[tags=caching-collection-mapping-example]
|
||||
----
|
||||
====
|
||||
|
||||
Collections are read-through, meaning they are cached upon being accessed for the first time:
|
||||
|
||||
[[caching-collection-example]]
|
||||
.Collection cache usage
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/NonStrictReadWriteCacheTest.java[tags=caching-collection-example]
|
||||
----
|
||||
====
|
||||
|
||||
Subsequent collection retrievals will use the cache instead of going to the database.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
The collection cache is not write-through, so any modification will trigger a collection cache entry invalidation.
|
||||
On a subsequent access, the collection will be loaded from the database and re-cached.
|
||||
====
|
||||
|
||||
[[caching-query]]
|
||||
=== Query cache
|
||||
|
||||
Aside from caching entities and collections, Hibernate offers a query cache too.
|
||||
This is useful for frequently executed queries with fixed parameter values.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Caching of query results introduces some overhead in terms of your applications normal transactional processing.
|
||||
For example, if you cache results of a query against `Person`,
|
||||
Hibernate will need to keep track of when those results should be invalidated because changes have been committed against any `Person` entity.
|
||||
|
||||
That, coupled with the fact that most applications simply gain no benefit from caching query results,
|
||||
leads Hibernate to disable caching of query results by default.
|
||||
====
|
||||
|
||||
To use query caching, you will first need to enable it with the following configuration property:
|
||||
|
||||
[[caching-query-configuration]]
|
||||
.Enabling query cache
|
||||
====
|
||||
[source, XML, indent=0]
|
||||
----
|
||||
<property
|
||||
name="hibernate.cache.use_query_cache"
|
||||
value="true" />
|
||||
----
|
||||
====
|
||||
|
||||
As mentioned above, most queries do not benefit from caching or their results.
|
||||
So by default, individual queries are not cached even after enabling query caching.
|
||||
Each particular query that needs to be cached must be manually set as cacheable.
|
||||
This way, the query looks for existing cache results or adds the query results to the cache when being executed.
|
||||
|
||||
[[caching-query-jpa-example]]
|
||||
.Caching query using JPA
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-query-jpa-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[caching-query-native-example]]
|
||||
.Caching query using Hibernate native API
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-query-native-example]
|
||||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
The query cache does not cache the state of the actual entities in the cache;
|
||||
it caches only identifier values and results of value type.
|
||||
|
||||
Just as with collection caching, the query cache should always be used in conjunction with the second-level cache for those entities expected to be cached as part of a query result cache.
|
||||
====
|
||||
|
||||
[[caching-query-region]]
|
||||
==== Query cache regions
|
||||
|
||||
This setting creates two new cache regions:
|
||||
|
||||
`org.hibernate.cache.internal.StandardQueryCache`::
|
||||
Holding the cached query results
|
||||
`org.hibernate.cache.spi.UpdateTimestampsCache`::
|
||||
Holding timestamps of the most recent updates to queryable tables.
|
||||
These are used to validate the results as they are served from the query cache.
|
||||
|
||||
[IMPORTANT]
|
||||
====
|
||||
If you configure your underlying cache implementation to use expiry or timeouts,
|
||||
it's very important that the cache timeout of the underlying cache region for the `UpdateTimestampsCache` be set to a higher value than the timeouts of any of the query caches.
|
||||
|
||||
In fact, we recommend that the `UpdateTimestampsCache` region not be configured for expiry at all.
|
||||
Note that an LRU (Least Recently Used) cache expiry policy is never appropriate for this particular cache region.
|
||||
====
|
||||
|
||||
If you require fine-grained control over query cache expiration policies,
|
||||
you can specify a named cache region for a particular query.
|
||||
|
||||
[[caching-query-region-jpa-example]]
|
||||
.Caching query in custom region using JPA
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-query-region-jpa-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[caching-query-region-native-example]]
|
||||
.Caching query in custom region using Hibernate native API
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-query-region-native-example]
|
||||
----
|
||||
====
|
||||
|
||||
If you want to force the query cache to refresh one of its regions (disregarding any cached results it finds there),
|
||||
you can use custom cache modes.
|
||||
|
||||
[[caching-query-region-store-mode-jpa-example]]
|
||||
.Using custom query cache mode with JPA
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-query-region-store-mode-jpa-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[caching-query-region-native-example]]
|
||||
.Using custom query cache mode with Hibernate native API
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-query-region-store-mode-native-example]
|
||||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
When using http://docs.oracle.com/javaee/7/api/javax/persistence/CacheStoreMode.html#REFRESH[`CacheStoreMode.REFRESH`] or https://docs.jboss.org/hibernate/stable/orm/javadocs/org/hibernate/CacheMode.html#REFRESH[`CacheMode.REFRESH`] in conjunction with the region you have defined for the given query,
|
||||
Hibernate will selectively force the results cached in that particular region to be refreshed.
|
||||
|
||||
This is particularly useful in cases where underlying data may have been updated via a separate process
|
||||
and is a far more efficient alternative to bulk eviction of the region via `SessionFactory` eviction which looks as follows:
|
||||
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-query-region-native-evict-example]
|
||||
----
|
||||
|
||||
====
|
||||
|
||||
[[caching-management]]
|
||||
=== Managing the Cached Data
|
||||
=== Managing the cached data
|
||||
|
||||
At runtime Hibernate handles moving data into and out of the second-level cache in response to the operations performed by the `Session`.
|
||||
Traditionally, Hibernate defined the https://docs.jboss.org/hibernate/stable/orm/javadocs/org/hibernate/CacheMode.html[`CacheMode`] enumeration to describe
|
||||
the ways of interactions with the cached data.
|
||||
JPA split cache modes by storage (http://docs.oracle.com/javaee/7/api/javax/persistence/CacheStoreMode.html[`CacheStoreMode`])
|
||||
and retrieval (http://docs.oracle.com/javaee/7/api/javax/persistence/CacheRetrieveMode.html[`CacheRetrieveMode`]).
|
||||
|
||||
The `org.hibernate.Cache` interface (or the `javax.persistence.Cache` interface if using JPA) allow to clear data from the second-level cache.
|
||||
The relationship between Hibernate and JPA cache modes can be seen in the following table:
|
||||
|
||||
TODO
|
||||
.Cache modes relationships
|
||||
[cols=",,,",options="header",]
|
||||
|======================================
|
||||
|Hibernate | JPA | Description
|
||||
|`CacheMode.NORMAL` |`CacheStoreMode.USE` and `CacheRetrieveMode.USE` | Default. Reads/writes data from/into cache
|
||||
|`CacheMode.REFRESH` |`CacheStoreMode.REFRESH` and `CacheRetrieveMode.BYPASS` | Doesn't read from cache, but writes to the cache upon loading from the database
|
||||
|`CacheMode.PUT` |`CacheStoreMode.USE` and `CacheRetrieveMode.BYPASS` | Doesn't read from cache, but writes to the cache as it reads from the database
|
||||
|`CacheMode.GET` |`CacheStoreMode.BYPASS` and `CacheRetrieveMode.USE` | Read from the cache, but doesn't write to cache
|
||||
|`CacheMode.IGNORE` |`CacheStoreMode.BYPASS` and `CacheRetrieveMode.BYPASS` | Doesn't read/write data from/into cache
|
||||
|======================================
|
||||
|
||||
Setting the cache mode can be done wither when loading entities directly or when executing a query.
|
||||
|
||||
[[caching-management-cache-mode-entity-jpa-example]]
|
||||
.Using custom cache modes with JPA
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-management-cache-mode-entity-jpa-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[caching-management-cache-mode-entity-native-example]]
|
||||
.Using custom cache modes wit Hibernate native API
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-management-cache-mode-entity-native-example]
|
||||
----
|
||||
====
|
||||
|
||||
The custom cache modes can be set for queries as well:
|
||||
|
||||
[[caching-management-cache-mode-query-jpa-example]]
|
||||
.Using custom cache modes for queries with JPA
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-management-cache-mode-query-jpa-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[caching-management-cache-mode-query-native-example]]
|
||||
.Using custom cache modes for queries with Hibernate native API
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-management-cache-mode-query-native-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[caching-management-evict]]
|
||||
==== Evicting cache entries
|
||||
|
||||
Because the second level cache is bound to the `EntityManagerFactory` or the `SessionFactory`,
|
||||
cache eviction must be done through these two interfaces.
|
||||
|
||||
JPA only supports entity eviction through the https://docs.oracle.com/javaee/7/api/javax/persistence/Cache.html[`javax.persistence.Cache`] interface:
|
||||
|
||||
[[caching-management-evict-jpa-example]]
|
||||
.Evicting entities with JPA
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-management-evict-jpa-example]
|
||||
----
|
||||
====
|
||||
|
||||
Hibernate is much more flexible in this regard as it offers a fine-grained control over what needs to be evicted.
|
||||
The https://docs.jboss.org/hibernate/stable/orm/javadocs/org/hibernate/Cache.html[`org.hibernate.Cache`] interface defines various evicting strategies:
|
||||
|
||||
- entities (by their class or region)
|
||||
- entities stored using the natural-id (by their class or region)
|
||||
- collections (by the region, and it might take the collection owner identifier as well)
|
||||
- queries (by region)
|
||||
|
||||
[[caching-management-evict-native-example]]
|
||||
.Evicting entities with Hibernate native API
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-management-evict-native-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[caching-statistics]]
|
||||
=== Caching statistics
|
||||
|
||||
If you enable the `hibernate.generate_statistics` configuration property,
|
||||
Hibernate will expose a number of metrics via `SessionFactory.getStatistics()`.
|
||||
Hibernate can even be configured to expose these statistics via JMX.
|
||||
|
||||
This way, you can get access to the https://docs.jboss.org/hibernate/stable/orm/javadocs/org/hibernate/stat/Statistics.html[`Statistics`] class which comprises all sort of
|
||||
second-level cache metrics.
|
||||
|
||||
[[caching-statistics-example]]
|
||||
.Caching statistics
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-statistics-example]
|
||||
----
|
||||
====
|
|
@ -127,6 +127,7 @@ Others may also be unsafe for locking, because of lack of precision.
|
|||
|generated |Whether the timestamp property value is generated by the database. Optional, defaults to `never`.
|
||||
|=======================================================================
|
||||
|
||||
[[locking-pessimistic]]
|
||||
=== Pessimistic
|
||||
|
||||
Typically, you only need to specify an isolation level for the JDBC connections and let the database handle locking issues.
|
||||
|
@ -137,6 +138,7 @@ If you do need to obtain exclusive pessimistic locks or re-obtain locks at the s
|
|||
Hibernate always uses the locking mechanism of the database, and never lock objects in memory.
|
||||
====
|
||||
|
||||
[[locking-LockMode]]
|
||||
=== The `LockMode` class
|
||||
|
||||
The `LockMode` class defines the different lock levels that Hibernate can acquire.
|
||||
|
@ -168,4 +170,58 @@ In the case of `UPGRADE`, `UPGRADE_NOWAIT` or `UPGRADE_SKIPLOCKED`, the `SELECT
|
|||
If the requested lock mode is not supported by the database, Hibernate uses an appropriate alternate mode instead of throwing an exception.
|
||||
This ensures that applications are portable.
|
||||
|
||||
TODO: write about buildLockRequest and LockOptions and more about JPA LockType
|
||||
[[locking-jpa-query-hints]]
|
||||
=== JPA locking query hints
|
||||
|
||||
JPA 2.0 introduced two query hints:
|
||||
|
||||
javax.persistence.lock.timeout:: it gives the number of milliseconds a lock acquisition request will wait before throwing an exception
|
||||
javax.persistence.lock.scope:: defines the http://docs.oracle.com/javaee/7/api/javax/persistence/PessimisticLockScope.html[_scope_] of the lock acquisition request.
|
||||
The scope can either be `NORMAL` (default value) or `EXTENDED`. The `EXTENDED` scope will cause a lock acquisition request to be passed to other owned table structured (e.g. `@Inheritance(strategy=InheritanceType.JOINED)`, `@ElementCollection`)
|
||||
|
||||
[[locking-jpa-query-hints-timeout-example]]
|
||||
.`javax.persistence.lock.timeout` example
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/../../../../../../test/java/org/hibernate/jpa/test/userguide/locking/ExplicitLockingTest.java[tags=locking-jpa-query-hints-timeout-example]
|
||||
----
|
||||
|
||||
[source, SQL, indent=0]
|
||||
----
|
||||
include::{sourcedir}/../../../../../../test/java/org/hibernate/jpa/test/userguide/locking/locking-jpa-query-hints-timeout-example.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Not all JDBC database drivers support setting a timeout value for a locking request.
|
||||
If not supported, the Hibernate dialect ignores this query hint.
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
The `javax.persistence.lock.scope` is https://hibernate.atlassian.net/browse/HHH-9636[not yet supported] as specified by the JPA standard.
|
||||
====
|
||||
|
||||
[[locking-buildLockRequest]]
|
||||
=== The `buildLockRequest` API
|
||||
|
||||
Traditionally, Hibernate offered the `Session#lock()` method for acquiring an optimistic or a pessimistic lock on a given entity.
|
||||
Because varying the locking options was difficult when using a single `LockMode` parameter, Hibernate has added the `Session#buildLockRequest()` method API.
|
||||
|
||||
The following example shows how to obtain shared database lock without waiting for the lock acquisition request.
|
||||
|
||||
[[locking-buildLockRequest-example]]
|
||||
.`buildLockRequest` example
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/../../../../../../test/java/org/hibernate/jpa/test/userguide/locking/ExplicitLockingTest.java[tags=locking-buildLockRequest-example]
|
||||
----
|
||||
|
||||
[source, SQL, indent=0]
|
||||
----
|
||||
include::{sourcedir}/../../../../../../test/java/org/hibernate/jpa/test/userguide/locking/locking-buildLockRequest-example.sql[]
|
||||
----
|
||||
====
|
|
@ -15,6 +15,20 @@ It may or may not physically exist in the database yet.
|
|||
|
||||
Much of the `org.hibernate.Session` and `javax.persistence.EntityManager` methods deal with moving entities between these states.
|
||||
|
||||
=== Accessing Hibernate APIs from JPA
|
||||
|
||||
JPA defines an incredibly useful method to allow applications access to the APIs of the underlying provider.
|
||||
|
||||
.Accessing Hibernate APIs from JPA
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir}/UnwrapWithEM.java[]
|
||||
----
|
||||
====
|
||||
|
||||
include::BytecodeEnhancement.adoc[]
|
||||
|
||||
=== Making entities persistent
|
||||
|
||||
Once you've created a new entity instance (using the standard `new` operator) it is in `new` state.
|
||||
|
@ -291,16 +305,48 @@ include::{sourcedir}/CheckingLazinessWithJPA2.java[]
|
|||
----
|
||||
====
|
||||
|
||||
=== Accessing Hibernate APIs from JPA
|
||||
=== Evicting entities
|
||||
|
||||
JPA defines an incredibly useful method to allow applications access to the APIs of the underlying provider.
|
||||
When the `flush()` method is called, the state of the entity is synchronized with the database.
|
||||
If you do not want this synchronization to occur, or if you are processing a huge number of objects and need to manage memory efficiently,
|
||||
the `evict()` method can be used to remove the object and its collections from the first-level cache.
|
||||
|
||||
.Accessing Hibernate APIs from JPA
|
||||
[[caching-management-jpa-detach-example]]
|
||||
.Detaching an entity from the `EntityManager`
|
||||
====
|
||||
[source,java]
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/UnwrapWithEM.java[]
|
||||
include::{sourcedir}/../../../../../../test/java/org/hibernate/jpa/test/userguide/caching/FirstLevelCacheTest.java[tags=caching-management-jpa-detach-example]
|
||||
----
|
||||
====
|
||||
|
||||
include::BytecodeEnhancement.adoc[]
|
||||
[[caching-management-native-evict-example]]
|
||||
.Evicting an entity from the Hibernate `Session`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/../../../../../../test/java/org/hibernate/jpa/test/userguide/caching/FirstLevelCacheTest.java[tags=caching-management-native-evict-example]
|
||||
----
|
||||
====
|
||||
|
||||
To detach all entities from the current persistence context, both the `EntityManager` and the Hibernate `Session` define a `clear()` method.
|
||||
|
||||
[[caching-management-clear-example]]
|
||||
.Clearing the persistence context
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/../../../../../../test/java/org/hibernate/jpa/test/userguide/caching/FirstLevelCacheTest.java[tags=caching-management-clear-example]
|
||||
----
|
||||
====
|
||||
|
||||
To verify if an entity instance is currently attached to the running persistence context, both the `EntityManager` and the Hibernate `Session` define a `contains(Object entity)` method.
|
||||
|
||||
[[caching-management-contains-example]]
|
||||
.Verify if an entity is contained in a persistence context
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/../../../../../../test/java/org/hibernate/jpa/test/userguide/caching/FirstLevelCacheTest.java[tags=caching-management-contains-example]
|
||||
----
|
||||
====
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.jpa.test.userguide.caching;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.cache.ehcache.EhCacheRegionFactory;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
|
||||
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class FirstLevelCacheTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
private static final Logger log = Logger.getLogger( FirstLevelCacheTest.class );
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Person.class
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings( "unchecked" )
|
||||
protected void addConfigOptions(Map options) {
|
||||
options.put( AvailableSettings.USE_SECOND_LEVEL_CACHE, Boolean.TRUE.toString() );
|
||||
options.put( AvailableSettings.CACHE_REGION_FACTORY, EhCacheRegionFactory.class.getName() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCache() {
|
||||
Person aPerson = doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
entityManager.persist( new Person() );
|
||||
entityManager.persist( new Person() );
|
||||
Person person = new Person();
|
||||
entityManager.persist( person );
|
||||
return person;
|
||||
});
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
List<Object> dtos = new ArrayList<>( );
|
||||
//tag::caching-management-jpa-detach-example[]
|
||||
for(Person person : entityManager.createQuery("select p from Person p", Person.class)
|
||||
.getResultList()) {
|
||||
dtos.add(toDTO(person));
|
||||
entityManager.detach( person );
|
||||
}
|
||||
//end::caching-management-jpa-detach-example[]
|
||||
//tag::caching-management-clear-example[]
|
||||
entityManager.clear();
|
||||
|
||||
//end::caching-management-clear-example[]
|
||||
|
||||
Person person = aPerson;
|
||||
|
||||
//tag::caching-management-contains-example[]
|
||||
entityManager.contains( person );
|
||||
|
||||
//end::caching-management-contains-example[]
|
||||
});
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
List<Object> dtos = new ArrayList<>( );
|
||||
//tag::caching-management-native-evict-example[]
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
for(Person person : (List<Person>) session.createQuery("select p from Person p").list()) {
|
||||
dtos.add(toDTO(person));
|
||||
session.evict( person );
|
||||
}
|
||||
//end::caching-management-native-evict-example[]
|
||||
//tag::caching-management-clear-example[]
|
||||
session.clear();
|
||||
//end::caching-management-clear-example[]
|
||||
|
||||
Person person = aPerson;
|
||||
|
||||
//tag::caching-management-contains-example[]
|
||||
session.contains( person );
|
||||
//end::caching-management-contains-example[]
|
||||
});
|
||||
}
|
||||
|
||||
private Object toDTO(Person person) {
|
||||
return person;
|
||||
}
|
||||
|
||||
|
||||
@Entity(name = "Person")
|
||||
public static class Person {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
public Person() {
|
||||
}
|
||||
|
||||
public Person(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.jpa.test.userguide.caching;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Version;
|
||||
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
import org.hibernate.cache.ehcache.EhCacheRegionFactory;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
|
||||
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class NonStrictReadWriteCacheTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
private static final Logger log = Logger.getLogger( NonStrictReadWriteCacheTest.class );
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Person.class,
|
||||
Phone.class
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings( "unchecked" )
|
||||
protected void addConfigOptions(Map options) {
|
||||
options.put( AvailableSettings.USE_SECOND_LEVEL_CACHE, Boolean.TRUE.toString() );
|
||||
options.put( AvailableSettings.CACHE_REGION_FACTORY, EhCacheRegionFactory.class.getName() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCache() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Person person = new Person();
|
||||
entityManager.persist( person );
|
||||
Phone home = new Phone( "123-456-7890" );
|
||||
Phone office = new Phone( "098-765-4321" );
|
||||
person.addPhone( home );
|
||||
person.addPhone( office );
|
||||
});
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Person person = entityManager.find( Person.class, 1L );
|
||||
person.getPhones().size();
|
||||
});
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
log.info( "Log collection from cache" );
|
||||
//tag::caching-collection-example[]
|
||||
Person person = entityManager.find( Person.class, 1L );
|
||||
person.getPhones().size();
|
||||
//end::caching-collection-example[]
|
||||
});
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
log.info( "Load from cache" );
|
||||
entityManager.find( Person.class, 1L ).getPhones().size();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Entity
|
||||
@Cacheable
|
||||
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
|
||||
public static class Person {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
//tag::caching-collection-mapping-example[]
|
||||
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
|
||||
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
|
||||
private List<Phone> phones = new ArrayList<>( );
|
||||
//end::caching-collection-mapping-example[]
|
||||
|
||||
@Version
|
||||
private int version;
|
||||
|
||||
public Person() {}
|
||||
|
||||
public Person(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<Phone> getPhones() {
|
||||
return phones;
|
||||
}
|
||||
|
||||
public void addPhone(Phone phone) {
|
||||
phones.add( phone );
|
||||
phone.setPerson( this );
|
||||
}
|
||||
}
|
||||
|
||||
//tag::caching-entity-mapping-example[]
|
||||
@Entity
|
||||
@Cacheable
|
||||
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
|
||||
public static class Phone {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String mobile;
|
||||
|
||||
@ManyToOne
|
||||
private Person person;
|
||||
|
||||
@Version
|
||||
private int version;
|
||||
|
||||
public Phone() {}
|
||||
|
||||
public Phone(String mobile) {
|
||||
this.mobile = mobile;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getMobile() {
|
||||
return mobile;
|
||||
}
|
||||
|
||||
public Person getPerson() {
|
||||
return person;
|
||||
}
|
||||
|
||||
public void setPerson(Person person) {
|
||||
this.person = person;
|
||||
}
|
||||
}
|
||||
//end::caching-entity-mapping-example[]
|
||||
}
|
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.jpa.test.userguide.caching;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.persistence.CacheRetrieveMode;
|
||||
import javax.persistence.CacheStoreMode;
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.CacheMode;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.cache.ehcache.EhCacheRegionFactory;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.stat.SecondLevelCacheStatistics;
|
||||
import org.hibernate.stat.Statistics;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class SecondLevelCacheTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
private static final Logger log = Logger.getLogger( SecondLevelCacheTest.class );
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Person.class
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings( "unchecked" )
|
||||
protected void addConfigOptions(Map options) {
|
||||
options.put( AvailableSettings.USE_SECOND_LEVEL_CACHE, Boolean.TRUE.toString() );
|
||||
options.put( AvailableSettings.CACHE_REGION_FACTORY, EhCacheRegionFactory.class.getName() );
|
||||
options.put( AvailableSettings.USE_QUERY_CACHE, Boolean.TRUE.toString() );
|
||||
options.put( AvailableSettings.GENERATE_STATISTICS, Boolean.TRUE.toString() );
|
||||
options.put( AvailableSettings.CACHE_REGION_PREFIX, "" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCache() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
entityManager.persist( new Person() );
|
||||
entityManager.persist( new Person() );
|
||||
Person aPerson= new Person();
|
||||
aPerson.setName( "John Doe" );
|
||||
aPerson.setCode( "unique-code" );
|
||||
entityManager.persist( aPerson );
|
||||
return aPerson;
|
||||
});
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
log.info( "Jpa load by id" );
|
||||
//tag::caching-entity-jpa-example[]
|
||||
Person person = entityManager.find( Person.class, 1L );
|
||||
//end::caching-entity-jpa-example[]
|
||||
});
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
log.info( "Native load by id" );
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
//tag::caching-entity-native-example[]
|
||||
Person person = session.get( Person.class, 1L );
|
||||
//end::caching-entity-native-example[]
|
||||
});
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
log.info( "Native load by natural-id" );
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
//tag::caching-entity-natural-id-example[]
|
||||
Person person = session
|
||||
.byNaturalId( Person.class )
|
||||
.using( "code", "unique-code")
|
||||
.load();
|
||||
//end::caching-entity-natural-id-example[]
|
||||
assertNotNull(person);
|
||||
});
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
log.info( "Jpa query cache" );
|
||||
//tag::caching-query-jpa-example[]
|
||||
List<Person> persons = entityManager.createQuery(
|
||||
"select p " +
|
||||
"from Person p " +
|
||||
"where p.name = :name", Person.class)
|
||||
.setParameter( "name", "John Doe")
|
||||
.setHint( "org.hibernate.cacheable", "true")
|
||||
.getResultList();
|
||||
//end::caching-query-jpa-example[]
|
||||
});
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
log.info( "Native query cache" );
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
//tag::caching-query-native-example[]
|
||||
List<Person> persons = session.createQuery(
|
||||
"select p " +
|
||||
"from Person p " +
|
||||
"where p.name = :name")
|
||||
.setParameter( "name", "John Doe")
|
||||
.setCacheable(true)
|
||||
.list();
|
||||
//end::caching-query-native-example[]
|
||||
});
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
log.info( "Jpa query cache region" );
|
||||
//tag::caching-query-region-jpa-example[]
|
||||
List<Person> persons = entityManager.createQuery(
|
||||
"select p " +
|
||||
"from Person p " +
|
||||
"where p.id > :id", Person.class)
|
||||
.setParameter( "id", 0L)
|
||||
.setHint( "org.hibernate.cacheable", "true")
|
||||
.setHint( "org.hibernate.cacheRegion", "query.cache.person" )
|
||||
.getResultList();
|
||||
//end::caching-query-region-jpa-example[]
|
||||
});
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
log.info( "Native query cache" );
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
//tag::caching-query-region-native-example[]
|
||||
List<Person> persons = session.createQuery(
|
||||
"select p " +
|
||||
"from Person p " +
|
||||
"where p.id > :id")
|
||||
.setParameter( "id", 0L)
|
||||
.setCacheable(true)
|
||||
.setCacheRegion( "query.cache.person" )
|
||||
.list();
|
||||
//end::caching-query-region-native-example[]
|
||||
});
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
log.info( "Jpa query cache store mode " );
|
||||
//tag::caching-query-region-store-mode-jpa-example[]
|
||||
List<Person> persons = entityManager.createQuery(
|
||||
"select p " +
|
||||
"from Person p " +
|
||||
"where p.id > :id", Person.class)
|
||||
.setParameter( "id", 0L)
|
||||
.setHint( "org.hibernate.cacheable", "true")
|
||||
.setHint( "org.hibernate.cacheRegion", "query.cache.person" )
|
||||
.setHint( "javax.persistence.cache.storeMode", CacheStoreMode.REFRESH )
|
||||
.getResultList();
|
||||
//end::caching-query-region-store-mode-jpa-example[]
|
||||
});
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
log.info( "Native query cache store mode" );
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
//tag::caching-query-region-store-mode-native-example[]
|
||||
List<Person> persons = session.createQuery(
|
||||
"select p " +
|
||||
"from Person p " +
|
||||
"where p.id > :id")
|
||||
.setParameter( "id", 0L)
|
||||
.setCacheable(true)
|
||||
.setCacheRegion( "query.cache.person" )
|
||||
.setCacheMode( CacheMode.REFRESH )
|
||||
.list();
|
||||
//end::caching-query-region-store-mode-native-example[]
|
||||
});
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
//tag::caching-statistics-example[]
|
||||
Statistics statistics = session.getSessionFactory().getStatistics();
|
||||
SecondLevelCacheStatistics secondLevelCacheStatistics =
|
||||
statistics.getSecondLevelCacheStatistics( "query.cache.person" );
|
||||
long hitCount = secondLevelCacheStatistics.getHitCount();
|
||||
long missCount = secondLevelCacheStatistics.getMissCount();
|
||||
double hitRatio = (double) hitCount / ( hitCount + missCount );
|
||||
//end::caching-statistics-example[]
|
||||
return hitRatio;
|
||||
} );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
log.info( "Native query cache store mode" );
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
//tag::caching-query-region-native-evict-example[]
|
||||
session.getSessionFactory().getCache().evictQueryRegion( "query.cache.person" );
|
||||
//end::caching-query-region-native-evict-example[]
|
||||
});
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
//tag::caching-management-cache-mode-entity-jpa-example[]
|
||||
Map<String, Object> hints = new HashMap<>( );
|
||||
hints.put( "javax.persistence.cache.retrieveMode " , CacheRetrieveMode.USE );
|
||||
hints.put( "javax.persistence.cache.storeMode" , CacheStoreMode.REFRESH );
|
||||
Person person = entityManager.find( Person.class, 1L , hints);
|
||||
//end::caching-management-cache-mode-entity-jpa-example[]
|
||||
});
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
//tag::caching-management-cache-mode-entity-native-example[]
|
||||
session.setCacheMode( CacheMode.REFRESH );
|
||||
Person person = session.get( Person.class, 1L );
|
||||
//end::caching-management-cache-mode-entity-native-example[]
|
||||
});
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
//tag::caching-management-cache-mode-query-jpa-example[]
|
||||
List<Person> persons = entityManager.createQuery(
|
||||
"select p from Person p", Person.class)
|
||||
.setHint( "org.hibernate.cacheable", "true")
|
||||
.setHint( "javax.persistence.cache.retrieveMode " , CacheRetrieveMode.USE )
|
||||
.setHint( "javax.persistence.cache.storeMode" , CacheStoreMode.REFRESH )
|
||||
.getResultList();
|
||||
//end::caching-management-cache-mode-query-jpa-example[]
|
||||
|
||||
//tag::caching-management-evict-jpa-example[]
|
||||
entityManager.getEntityManagerFactory().getCache().evict( Person.class );
|
||||
//end::caching-management-evict-jpa-example[]
|
||||
});
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
//tag::caching-management-cache-mode-query-native-example[]
|
||||
List<Person> persons = session.createQuery(
|
||||
"select p from Person p" )
|
||||
.setCacheable( true )
|
||||
.setCacheMode( CacheMode.REFRESH )
|
||||
.list();
|
||||
//end::caching-management-cache-mode-query-native-example[]
|
||||
|
||||
//tag::caching-management-evict-native-example[]
|
||||
session.getSessionFactory().getCache().evictQueryRegion( "query.cache.person" );
|
||||
//end::caching-management-evict-native-example[]
|
||||
});
|
||||
}
|
||||
|
||||
//tag::caching-entity-natural-id-mapping-example[]
|
||||
@Entity(name = "Person")
|
||||
@Cacheable
|
||||
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
|
||||
public static class Person {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@NaturalId
|
||||
@Column(name = "code", unique = true)
|
||||
private String code;
|
||||
|
||||
public Person() {}
|
||||
|
||||
public Person(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
//end::caching-entity-natural-id-mapping-example[]
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
package org.hibernate.jpa.test.userguide.locking;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.LockModeType;
|
||||
import javax.persistence.PessimisticLockScope;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.hibernate.jpa.test.util.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* <code>BuildLockRequestTest</code> - Build Lock Request Test
|
||||
*
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class ExplicitLockingTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
private static final Logger log = Logger.getLogger( ExplicitLockingTest.class );
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Person.class,
|
||||
Phone.class,
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJPALockTimeout() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Person person = new Person( "John Doe" );
|
||||
entityManager.persist( person );
|
||||
} );
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
log.info( "testJPALockTimeout" );
|
||||
Long id = 1L;
|
||||
//tag::locking-jpa-query-hints-timeout-example[]
|
||||
entityManager.find(
|
||||
Person.class, id, LockModeType.PESSIMISTIC_WRITE,
|
||||
Collections.singletonMap( "javax.persistence.lock.timeout", 200 )
|
||||
);
|
||||
//end::locking-jpa-query-hints-timeout-example[]
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJPALockScope() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Person person = new Person( "John Doe" );
|
||||
entityManager.persist( person );
|
||||
Phone home = new Phone( "123-456-7890" );
|
||||
Phone office = new Phone( "098-765-4321" );
|
||||
person.getPhones().add( home );
|
||||
person.getPhones().add( office );
|
||||
entityManager.persist( person );
|
||||
} );
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
log.info( "testJPALockScope" );
|
||||
Long id = 1L;
|
||||
//tag::locking-jpa-query-hints-scope-example[]
|
||||
Person person = entityManager.find(
|
||||
Person.class, id, LockModeType.PESSIMISTIC_WRITE,
|
||||
Collections.singletonMap(
|
||||
"javax.persistence.lock.scope",
|
||||
PessimisticLockScope.EXTENDED )
|
||||
);
|
||||
//end::locking-jpa-query-hints-scope-example[]
|
||||
assertEquals( 2, person.getPhones().size() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildLockRequest() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
log.info( "testBuildlLockRequest" );
|
||||
Person person = new Person( "John Doe" );
|
||||
Phone home = new Phone( "123-456-7890" );
|
||||
Phone office = new Phone( "098-765-4321" );
|
||||
person.getPhones().add( home );
|
||||
person.getPhones().add( office );
|
||||
entityManager.persist( person );
|
||||
entityManager.flush();
|
||||
} );
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Long id = 1L;
|
||||
//tag::locking-buildLockRequest-example[]
|
||||
Person person = entityManager.find( Person.class, id );
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
session
|
||||
.buildLockRequest( LockOptions.NONE )
|
||||
.setLockMode( LockMode.PESSIMISTIC_READ )
|
||||
.setTimeOut( LockOptions.NO_WAIT )
|
||||
.lock( person );
|
||||
//end::locking-buildLockRequest-example[]
|
||||
} );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Long id = 1L;
|
||||
//tag::locking-buildLockRequest-scope-example[]
|
||||
Person person = entityManager.find( Person.class, id );
|
||||
Session session = entityManager.unwrap( Session.class );
|
||||
session
|
||||
.buildLockRequest( LockOptions.NONE )
|
||||
.setLockMode( LockMode.PESSIMISTIC_READ )
|
||||
.setTimeOut( LockOptions.NO_WAIT )
|
||||
.setScope( true )
|
||||
.lock( person );
|
||||
//end::locking-buildLockRequest-scope-example[]
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
//tag::locking-jpa-query-hints-scope-entity-example[]
|
||||
@Entity(name = "Person")
|
||||
public static class Person {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
@Column(name = "`name`")
|
||||
private String name;
|
||||
|
||||
@ElementCollection
|
||||
@JoinTable(name = "person_phone", joinColumns = @JoinColumn(name = "person_id"))
|
||||
private List<Phone> phones = new ArrayList<>();
|
||||
|
||||
public Person() {
|
||||
}
|
||||
|
||||
public Person(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<Phone> getPhones() {
|
||||
return phones;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Phone {
|
||||
|
||||
@Column
|
||||
private String mobile;
|
||||
|
||||
public Phone() {}
|
||||
|
||||
public Phone(String mobile) {
|
||||
this.mobile = mobile;
|
||||
}
|
||||
}
|
||||
//end::locking-jpa-query-hints-scope-entity-example[]
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
SELECT p.id AS id1_0_0_ ,
|
||||
p.name AS name2_0_0_
|
||||
FROM Person p
|
||||
WHERE p.id = 1
|
||||
|
||||
SELECT id
|
||||
FROM Person
|
||||
WHERE id = 1
|
||||
FOR SHARE NOWAIT
|
|
@ -0,0 +1,5 @@
|
|||
SELECT explicitlo0_.id AS id1_0_0_,
|
||||
explicitlo0_."name" AS name2_0_0_
|
||||
FROM person explicitlo0_
|
||||
WHERE explicitlo0_.id = 1
|
||||
FOR UPDATE wait 2
|
|
@ -0,0 +1,5 @@
|
|||
SELECT explicitlo0_.id AS id1_0_0_,
|
||||
explicitlo0_."name" AS name2_0_0_
|
||||
FROM person explicitlo0_
|
||||
WHERE explicitlo0_.id = 1
|
||||
FOR UPDATE wait 2
|
Loading…
Reference in New Issue