Hibernate Cache: Session Cache, Second Level Cache, and Query Cache
Hibernate provides support for Cache to reduce necessary data access. If this function can be correctly used, the program performance will be greatly improved. However, in many cases, we may not use the correct one. The Cache type in Hibernate provides three types of Cache, which may be different from those on the Internet, most of the online statements are that Cache in Hibernate is divided into Level 1 Cache and level 2 Cache. I divided them into three types: 1) Session Cache: caching objects in the current session object 2) Second Level Cache: used to store objects across sessions. 3) Query Cache: stores the IDs of Query statements, parameters, and Query results. Next we will talk about these three caches separately. Find the first example in the Document of Hibernate: copy the code public class Event {private Long id; private String title; private Date date; public Long getId () {return id ;} public void setId (Long id) {this. id = id;} public String getTitle () {return title;} public void setTitle (String title) {this. title = title;} public Date getDate () {return date;} public void setDate (Date date) {this. date = date ;}} copy the code database Description: 1 Session Cache stores the [entity] or [entity set] in the Session. Example: Session Cache: copy the code package com. hibernateTest. dao; import java. util. date; import org. hibernate. session; import com. hibernateTest. entity. event; import util. hibernateUtil; public class EventDao {public static void main (String [] args) {EventDao mgr = new EventDao ();/* if (args [0]. equals ("store") {mgr. createAndStoreEvent ("My Event", new Date ();} */mgr. doubleGet (); HibernateUtil. getSessionFact Ory (). close ();} private void createAndStoreEvent (String title, Date theDate) {Session session = HibernateUtil. getSessionFactory (). getCurrentSession (); session. beginTransaction (); Event theEvent = new Event (); theEvent. setTitle (title); theEvent. setDate (theDate); session. save (theEvent); session. getTransaction (). commit (); session. close ();} private void doubleGet () {Session session = HibernateUtil. GetSessionFactory (). getCurrentSession (); session. beginTransaction (); // first query Event theEvent = (Event) session. get (Event. class, 3L); System. out. println (theEvent. getTitle (); // query theEvent = (Event) session again. get (Event. class, 3L); System. out. println (theEvent. getTitle (); session. getTransaction (). commit (); session. close () ;}} copy the code and first query the result using SQL: that is, the title value is My Event. Next, let's take a look at the program execution results (after EditPlus is used): that is, an SQL query is executed. It seems that the result of the first query is cached. Since it is Session Cache, it should be cached to a variable in the Session object. Check the Session structure: According to the variable name in SessionImpl above, the data should be cached in persistenceContext. However, it is useless to guess, and it must be confirmed through debugging. During the debugging process, we found such a method: copy the Code protected Object doLoad (final LoadEvent event, final EntityPersister persister, final EntityKey keyToLoad, final LoadEventListener. loadType options) {if (log. isTraceEnabled () {log. trace ("attempting to resolve:" + MessageHelper. infoString (persister, event. getEntityId (), event. getSession (). getFactory ();} // obtain the entity Object entity = loadFromSessionCache (Event, keyToLoad, options); if (entity = REMOVED_ENTITY_MARKER) {log. debug ("load request found matching entity in context, but it is scheduled for removal; returning null"); return null;} if (entity = INCONSISTENT_RTN_CLASS_MARKER) {log. debug ("load request found matching entity in context, but the matched entity was of an inconsistent return type; returning null"); return null;} if (Entity! = Null) {if (log. isTraceEnabled () {log. trace ("resolved object in session cache:" + MessageHelper. infoString (persister, event. getEntityId (), event. getSession (). getFactory ();} return entity;} // obtain the entity = loadFromSecondLevelCache (event, persister, options) from the second-level cache; if (entity! = Null) {if (log. isTraceEnabled () {log. trace ("resolved object in second-level cache:" + MessageHelper. infoString (persister, event. getEntityId (), event. getSession (). getFactory ();} return entity;} if (log. isTraceEnabled () {log. trace ("object not resolved in any cache:" + MessageHelper. infoString (persister, event. getEntityId (), event. getSession (). getFactory ();} // obtain the object r from the data source Eturn loadFromDatasource (event, persister, keyToLoad, options);} copy the method above the code to clearly explain the Hibernate query process. 1) first query data from the Session cache; 2) No data can be found from the second-level cache; 3) data is still almost loaded from data sources (such as XML files and databases. From process 1) We know that other operations will actually store the data to the Session Cache. Otherwise, how can we query the data from the Session Cache. Let's take a look at how data is queried from the Session Cache? Copy the Code protected Object loadFromSessionCache (final LoadEvent event, final EntityKey keyToLoad, final LoadEventListener. loadType options) throws HibernateException {SessionImplementor session = event. getSession (); Object old = session. getEntityUsingInterceptor (keyToLoad); if (old! = Null) {// this object was already loaded // query EntityEntry oldEntry = Session from the persistenceContext attribute of the session object. getPersistenceContext (). getEntry (old); if (options. isCheckDeleted () {Status status Status = oldEntry. getStatus (); if (status = Status. DELETED | status = Status. GONE) {return REMOVED_ENTITY_MARKER;} if (options. isAllowNulls () {// EntityPersister persister = event. getSession (). GetFactory (). getEntityPersister (event. getEntityClassName (); EntityPersister persister = event. getSession (). getFactory (). getEntityPersister (keyToLoad. getEntityName (); if (! Persister. isInstance (old, event. getSession (). getEntityMode () {return INCONSISTENT_RTN_CLASS_MARKER;} upgradeLock (old, oldEntry, event. getLockOptions (), event. getSession ();} return old;} since the replication code obtains data from persistenceContext in the session object, the Session Cache mentioned above is indeed persistenceContext, it also verifies the correctness of the previous prediction. In the preceding example, two queries are performed. During the first query, there is no relevant entity in the Session Cache, so data is obtained from the database. That is, the loadFromDatasource method is used. When will the program store the queried data in the Session Cache to reduce the number of database accesses? To know the answer to this question, check the implementation of loadFromDatasource. After tracing N multi-layer calls to the method, the private void loadFromResultSet (final ResultSet rs, final int I, final Object object, final String instanceEntityName, final EntityKey key, final String rowIdAlias, final LockMode lockMode, final Loadable rootPersister, final SessionImplementor session) throws SQLException, HibernateException {final Serializable id = key. getIdentifier (); // Get the persister for the _ subclass _ fi Nal Loadable persister = (Loadable) getFactory (). getEntityPersister (instanceEntityName); if (log. isTraceEnabled () {log. trace ("Initializing object from ResultSet:" + MessageHelper. infoString (persister, id, getFactory ();} boolean eagerPropertyFetch = isEagerPropertyFetchEnabled (I ); // add temp entry so that the next step is circular-reference // safe-only needed because some types do N't take proper // advantage of two-phase-load (esp. components) // load data in two phases and store the data in the session cache. addUninitializedEntity (key, object, persister, lockMode ,! EagerPropertyFetch, session); // This is not very nice (and quite slow): final String [] [] cols = persister = rootPersister? GetEntityAliases () [I]. getSuffixedPropertyAliases (): getEntityAliases () [I]. getSuffixedPropertyAliases (persister); final Object [] values = persister. hydrate (rs, id, object, rootPersister, cols, eagerPropertyFetch, session); final Object rowId = persister. hasRowId ()? Rs. getObject (rowIdAlias): null; final AssociationType [] ownerAssociationTypes = getOwnerAssociationTypes (); if (ownerAssociationTypes! = Null & ownerAssociationTypes [I]! = Null) {String ukName = ownerAssociationTypes [I]. getRHSUniqueKeyPropertyName (); if (ukName! = Null) {final int index = (UniqueKeyLoadable) persister ). getPropertyIndex (ukName); final Type = persister. getPropertyTypes () [index]; // polymorphism not really handled completely correctly, // perhaps... well, actually its OK, assuming that the // entity name used in the lookup is the same as the // the one used here, which it will be EntityUniqueKey euk = new EntityUniqueKey (rootPersister. GetEntityName (), // polymorphism comment above ukName, type. semiResolve (values [index], session, object), type, session. getEntityMode (), session. getFactory (); session. getPersistenceContext (). addEntity (euk, object) ;}} TwoPhaseLoad. postHydrate (persister, id, values, rowId, object, lockMode ,! Failed, session);} copy the code to copy the public static void addUninitializedEntity (final EntityKey key, final Object object, final EntityPersister persister, final LockMode lockMode, final boolean complete, final SessionImplementor session) {session. getPersistenceContext (). addEntity (object, Status. LOADING, null, key, null, lockMode, true, persister, false, lazyPropertiesAreUnf Etched);} copy the code get, load, save, saveOrUpdate, update, and merge... And so on.