First level cache provided by Hibernate
Hibernate is a thread that corresponds to a session, and a thread can be viewed as a user. That is, the session cache (cache) can only be used by one thread, other threads are not used, and a primary cache is bound to threads.
The hibernate cache life cycle is very short, and as with the session life cycle, the first level cache is also called session level cache or transaction level cache. If the TB transaction commits or rolls back, we say the session is closed and the life cycle is over.
The difference between a cache and a pool of connections: both the cache and the pool are in memory, and the implementation is the same, all to improve performance. But there are subtle differences, the pool is heavyweight, the data inside is the same, such as a pool of 100 connection connected objects, the 100 are the same. The data in the cache, each of them is different. For example, reading 100 database records into the cache, these 100 records are different.
The cache is primarily used for queries//in the same session, issued two times for the Load method query student student = (student) session.load (Student.class, 1); System.out.println ("student.name=" + student.getname ());//does not issue a query statement, load uses cache student = (student) Session.load ( Student.class, 1); System.out.println ("student.name=" + student.getname ()), second query the same data for the first time, the second load method is to fetch data from the cache, does not emit SQL statements into the database query. The same session, issued two times Get method query Student student = (student) Session.get (Student.class, 1); System.out.println ("student.name=" + student.getname ());//does not issue query statements, get uses cache student = (student) Session.get ( Student.class, 1); System.out.println ("student.name=" + student.getname ()), second query the same data for the first time, the second time does not issue SQL statements query the database, but to the cache to fetch data. The same session, issued two times iterate query entity object iterator ITER = Session.createquery ("From Student s where s.id<5"). Iterate (); Iter.hasnext ()) {Student Student = (Student) iter.next (); System.out.println (Student.getname ());} System.out.println ("--------------------------------------");//It will issue the query ID statement, but will not issue a query based on the ID of the student's statement, because iterate uses cache iter = Session.createquery ("from Student s where s.id<5 "). Iterate (); while (Iter.hasnext ()) {Student Student = (Student) iter.next (); System.out.println (Student.getname ());}
When it comes to Iterater query, we immediately think: Iterater query in the absence of a cache will have n+1 problem.
Execute the above code to view the SQL statement of the console, the first time the iterate query emits a N+1 SQL statement, the first SQL statement queries all IDs, and then queries the entity object based on the ID, with n IDs that emit n statements to query the entity.
The second time iterate query, but only send a SQL statement, query all the ID, and then based on the ID into the cache to take entity objects, no longer send SQL statements to the database query.
The same session, issued two times iterate query, query Common Properties iterator iter = Session.createquery ("Select S.name from Student s where s.id<5"). Iterate (); while (Iter.hasnext ()) {String name = (string) iter.next (); SYSTEM.OUT.PRINTLN (name);} System.out.println ("--------------------------------------");//iterate query Normal properties, first-level cache is not cached, so issue a query statement// The first-level cache is the cache entity object of iter = Session.createquery ("Select S.name from Student s where s.id<5"). Iterate (); while (Iter.hasnext ()) {String name = (string) iter.next (); SYSTEM.OUT.PRINTLN (name);} Executes the code look at the console SQL statement, the first issue of the n+1-SQL statement, the second time or issued a n+1 SQL statement. Because the first-level cache caches only entity objects, TB does not cache normal properties, so the second issue is the SQL query statement. Two sessions each session sends a Load method to query the entity object try {session = Hibernateutils.getsession (); Session.begintransaction (); Student Student = (Student) session.load (Student.class, 1); System.out.println ("student.name=" + student.getname ()); Session.gettransaction (). commit ();} catch (Exception e) {e.printstacktrace (); Session.gettransaction (). rollback (); finally {hibernateutils.closesession (session);} The second session calls the Load method try {session = HibernateuTils.getsession (); Session.begintransaction (); Student Student = (Student) session.load (Student.class, 1);//will issue query statements, the session can not share a primary cache data// Because he will die with the death of the session System.out.println ("Student.name=" + student.getname ()); Session.gettransaction (). commit ();} catch (Exception e) {e.printstacktrace (); Session.gettransaction (). rollback (); finally {hibernateutils.closesession (session);} The Load method of the first session emits an SQL statement to query the entity object, and the Load method of the second session issues an SQL statement to query the entity object. Because the session can not share the first cache of data, so the second session of the Load method to query the same data or to the database query, because it cannot find the data cached in a session. The same session, call the Save method and then call the Load method to query the data just save student student = new Student (); Student.setname ("Zhang San");// The Save method returns the Idserializable id = session.save (student) of the entity object, Student = (student) session.load (student.class, id);//does not issue a query statement , because save supports cache System.out.println ("student.name=" + student.getname ()), Save the entity object First, and then use the Load method to query the entity object that was just saved. The Load method does not issue SQL statements to database queries, but rather to cache data, because the Save method also supports caching. The premise, of course, is the same session. Large quantities of data are added for (int i=0; i<100; i++) {Student Student = new Student (); Student.setname ("Zhang San" + i); session.Save (student);//Update the IF (i% = = 0) {Session.flush ()//for every 20 bars;//Clear cached content Session.clear ();}}
When a large amount of data is added, it causes memory overflow, because the Save method supports caching, and each save object is placed in the cache, and if the object is more than enough memory is bound to overflow. It is common practice to determine how many objects are save, and if you save 20 objects, manually clean up the cache, so that there is no memory overflow.
Note: Before you clean the cache, manually call the Flush method to synchronize to the database, otherwise the Save object is not saved to the database.
Note: the addition of large quantities of data is still not hibernate, which is a hibernate weakness. You can use JDBC (not too fast, just a bit better than hibernate), or use a tool product, such as Oracle SQL Loader, to import data very quickly.
Hibernate Level Two cache
The secondary cache needs to be sessionfactory to manage it, it is into the primary cache, everyone can use it, it is shared.
Secondary caches are more complex and generally use third-party products. Hibernate provides a simple implementation, made with Hashtable, that can only be used as part of our testing, commercial or third-party products.
Using a cache is definitely a long-time, unchanged data that doesn't make much sense if the data that's constantly changing is put into the cache. It is not necessary to use caching because of the frequent changes or the need to query the database frequently.
Hibernate has done some optimizations, and some third-party cache products have been integrated. The teacher uses Ehcache cache products.
integration with Ehcache level two cache product: Ehcache jar file in Hibernate Lib , we also need to set a series of cache usage policies, need a configuration file Ehcache.xml to configure . This file is placed under the classpath.
Default configuration, all classes follow this configuration <defaultcache//cache can put 10,000 objects maxelementsinmemory= "10000"//over period, if true is never expire eternal= " False "//When an object is accessed for an extended period of time without access expires (120 seconds has not been accessed again) timetoidleseconds=" 120 "//Object survival time (120 seconds), if the settings never expire, There is no need to set the timetoliveseconds= "120"//overflow problem, if set to true, cache more than 10,000 objects are saved to disk overflowtodisk= "true"/> We can also configure an object individually: <cache name= "Com.bjpowernode.hibernate.Student"
Maxelementsinmemory= "eternal=" false "timetoidleseconds=" 10000 "timetoliveseconds=" 10000 "overflowtodisk=" true "/> also needs to configure the cache in the Hibernate.cfg.xml configuration file so that hibernate knows that we are using that level two cache. <!--Configuring the cache provider--><property name= "Hibernate.cache.provider_class" >org.hibernate.cache.ehcacheprovider </property><!--Enable level two caching, which is also its default configuration--><property name= "Hibernate.cache.use_second_level_cache" > True</property> the configuration to enable level Two caching is not writable, because the default is true to turn on level two caching.
The objects of those entity classes must also be manually assigned to the cache in the Hibernate.cfg.xml://In the <sessionfactory> tab, configure <class-cache class after the <mapping> tag = "Com.bjpowernode.hibernate.Student" usage= "read-only"/> or in the Entity class mapping File://In the <class> tab,<id> label pre-configuration < Cache usage= "Read-only"/>
The Usage property represents a policy that uses caching and generally takes precedence over the use of read-only, which means that if the data is placed in the cache, it is not allowed to be modified and an error will be made if modified. It is important to note that the data we put into the cache is not allowed to be modified. Because the data in the cache is often modified, there is no need to put it in the cache.
Using the READ-ONLY strategy is efficient because caching cannot be changed. However, the problem of dirty data may occur, this problem resolution can only rely on the cache timeout, such as the above we set a timeout of 120 seconds, 120 can be modified in the cache object, and within 120 seconds access to the object may query dirty data, because we modified the object after the database changed, The cache does not change, which causes data to be out of sync, which is dirty data.
The second kind of caching strategy read-write, when persistent objects change, the cache changes, and the database changes. This method needs to be unlocked, and the efficiency is slower than the first.
There are also two strategies, see hibernate documentation, most commonly used or the 12th strategy.
Second-level cache test Code Demo: Note The above two sessions we talked about call the Load method to query the same data, the second session of the Load method or SQL statement to the database query data, because the primary cache is only shared in the current session, This means that the first-level cache cannot be accessed across sessions.
Turn on level Two cache, level two cache is process level cache, can share//two session call the Load method to query the same entity object try {session = Hibernateutils.getsession (); Session.begintransaction (); Student Student = (Student) session.load (Student.class, 1); System.out.println ("student.name=" + student.getname ()); Session.gettransaction (). commit ();} catch (Exception e) {e.printstacktrace (); Session.gettransaction (). rollback (); finally {hibernateutils.closesession (session);} try {session = Hibernateutils.getsession (); Session.begintransaction (); Student Student = (Student) session.load (Student.class, 1);//No query statement is issued because the session can share data in level two cache because of configuring level two cache The secondary cache is the process-level cache System.out.println ("student.name=" + student.getname ()); Session.gettransaction (). commit ();} catch (Exception e) {e.printstacktrace (); Session.gettransaction (). rollback (); finally {hibernateutils.closesession (session);} If the level two cache is turned on, then the load method of the second session call queries the data for the first query, and does not issue the SQL statement to query the database, but instead goes to the level two cache to fetch the data. Turn on level two cache//two session call get method to query the same entity object try {session = Hibernateutils.getsession (); Session.begintransaction (); Student Student = (Student) seSsion.get (Student.class, 1); System.out.println ("student.name=" + student.getname ()); Session.gettransaction (). commit ();} catch (Exception e) {e.printstacktrace (); Session.gettransaction (). rollback (); finally {hibernateutils.closesession (session);} try {session = Hibernateutils.getsession (); Session.begintransaction (); Student Student = (Student) session.get (Student.class, 1);//No query statement is issued because the session can share data in level two cache because of configuring level two cache The secondary cache is the process-level cache System.out.println ("student.name=" + student.getname ()); Session.gettransaction (). commit ();} catch (Exception e) {e.printstacktrace (); Session.gettransaction (). rollback (); finally {hibernateutils.closesession (session);}
Note: Level Two caching must be managed by Sessionfactory to allow sessionfactory to clear level two cache. Sessionfactory.evict (Student.class);//Clear all Student objects in level two cache, sessionfactory.evict (student.class,1);// Clears the student object with ID 1 in level two cache.
If the first session calls the load or get method to query the data, the level two cache is cleared, then the second session call the load or get method query the same data, or will issue a SQL statement query database, because there is no data in the cache can only be queried in the database.
When we query the data will be automatically placed in the default level two and the first level of the cache, if we want to query the data is not put in the cache, it is also possible. That is, we can control the exchange of first-level cache and level two cache.
Session.setcachemode (cachemode.ignore); Prevents data in the first-level cache from being placed in the level two cache.
Using the above code test, execute Session.setcachemode (Cachemode.ignore) before the first session calls the Load method, so the data queried by the Load method is not placed in the level two cache. Then the second session to execute the Load method query the same data, will issue SQL statements to the database query, because there is no data in level two cache, first-class cache because different sessions can not be shared, so only to the database query.
We've talked about the overflow when large quantities of data are added, and the workaround is to clean up the first-level cache after 20 objects per day. If we use a level two cache, it is not enough to clean the primary cache, and to disable level one or two cache interaction, call Session.setcachemode (Cachemode.ignore) before the Save method.
The second-level cache also does not store query data for normal properties, which is the same as the first-level cache, and only holds entity objects. Session-level caching does not make much sense to improve performance because the life cycle is too short.
Hibernate Query Cache
Both the first level cache and the level two cache are only the entity objects, if the query entity object's normal attribute data can only be placed in the query cache, the query cache also holds the ID of the query entity object.
The life cycle of the query cache is indeterminate, and the lifetime of the query cache ends when its associated table is modified. The modification of the table here refers to the modification by hibernate, not the database client software to log on to the database.
Hibernate query cache is off by default, if you want to use it, configure it to the Hibernate.cfg.xml file: <property name= "Hibernate.cache.use_query_cache" >true </property> the query cache must be enabled manually in the program, and the Setcacheable (True) method in the query interface is enabled. Turn off level Two cache, do not open query cache, use list method to query Common Properties//the same sessin, query two times List names = Session.createquery ("Select S.name from Student S"). List (); for (int i=0; i<names.size (); i++) {String name = (string) names.get (i); SYSTEM.OUT.PRINTLN (name);} System.out.println ("-----------------------------------------");//emits an SQL statement names = session.createquery ("Select S.name from Student S "). Setcacheable (True). List (), for (int i=0; i<names.size (); i++) {String name = (string) names.get ( i); SYSTEM.OUT.PRINTLN (name);} The above code runs, because the query cache is not used, and the secondary cache does not cache normal properties, so the second query will issue an SQL statement to the database query. Now open the query cache, close level two cache, and call Setcacheable (TRUE) before the first list method, and call this code before the second list query, you can write the following: List names = Session.createquery ( "Select S.name from Student S"). Setcacheable (True). List (); the other code does not change, after running the code found that the second list query normal properties did not issue the SQL statement, that is, there is no query in the database, Instead, the data is taken to the query cache. Open query cache, close level two cache, use list method to query normal properties//Call Lis in two sessionT method try {session = Hibernateutils.getsession (); Session.begintransaction (); List names = Session.createquery ("Select S.name from Student S"). Setcacheable (True). List (); for (int i=0; i<names.size (); i++) {String name = (string) names.get (i); SYSTEM.OUT.PRINTLN (name);} Session.gettransaction (). commit ();} catch (Exception e) {e.printstacktrace (); Session.gettransaction (). rollback (); finally {hibernateutils.closesession (session);} System.out.println ("----------------------------------------"); try {session = Hibernateutils.getsession (); Session.begintransaction ();//does not issue a query statement because the query cache is not related to the life cycle of the session List names = Session.createquery ("Select S.name from Student S "). Setcacheable (True). List (); for (int i=0; i<names.size (); i++) {String name = (string) names.get (i); SYSTEM.OUT.PRINTLN (name);} Session.gettransaction (). commit ();} catch (Exception e) {e.printstacktrace (); Session.gettransaction (). rollback (); finally {hibernateutils.closesession (session);}
The result is the second session of the list method to query the normal properties, not issued SQL statements to the database query, but to the query cache to fetch data, indicating that the query cache and session life cycle is not related.
Turn on cache, turn off level two cache, use iterate method to query common properties
Call the Iterate method query in two session
Running the result is the second session of the iterate method or issued a SQL statement query database, which indicates that the iterate iterative query normal properties do not support query caching.
Close the query cache, turn off level two caching, and query entity objects with the list method
Call the list method query in two session
Running results The first session of the call list method queries the entity object to issue the SQL statement query data, because the two cache is turned off, so the second session calls the list method to query the entity object, or it emits a SQL statement to the database query.
Turn on query caching and turn off level two cache
Call the list method in the two session to query the entity object
Run results The first session of the call list method queries the entity object to issue an SQL statement to query the database. The second session called the list method to query the entity object, but issued a lot of SQL statements query the database, which is the same as the n+1 problem, issued a N+1 SQL statement. Why is there such a situation? This is because we are now querying the entity object, the query cache will put the ID of the entity object of the first query into the cache, when the second session called the list method again, it will be in the query cache to the ID one to take out, and then to the corresponding cache to find ( First to find a level of cache can not find a level two cache), if found to return, if not found, will be based on an ID to the database query, so an ID will have an SQL statement.
Note: If a level two cache is configured, the first query of the entity object will be stored in both the primary cache and the level two cache. If there is no level two cache, it is only stored in the first level cache. (First-level cache cannot be shared across session)
Turn on the query cache and turn on level two caching
Call the list method in the two session to query the entity object
The result is that the first session call to the list method emits an SQL statement to query the entity object in the database, because a level two cache is configured, the entity object is placed in the level two cache, because the query cache is configured, then all the ID of the entity object is placed in the query cache. The second session called the list method does not emit a SQL statement, but instead takes the data to the level two cache.
The query cache is not very meaningful, and the query cache is simply storing the data queried by the list method or the Iterate method. We seldom appear in the query with exactly the same conditions of the query, which is the low hit rate, so that the data in the cache is always changing, so it makes little sense. It is only meaningful to configure the query cache unless multiple queries are data that queries the same criteria, which means that the results are always the same.
Hibernate cache and level two cache