Reproduced:
Many people do not understand the level two cache, or have the wrong understanding, I have been trying to write an article about hibernate two cache, and today finally can not help.
My experience is mainly from the hibernate2.1 version, the basic principle and 3.0, 3.1 is the same, please forgive my stubborn.
Hibernate session provides a first level cache, each session, the same ID two times load, will not send two SQL to the database, but when the session is closed, the primary cache is invalidated.
The second level cache is the sessionfactory level of the global cache, it can be used under different cache class libraries, such as Ehcache, Oscache, etc., need to set up hibernate.cache.provider_class, we use ehcache here, In 2.1, it's
Hibernate.cache.provider_class=net.sf.hibernate.cache.ehcacheprovider
If you use query caching, add
Hibernate.cache.use_query_cache=true
The cache can simply be viewed as a map, with key in the cache to find value.
class's Cache
For a record, that is, a PO, is based on the ID to find, the cache key is Id,value is Pojo. Either List,load or iterate, the cache is populated whenever an object is read. But the list will not use the cache, and iterate will fetch the database select ID First, then an ID of the load, if there is in the cache, from the cache, no words go to the database load. Suppose it is a read-write cache that needs to be set:
<cache usage= "Read-write"/>
If you are using a level two cache implementation that is Ehcache, you need to configure Ehcache.xml
<cache name= "Com.xxx.pojo.Foo" maxelementsinmemory= "$" eternal= "false" timetoliveseconds= "7200" timetoidleseconds= "3600" overflowtodisk= "true"/>
Where eternal indicates that the cache is never timed out, Timetoliveseconds is the timeout for each element in the cache (this is a pojo), and if the eternal= "false" exceeds the specified time, the element is removed. Timetoidleseconds is a daze time, is optional. If you put more than 500 elements into the cache, if you overflowtodisk= "true", some of the data in the cache will be stored in a temporary file on your hard disk.
This is configured for each class that needs to be cached. If you are not configured, Hibernate warns you when it is started and then uses the Defaultcache configuration so that multiple classes share a configuration.
When an ID is modified by hibernate, Hibernate will know and then remove the cache.
So you might think, the same query condition, first list, the second time iterate, you can use to cache. In fact, it is difficult, because you can not determine when the first time, and the condition of each query is usually not the same, if there are 100 records in the database, ID from 1 to 100, the first list of the time out of the top 50 ID, the second time iterate query to 30 to 70th ID , then 30-50 is taken from the cache, 51 to 70 is taken from the database, a total of 1+20 SQL sent. So I always think iterate is useless, there will always be 1+n problem.
(digression: It is said that large queries with list will put the entire result set into memory, very slow, and iterate only select id better, but large queries are always paged, who will not really put the entire result set, if a page 20, Iterate total need to execute 21 statements, list although select a number of fields, than iterate the first select ID statement slower, but only one statement, do not load the entire result set hibernate will also be based on the database dialect optimization, such as the use of MySQL limit, Overall it should be a quick list. )
If you want to cache the results of a list or iterate query, you need to use the query cache.
Query Cache
First you need to configure Hibernate.cache.use_query_cache=true
If you use Ehcache, configure Ehcache.xml, notice that hibernate3.0 is not NET.SF's package name.
<cache name= "Net.sf.hibernate.cache.StandardQueryCache"
Maxelementsinmemory= "eternal=" false "timetoidleseconds=" 3600 "
timetoliveseconds= "7200" overflowtodisk= "true"/>
<cache name= "Net.sf.hibernate.cache.UpdateTimestampsCache"
Maxelementsinmemory= "eternal=" true "overflowtodisk=" true "/>
And then
Query.setcacheable (TRUE);//Activate query cache
Query.setcacheregion ("mycacheregion");//Specify the cacheregion to use, optional
The second line specifies that the cacheregion to be used is mycacheregion, that is, you can make a separate configuration for each query cache, use Setcacheregion to do this designation, and configure it in Ehcache.xml:
<cache name= "Mycacheregion" maxelementsinmemory= "ten" eternal= "false" timetoidleseconds= "3600" timetoliveseconds= "7200" overflowtodisk= "true"/>
If you omit the second line and do not set cacheregion, then the standard query cache configuration mentioned above is used, which is Net.sf.hibernate.cache.StandardQueryCache
For the query cache, the cache key is based on HQL generated SQL, plus parameters, paging and other information (can be seen through the log output, but its output is not very readable, it is better to change its code).
such as HQL:
From Cat C where c.name?
Generate SQL that is roughly the following:
SELECT * from Cat C where c.name?
The parameter is "tiger%", then the query cache key* about * is such a string (I write by memory, not accurate, but see also should understand):
SELECT * from Cat C where c.name? , parameter:tiger%
This guarantees the same key with the same query, the same parameters, and so on.
Now say the cached value, if it is a list mode, value is not the entire result set here, but rather the string ID of the query. That is, either the list method or the Iterate method, the first time the query, they query the way they usually do the same, the list executes a sql,iterate execution 1+n bar, the more out of the behavior is that they populate the cache. But to the same condition when the second query, it is the same as the iterate behavior, according to the cache key to go to the cache and found that Value,value is a string of IDs, and then in the class of the cache to a one of the load out. This is done to save memory.
As you can see, the query cache needs to open the class cache of the related classes. When the list and iterate methods are executed for the first time, both the query cache and the class cache are populated.
There is also an important problem that can easily be overlooked, that is, even the list method may encounter 1+n problems after opening the query cache! The same condition when the first list is not found in the query cache, regardless of whether the class cache has data, always send an SQL statement to the database to get all the data, and then populate the query cache and the class cache. But the second time, the problem comes, if your class cache time-out is relatively short, now the class cache has timed out, but the query cache is still in, then the list method after obtaining the ID string, will be a database load! Therefore, the class cache time-out must not be shorter than the time-out of the query cache setting! If you also set a daze time, to ensure that the class cache daze time is greater than the cache of the query time to live. Here are other things, such as the class cache is enforced by the program evict, this situation please pay attention to.
In addition, if the HQL query contains a SELECT clause, then the value inside the query cache is the entire result set.
When hibernate updates the database, how does it know which query cache to update?
Hibernate maintains the last update time for each table in one place, in fact, in the cache configuration specified above Net.sf.hibernate.cache.UpdateTimestampsCache.
When you update through Hibernate, Hibernate will know which tables are affected by this update. It then updates the last update time for these tables. Each cache has a build time and the table queried by the cache, and when hibernate queries a cache for existence, if the cache exists, it also needs to take out the cache generation time and the table the cache is querying, and then go to the last update time for those tables, and if a table has been updated after the build time, Then this cache is invalid.
As you can see, whenever a table is updated, the query cache that is involved in the table is invalidated, so the query cache hit rate may be low.
Collection Cache
Need to be set up inside HBM's collection
<cache usage= "Read-write"/>
If class is cat,collection called children, then ehcache inside configuration
<cache name= "Com.xxx.pojo.Cat.children"
Maxelementsinmemory= "eternal=" false "timetoidleseconds=" 3600 "timetoliveseconds=" 7200 "
Overflowtodisk= "true"/>
The collection cache is the same as the list of the previous query cache, but it does not expire because the table has been updated, and a collection cache is only invalidated if the elements inside the collection are deleted.
There is a problem, if your collection is sorted according to a field, when one of the elements updates the field, causing the order to change, the order of the collection cache is not updated.
Caching Policies
Read-only cache (READ-ONLY): There's nothing to say.
Read/write Cache (Read-write): The program may want to update data
Non-strict read/write Cache (nonstrict-read-write): Update data is required, but two transactions are less likely to update the same record, performance is better than read/write caching
Transaction cache (Transactional): Cache support transactions, when an exception occurs, the cache can be rolled back, only support JTA environment, this I did not how to study
Read-write cache and the non-strict read-write cache in the implementation of the difference is that the read-write cache update cache when the data into a lock, other transactions if the corresponding cache data, found to be locked, and then directly take the database query.
In the Ehcache implementation of hibernate2.1, if a transaction that locks a partially cached event has an exception, the cache is locked until it expires in 60 seconds.
The non-strict read-write cache does not lock data in the cache.
Precondition for using level two cache
Your hibernate program has exclusive write access to the database, and other processes update the database, and hibernate is impossible to know. You must operate the database directly through hibernate, and hibernate is unaware if you call the stored procedure or update the database yourself using JDBC. hibernate3.0 Bulk Update and delete is not updated with level two cache, but it is said that 3.1 has solved the problem.
This limit is quite tricky, sometimes hibernate do batch update, delete very slow, but you can not write your own JDBC to optimize, very depressed it.
Sessionfactory also provides a way to remove the cache, you must write some JDBC, you can call these methods to remove the cache, these methods are:
void evict (Class persistentclass)
Evict all entries from the Second-level cache.
void evict (Class persistentclass, Serializable ID)
Evict an entry from the Second-level cache.
void Evictcollection (String roleName)
Evict all entries from the Second-level cache.
void Evictcollection (String roleName, Serializable ID)
Evict an entry from the Second-level cache.
void Evictqueries ()
Evict any query result sets cached in the default query cache region.
void Evictqueries (String cacheregion)
Evict any query result sets cached in the Named query cache region.
But I don't recommend it because it's hard to maintain. For example, you are now using JDBC batch update a table, there are 3 query cache will use this table, with Evictqueries (String cacheregion) Remove 3 query cache, and then use evict (Class persistentclass) The class cache is removed and looks as if it is complete. But the day you add a related query cache, you may forget to update the removal code here. If your JDBC code is everywhere, when you add a query cache, do you know where other changes will be made?
----------------------------------------------------
Summary:
Don't take it for granted that caching will certainly improve performance, only if you can harness it and the conditions are right. Hibernate's level Two cache limit is still relatively large, and the inconvenient use of JDBC may significantly reduce the performance of the update. If you do not understand the principle of the use of chaos, there may be 1+n problems. Improper use may also result in the reading of dirty data.
If you can't stand the many limitations of hibernate, do caching yourself at the application level.
The higher the level of caching, the better the effect will be. As if the disk has a cache, the database is to implement its own cache, although the database has a cache, our application is to do the cache. Because the underlying cache it does not know what the high-level to use these data to do, can only do more common, and high-level can be targeted to implement the cache, so at a higher level cache, the effect is better.