cache
Today we will talk about the entity state and hibernate cache in hibernate.
1) First, let’s take a look at the entity status:
There are three main types of entity states: transient, persistent, and detached.
You probably understand it by reading English.
transient: refers to the data that has not yet corresponds to the data in the database.
persistent: refers to the data corresponding to the data in the database, and any changes in it will be reflected in the database.
detached: refers to the data corresponding to the data in the database, but because the session is closed, the modifications it makes will not affect the database records.
Let's directly code:
Transaction tx = session.beginTransaction(); User user = new User(); user.setName("shun"); //The user here has not been saved to the database, and there is no corresponding record in the database table. It is transient state session.save(user); tx.commit(); //After commit, the user becomes persistent state session.close(); //Because the session is closed, the user is detached at this time, and all its modifications will not be reflected in the database. Session session2 = sessionFactory.openSession(); tx = session2.beginTransaction(); user.setName("shun123"); session2.saveOrUpdate(user); tx.commit(); //When we call saveOrUpdate, the user becomes persistent again, and all its modifications will be reflected in the database. session2.close(); We see the code. First, we define an object user. Before it is saved, it is the transient state and there is no corresponding record in the database. When we save and submit the modification, the user becomes persistent state and there is a corresponding record in the database. When we close the session, the user becomes detached, and its changes will not be reflected in the database unless we manually call corresponding update and addition methods such as saveOrUpdate. And what should we do when we want it to go from persistent to transient state? Just delete it directly. After deletion, the object will have no corresponding record in the database, which will become transient.
The state transition of hibernate is relatively simple. When it is transient, there is no corresponding record for the database, while persistent and detached have corresponding records. However, the only difference is that detached is a state that only exists after the session is closed. So what is the difference between transient and detached? It's just a question of whether there is a corresponding database table record.
2) After reading the status, let’s take a look at the cache of hibernate
There are two types of caches for hibernate: Level 1 cache and Level 2 cache.
Level 1 cache: The so-called level 1 cache is the internal cache.
Level 2 cache: It includes application-level cache, which is called SessionFactory cache in hibernate, and the other is distributed cache, which is the safest cache method.
Let's take a look at the program directly:
public static void main(String[] args) { Configuration cfg = new Configuration().configure(); SessionFactory sessionFactory = cfg.buildSessionFactory(); Session session = sessionFactory.openSession(); User user = (User)session.load(User.class,new Long(29)); System.out.println(user.getName()); User user2 = (User)session.load(User.class,new Long(29)); System.out.println(user2.getName()); session.close(); } See the results:
Hibernate: select user0_.USER_ID as USER1_0_0_, user0_.USER_NAME as USER2_0_0_, user0_.age as age0_0_ from USER user0_ where user0_.USER_ID=? shun123123 shun123123
In the example we used load twice, but there was only one SQL statement in the result, which indicated that it was only queryed once.
Why? This is why the hibernate cache works. After the first query is completed, hibernate the found entity in the cache. The next time you check, the cache will be checked to see if there is any entity with the corresponding ID. If there is, it will be taken out directly, otherwise the database query will be conducted.
Let's modify the code to:
User user = (User)session.load(User.class,new Long(29)); System.out.println(user.getName()); session.evict(user);//Delete user from cache User user2 = (User)session.load(User.class,new Long(29)); System.out.println(user2.getName()); session.close();
See the results:
Hibernate: select user0_.USER_ID as USER1_0_0_, user0_.USER_NAME as USER2_0_0_, user0_.age as age0_0_ from USER user0_ where user0_.USER_ID=? shun123123 Hibernate: select user0_.USER_ID as USER1_0_0_, user0_.USER_NAME as USER2_0_0_, user0_.age as age0_0_ from USER user0_ where user0_.USER_ID=? shun123123
After we delete the user from the cache, the second query is also directly retrieved from the database.
Level 2 cache talk
Let's look at the entity class first:
public class User implements Serializable{ public Long id; private String name; private int age; } The mapping file is omitted, everyone should write it.
Let’s take a look at the hibernate configuration file:
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property> <property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.use_query_cache">true</property>
We see that in the provider_class we specify the ehcache, so we also need ehcache.xml to be placed in the classpath:
<?xml version="1.0" encoding="UTF-8"?> <ehcache> <diskStore path="java.io.path"/> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" /> </ehcache>
Next, let's take a look at the test method:
public static void main(String[] args) { Configuration cfg = new Configuration().configure(); SessionFactory sessionFactory = cfg.buildSessionFactory(); Session session = sessionFactory.openSession(); Query query = session.createQuery("from User user where name = 'shun123'"); Iterator iter = query.iterate(); while(iter.hasNext()) { System.out.println(((User)iter.next()).getName()); } session.close(); Session session2 = sessionFactory.openSession(); Query query2 = session2.createQuery("from User user where name='shun123'"); Iterator iter2 = query2.iterate(); while(iter2.hasNext()) { System.out.println(((User)iter2.next()).getName()); } session2.close(); } After running, you can see:
Hibernate: select user0_.USER_ID as col_0_0_ from USER user0_ where user0_.USER_NAME='shun123' Hibernate: select user0_.USER_ID as USER1_0_0_, user0_.USER_NAME as USER2_0_0_, user0_.age as age0_0_ from USER user0_ where user0_.USER_ID=? shun123 Hibernate: select user0_.USER_ID as col_0_0_ from USER user0_ where user0_.USER_NAME='shun123' shun123
We can see that it only performs one sentence of search, and the ID is not fetched for searching on the second query, which is mainly due to the secondary cache.
Let’s analyze the code in the test method first. In the test method, we open two sessions and create two queryes separately for the same query. However, the two sessions can share the cache, which is the second level cache, the SessionFactory level cache. As long as our session is created by the same SessionFactory, we can share the secondary cache to reduce interaction with the database.
Let's take a look at the meaning in the configuration file:
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property> <property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.use_query_cache">true</property>
If we need to use the secondary cache, we need to configure it first:
<property name="hibernate.cache.use_second_level_cache">true</property>
Perform the second-level cache of account opening, and then pass:
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
Specify the provision class for the secondary cache. Generally, we use ehcache. The others mine are not used for the time being and are not very clear, so I won't talk about them for the time being.
Like our example just now, we only need to configure the above two, which can completely run normally and use the secondary cache.
So what is the third sentence for?
<property name="hibernate.cache.use_query_cache">true</property>
This configuration indicates that we need to use cache when querying. If we need to use this, we must call query.setCacheable(true) in advance to enable it.
Let's look at the code together (we won't enable cache for now):
public static void main(String[] args) { Configuration cfg = new Configuration().configure(); SessionFactory sessionFactory = cfg.buildSessionFactory(); Session session = sessionFactory.openSession(); Query query = session.createQuery("from User user where name = 'shun123'"); List list = query.list(); for (int i = 0; i < list.size(); i++){ System.out.println(((User)list.get(i)).getName()); } session.close(); Session session2 = sessionFactory.openSession(); Query query2 = session2.createQuery("from User user where name='shun123'"); List list2 = query2.list(); for (int i = 0; i < list2.size(); i++){ System.out.println(((User)list.get(i)).getName()); } session2.close(); } The result output here is:
Hibernate: select user0_.USER_ID as USER1_0_, user0_.USER_NAME as USER2_0_, user0_.age as age0_ from USER user0_ where user0_.USER_NAME='shun123' shun123 Hibernate: select user0_.USER_ID as USER1_0_, user0_.USER_NAME as USER2_0_, user0_.age as age0_ from USER user0_ where user0_.USER_NAME='shun123' shun123
We see that it does not utilize the cache because we use list here, and list only writes and does not read the cache. So there will be two queries here.
Then let's modify it:
public static void main(String[] args) { Configuration cfg = new Configuration().configure(); SessionFactory sessionFactory = cfg.buildSessionFactory(); Session session = sessionFactory.openSession(); Query query = session.createQuery("from User user where name = 'shun123'"); <span style="background-color: #ffffff;"><span style="color: #ff0000;">query.setCacheable(true);</span></span> List list = query.list(); for (int i = 0; i < list.size(); i++){ System.out.println(((User)list.get(i)).getName()); } session.close(); Session session2 = sessionFactory.openSession(); Query query2 = session2.createQuery("from User user where name='shun123'"); <span style="color: #ff0000;">query2.setCacheable(true);</span> List list2 = query2.list(); for (int i = 0; i < list2.size(); i++){ System.out.println(((User)list.get(i)).getName()); } session2.close(); } I saw two red sentences of code. These are the two codes that we added to enable query cache. Now we see the result:
Hibernate: select user0_.USER_ID as USER1_0_, user0_.USER_NAME as USER2_0_, user0_.age as age0_ from USER user0_ where user0_.USER_NAME='shun123' shun123 shun123
There is only one query left, why? Just at those two red codes, we turned on the cache, remember, it needs to be used twice. Set both query caches to be cached.
Criteria is also a similar approach. In order to avoid some children's shoes forgetting how to write Criteria, I will put some code:
public static void main(String[] args) { Configuration cfg = new Configuration().configure(); SessionFactory sessionFactory = cfg.buildSessionFactory(); Session session = sessionFactory.openSession(); Criteria criteria1 = session.createCriteria(User.class); criteria1.setCacheable(true); criteria1.add(Restrictions.eq("name","shun123")); List list = criteria1.list(); for (int i = 0; i < list.size(); i++){ System.out.println(((User)list.get(i)).getName()); } session.close(); Session session2 = sessionFactory.openSession(); Criteria criteria2 = session2.createCriteria(User.class); criteria2.setCacheable(true); criteria2.add(Restrictions.eq("name","shun123")); List list2 = criteria2.list(); for (int i = 0; i < list2.size(); i++){ System.out.println(((User)list.get(i)).getName()); } session2.close(); } Let's see the results:
Hibernate: select this_.USER_ID as USER1_0_0_, this_.USER_NAME as USER2_0_0_, this_.age as age0_0_ from USER this_ where this_.USER_NAME=? shun123 shun123