Hibernate has two kinds
of cache, which are the first-level cache and the second-level cache. We also
call the first-leve cache session cache, and the second-level cache
process-level cache. The first-level cache is mandatory and can’t be turned
off; However, the second-level cache in Hibernate is optional. Because the
process-level cache caches objects across sessions and has process or cluster
scope, in some situation, turning on the process-level cache can improve the
performance of the application. In fact, before accessing the database to load
an object, Hibernate will first look in the Session cache and then in the
process-level cache.
If you have queries that
run over and over, with the same parameters, query caching provides performance
gains. Caching introduces overhead in
the area of transactional processing. For example, if you cache results of a
query against an object, Hibernate needs to keep track of whether any changes
have been committed against the object, and invalidate the cache accordingly.
In addition, the benefit from caching query results is limited, and highly
dependent on the usage patterns of your application. For these reasons,
Hibernate disables the query cache by default.
Enabling the query cache
1.
Set the hibernate.cache.use_query_cache
property to true.This setting creates two new cache regions:
org.hibernate.cache.internal.StandardQueryCache
holds the cached query results.
org.hibernate.cache.spi.UpdateTimestampsCache
holds timestamps of the most recent updates to queryable tables. These
timestamps validate results served from
the query cache.
2.
Adjust the cache timeout of the underlying
cache region
If you configure your
underlying cache implementation to use expiry or timeouts, set the cache
timeout of the underlying cache region for the UpdateTimestampsCache to a
higher value than the timeouts of any of the query caches. It is possible, and
recommended, to set the UpdateTimestampsCache region never to expire. To be
specific, a LRU (Least Recently Used) cache expiry policy is never appropriate.
3. Enable results caching for specific queries
Since most queries do
not benefit from caching of their results, you need to enable caching for
individual queries, e ven after enabling query caching overall. To enable
results caching for a particular query, call
org.hibernate.Query.setCacheable(true). This call allows the query to look for
existing cache results or add its results to the cache when it is executed.
The query cache does not
cache the state of the actual entities in the cache. It caches identifier
values and results of value type. Therefore, always use the query cache in
conjunction with the second-level cache for those entities which should be
cached as part of a query result cache.
Query cache regions
For fine-grained control
over query cache expiration policies, specify a named cache region for a
particular query by calling Query.setCacheRegion().
Session session = SessionFactory.openSession();
Query query = session.createQuery("FROM EMPLOYEE");
query.setCacheable(true);
query.setCacheRegion("employee");
List users = query.list();
SessionFactory.closeSession();
To force the query cache
to refresh one of its regions and disregard any cached results in the region,
call org.hibernate.Query.setCacheMode(CacheMode.REFRESH). In conjunction with
the region defined for the given query, Hibernate selectively refreshes the
results cached in that particular region. This is much more efficient than bulk
eviction of the region via org.hibernate.SessionFactory.evictQueries().
Second-level cache(process
scoped cache)
A hibernate SessionFactory aims to create sessions, initializes
JDBC connections and pool them. The second level cache lives in the
SessionFactory level so all session can share it. This is why it’s called a
process scoped cache. The second level cache is not enabled by default. One has
to configure the cache strategy for hibernate entities and the cache provider.
By default, entities are
not part of the second-level cache, and their use is not recommended. If you
absolutely must use entities, set the shared-cache-mode element in
persistence.xml, or use property javax.persistence.sharedCache.mode in your
configuration.
Possible values for Shared Cache Mode
ENABLE_SELECTIVE: Entities are not
cached unless you explicitly mark them as cacheable. This is the default and
recommended value.
DISABLE_SELECTIVE: Entities are cached
unless you explicitly mark them as not cacheable.
ALL: All entities are always cached even if you
mark them as not cacheable.
NONE: No entities are cached
even if you mark them as cacheable. This option basically disables second-level
caching.
Caching strategies
read-only : A read-only cache is
good for data that needs to be read often but not modified. It is simple,
performs well, and is safe to use in a clustered environment.
nonstrict read-write :Some applications only
rarely need to modify data. This is the case if two transactions are unlikely
to try to update the same item simultaneously. In this case, you do not need
strict transaction isolation, and a nonstrict-read-write cache might be
appropriate. If the cache is used in a JTA environment, you must specify hibernate.transaction.manager_lookup_class.
In other environments, ensore that the transaction is complete before you call
Session.close() or Session.disconnect().
read-write :A read-write cache is
appropriate for an application which needs to update data regularly. Do not use
a read-write strategy if you need serializable transaction isolation. In a JTA
environment, specify a strategy for obtaining the JTA TransactionManager by
setting the property hibernate.transaction.manager_lookup_class. In non-JTA
environments, be sure the transaction is complete before you call
Session.close() or Session.disconnect().
To use the read-write
strategy in a clustered environment, the underlying cache implementation must
support locking. The build-in cache providers do not support locking.
transactional :The transactional cache
strategy provides support for transactional cache providers such as JBoss
TreeCache. You can only use such a cache in a JTA environment, and you must
first specify hibernate.transaction.manager_lookup_class.
Second-level cache
providers for Hibernate Cache Interface
Supported strategies
Strategy/Provider
|
Read-only
|
Nonstrictread-write
|
Read-write
|
Transactional
|
EHCache
|
X
|
X
|
X
|
|
OSCache
|
X
|
X
|
X
|
|
SwarmCache
|
X
|
X
|
||
JBoss Cache
|
X
|
X
|
||
HashTable (testing only)
|
X
|
X
|
X
|
Configuring cache
providers
You will specify a cache
provider in hibernate.cfg.xml configuration file. We choose EHCache as our
second-level cache provider:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- Assume
students is the database name -->
<property name="hibernate.connection.url">
jdbc:mysql://localhost/test
</property>
<property name="hibernate.connection.username">
root
</property>
<property name="hibernate.connection.password">
root123
</property>
<property name="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
<!-- List of XML
mapping files -->
<mapping resource="Employee.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Now, you need to specify
the properties of the cache regions. EHCache has its own configuration file,ehcache.xml,
which should be in the CLASSPATH of the application. A cache configuration in
ehcache.xml for the Employee class may look like this:
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
<cache name="Employee"
maxElementsInMemory="500"
eternal="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="false"
/>
Using annotations
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Forest { ... }
Using <cache> element in hbm.xml
file
<cache
usage="transactional"
region="RegionName"
include="all"
/>
Usage:The caching strategy.
This attribute is required, and can be any of the following values.
o transactional
o read-write
o nonstrict-read-write
o read-only
Region: The name of the
second-level cache region. This optional attribute defaults to the class or
collection role name.
Include: Whether properties of
the entity mapped with lazy=true can be cached when attribute-level
lazy fetching is enabled. Defaults to all and can also
be non-lazy.
Instead
of <cache>, you can
use <class-cache> and <collection-cache> elements
inhibernate.cfg.xml.
Managing the caches
Whenever
you pass an object to
save()
, update()
or saveOrUpdate()
,
and whenever you retrieve an object using load()
, get()
, list()
, iterate()
or scroll()
,
that object is added to the internal cache of the Session
.
When
flush()
is subsequently called, the state of
that object will be 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.ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result set
while ( cats.next() ) {
Cat cat = (Cat) cats.get(0);
doSomethingWithACat(cat);
sess.evict(cat);
}
The
Session
also provides a contains()
method to determine if an instance
belongs to the session cache.
To
evict all objects from the session cache, call
Session.clear()
For
the second-level cache, there are methods defined on
SessionFactory
for evicting the cached state of an
instance, entire class, collection instance or entire collection role.sessionFactory.evict(Cat.class, catId); //evict a particular Cat
sessionFactory.evict(Cat.class); //evict all Cats
sessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittens
sessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections
The
CacheMode
controls how a particular session
interacts with the second-level cache:
o
CacheMode.NORMAL
: will
read items from and write items to the second-level cache
o
CacheMode.GET
: will
read items from the second-level cache. Do not write to the second-level cache
except when updating data
o
CacheMode.PUT
: will
write items to the second-level cache. Do not read from the second-level cache
o
CacheMode.REFRESH
: will
write items to the second-level cache. Do not read from the second-level cache.
Bypass the effect of hibernate.cache.use_minimal_puts
forcing a refresh of the second-level
cache for all items read from the database
To
browse the contents of a second-level or query cache region, use the
Statistics
API:Map cacheEntries = sessionFactory.getStatistics()
.getSecondLevelCacheStatistics(regionName)
.getEntries();
You
will need to enable statistics and, optionally, force Hibernate to keep the
cache entries in a more readable format:
hibernate.generate_statistics true
hibernate.cache.use_structured_entries true
Session Cache
Fist
level cache in
hibernate is enabled by default and you do not need to do anything to get this
functionality working. In fact, you can not disable it even forcefully. it
is associated with Session object. As we know session object is created on
demand from session factory and it is lost, once the session is closed.
Similarly, first level cache associated with session object is available only
till session object is live. It is available to session object only and is not
accessible to any other session object in any other part of
application.
Important facts
1. First level cache is associated with
“session” object and other session objects in application can not see it.
2. The scope of cache objects is of
session. Once session is closed, cached objects are gone forever.
3. First level cache is enabled by
default and you can not disable it.
4. When we query an entity first time,
it is retrieved from database and stored in first level cache associated with
hibernate session.
5. If we query same object again with
same session object, it will be loaded from cache and no sql query will be
executed.
6. The loaded entity can be removed
from session using evict() method. The next loading of this entity will again
make a database call if it has been removed using evict() method.
7. The whole session cache can be
removed using clear() method. It will remove all the entities stored in cache. evict() is used to remove a particular object
from cache associated with session, and clear() method is used to remove all cached objects associated with
session.
No comments:
Post a Comment