Research on Hibernate Cache

Keywords: Java Session Hibernate SQL Database

1. What is caching?

Database caching refers to data between applications and physical data sources. That is to copy the data from the physical data source to the cache. With caching, applications can reduce the frequency of access to physical data sources, thereby improving efficiency. Cached media are usually memory or hard disk.

Hibernate has three types of caches: first-level caches, second-level caches and query caches.

2. Level 1 cache

The first level cache, Session cache, is managed automatically by Session without the need for program intervention. The first level cache is loaded and cached according to the ID of the object. The following code:

    @Override
    public void testCache() {
        // TODO Auto-generated method stub
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();  
        Course c = (Course) session.get(Course.class, 1);
        System.out.println("Name:" + c.getName());
        c = (Course) session.get(Course.class, 1);
        System.out.println("Name:" + c.getName());
        tx.commit();
        session.close();

    }

Operation results:

Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
Name:Computer Principle
Name:Computer Principle

The first query generates the SQL statement, and puts the queried object in the first level cache. The second query finds the object directly in the first level cache, so there is no need to generate the SQL statement again.

Look at another example:

    @Override
    public void testCache() {
        // TODO Auto-generated method stub
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();  
        Course c = (Course) session.get(Course.class, 1);
        System.out.println("Name:" + c.getName());
        tx.commit();
        session.close();
        
        session = sessionFactory.openSession();
        tx = session.beginTransaction(); 
        c = (Course) session.get(Course.class, 1);
        System.out.println("Name:" + c.getName());
        tx.commit();
        session.close();
    }

Since the first level cache is the Session level cache, the first level cache does not exist after the Session is closed, and the second query also generates SQL statements:

Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
Name:Computer Principle
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
Name:Computer Principle

3. Level 2 cache

The second level cache, SessionFactory cache, is similar to the first level cache. It also loads and caches according to the ID of the object. The difference is that the first level cache is only valid in SessionFactory, while the second level cache is valid in SessionFactory. When accessing an ID object, first go to the first level cache to find, if not to find the second level cache to find. Secondary cache includes EHCache, OSCache, Swarm Cache and JBoss Cache. Here we take EHCache as an example.

Secondary caching requires program management. First, configure Maven to download the relevant Jar and add it to the pom file:

        <dependency> 
            <groupId>org.hibernate</groupId> 
            <artifactId>hibernate-ehcache</artifactId> 
            <version>4.1.0.Final</version> 
        </dependency>

        <dependency>  
            <groupId>net.sf.ehcache</groupId>  
            <artifactId>ehcache</artifactId>  
            <version>2.8.3</version>  
        </dependency>

Create the EHCache configuration file ehcache.xml:

<ehcache>
    <diskStore path="E:\Eclipse\MyWorkspace\Cache"/>
    <defaultCache
        maxElementsInMemory="10000"
        eternal="true"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
    />
    <cache name="com.hzhi.course.entity.Course"
        maxElementsInMemory="10000"
        eternal="true"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
        overflowToDisk="true"
    />
</ehcache>

defaultCache is the default setting. The following cache specifies which class to cache. It sets the maximum number of cached objects, whether permanently valid, the maximum number of idle seconds, the maximum number of surviving seconds, whether to write to the hard disk when the memory is full, the path to the hard disk, and so on.

Modify the hbm file of the class that needs to be cached:

    <class name="com.hzhi.course.entity.Course" table="clas">
        <cache usage="read-only"/>
                ......
    </class>

usage sets concurrent access policy, which is generally set to read-only.

Modify the configuration of SessionFactory in applicationContext.xml to add some properties of the secondary cache:

    <!-- SessionFactory -->
     <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" >
            <ref local="dataSource"/>
        </property>
        <!-- To configure Hibernate Attribute -->
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.connection.isolation">8</prop>
                <!-- Two level cache -->
                <prop key="hibernate.cache.use_second_level_cache">false</prop>
                <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>                
                <prop key="hibernate.cache.provider_configuration_file_resource_path">WEB-INF/ehcache.xml</prop>                
            </props>
        </property>
     ......
</bean>

Run the following example:

    @Override
    public void testCache() {
        // TODO Auto-generated method stub
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();  
        Course c = (Course) session.get(Course.class, 1);
        System.out.println("Name:" + c.getName());
        tx.commit();
        session.close();
        
        session = sessionFactory.openSession();
        tx = session.beginTransaction(); 
        c = (Course) session.get(Course.class, 1);
        System.out.println("Name:" + c.getName());
        tx.commit();
        session.close();
    }

Result:

Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
Name:Computer Principle
Name:Computer Principle

Although Session is turned off, the secondary cache still exists, so only one SQL statement is generated.

The following example:

    @Override
    public void testCache() {
        // TODO Auto-generated method stub
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();  
        Query query = session.createQuery("from Course");  
        Iterator iter = query.iterate();  
        while(iter.hasNext()){  
               System.out.println(((Course)iter.next()).getName());  
        }
        tx.commit();
        session.close();
        
        session = sessionFactory.openSession();
        tx = session.beginTransaction(); 
        query = session.createQuery("from Course");  
        iter = query.iterate();  
        while(iter.hasNext()){  
               System.out.println(((Course)iter.next()).getName());  
        }
        tx.commit();
        session.close();
    }

Result:

Hibernate: 
    select
        course0_.ID as col_0_0_ 
    from
        clas course0_
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
//Computer Principle
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
//computer network
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
//Principle of database
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
C language
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
//College English A
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
Java
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
Linux
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
//Advanced mathematics
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
//Chinese
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
//College Physics
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
//software engineering
Hibernate: 
    select
        course0_.ID as col_0_0_ 
    from
        clas course0_
//Computer Principle
//computer network
//Principle of database
C language
//College English A
Java
Linux
//Advanced mathematics
//Chinese
//College Physics
//software engineering

It can be seen that the first generated SQL statement caches the ID of each object and generates an SQL statement query based on each ID when attribute values are needed. In the second Session, we first generate an SQL statement, get the ID, and then look up the object according to the ID. Because the second level cache is opened and the object is found in the second level cache, we output it directly, and do not generate the SQL statement according to each ID.

Whether it is a first-level cache or a second-level cache, only objects can be cached, and the values of attributes can not be cached. The following example:

    @Override
    public void testCache() {
        // TODO Auto-generated method stub
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();  
        Query query = session.createQuery("select c.name from Course c");   
        List<String> names = query.list();  
        for(Iterator iter = names.iterator(); iter.hasNext();){  
            String name = (String) iter.next();  
            System.out.println(name);  
        }  
        System.out.println("----------");  
        query = session.createQuery("select c.name from Course c");   
        names = query.list();  
        for(Iterator iter = names.iterator(); iter.hasNext();){  
            String name = (String) iter.next();  
            System.out.println(name);  
        } 
        System.out.println("----------"); 
        tx.commit();
        session.close();

    }

Operation results:

Hibernate: 
    select
        course0_.NAME as col_0_0_ 
    from
        clas course0_
Computer Principle
computer network
Principle of database
C language
College English A
Java
Linux
Advanced mathematics
Chinese
College Physics
software engineering
----------
Hibernate: 
    select
        course0_.NAME as col_0_0_ 
    from
        clas course0_
Computer Principle
computer network
Principle of database
C language
College English A
Java
Linux
Advanced mathematics
Chinese
College Physics
software engineering
----------

Although the second level cache is opened, the result of the query is not an object, but an attribute, so there is no cache. The second query still generates a query statement. To solve this problem, you need to query the cache.

3. Cache query

On the basis of configuring the secondary cache, you can set up the query cache and add a line to the settings of the Session Factory:

<prop key="hibernate.cache.use_query_cache">true</prop>

That is, the query cache is opened. The query cache is also a Session Factory level cache, which is valid throughout the Session Factory.

Close the secondary cache, run the following example, add setCacheable(true) after Query to open the query cache:

    @Override
    public void testCache() {
        // TODO Auto-generated method stub
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();  
        Query query = session.createQuery("select c.name from Course c");  
        query.setCacheable(true);  
        List<String> names = query.list();  
        for(Iterator iter = names.iterator(); iter.hasNext();){  
            String name = (String) iter.next();  
            System.out.println(name);  
        }  
        System.out.println("----------");  
        query = session.createQuery("select c.name from Course c");  
        query.setCacheable(true);  
        names = query.list();  
        for(Iterator iter = names.iterator(); iter.hasNext();){  
            String name = (String) iter.next();  
            System.out.println(name);  
        } 
        System.out.println("----------"); 
        tx.commit();
        session.close();

    }

Result:

Hibernate: 
    select
        course0_.NAME as col_0_0_ 
    from
        clas course0_
Computer Principle
computer network
Principle of database
C language
College English A
Java
Linux
Advanced mathematics
Chinese
College Physics
software engineering
----------
Computer Principle
computer network
Principle of database
C language
College English A
Java
Linux
Advanced mathematics
Chinese
College Physics
software engineering
----------

Because the HQL statements of the two queries are identical, only one SQL statement is generated. But if you change the second query:

        System.out.println("----------"); 
        query = session.createQuery("select c.name from Course c where c.id > 5");  
        query.setCacheable(true);  
        names = query.list();  
        for(Iterator iter = names.iterator(); iter.hasNext();){  
            String name = (String) iter.next();  
            System.out.println(name);  
        } 
        System.out.println("----------"); 

Result:

Hibernate: 
    select
        course0_.NAME as col_0_0_ 
    from
        clas course0_
Computer Principle
computer network
Principle of database
C language
College English A
Java
Linux
Advanced mathematics
Chinese
College Physics
software engineering
----------
Hibernate: 
    select
        course0_.NAME as col_0_0_ 
    from
        clas course0_ 
    where
        course0_.ID>5
College English A
Java
Linux
Advanced mathematics
Chinese
College Physics
software engineering
----------

Because the HQL statement has changed, the second time the SQL statement has been generated.

Query caching can either cache attributes or objects. When caching objects, only the ID of the objects is cached. The following example:

    @Override
    public void testCache() {
        // TODO Auto-generated method stub
        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();  
        Query query = session.createQuery("from Course");
        query.setCacheable(true);
        List<Course> list = query.list();
        for (int i=0; i<list.size(); i++){
            System.out.println(list.get(i).getName());  
        }
        System.out.println("----------"); 
        tx.commit();
        session.close();
        
        session = sessionFactory.openSession();
        tx = session.beginTransaction();        
        query = session.createQuery("from Course"); 
        query.setCacheable(true);
        list = query.list();
        for (int i=0; i<list.size(); i++){
            System.out.println(list.get(i).getName());  
        }
        System.out.println("----------"); 

        tx.commit();
        session.close();
    }

Results:

Hibernate: 
    select
        course0_.ID as ID0_,
        course0_.NAME as NAME0_,
        course0_.COMMENT as COMMENT0_ 
    from
        clas course0_
//Computer Principle
//computer network
//Principle of database
C language
//College English A
Java
Linux
//Advanced mathematics
//Chinese
//College Physics
//software engineering
----------
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
Hibernate: 
    select
        course0_.ID as ID0_0_,
        course0_.NAME as NAME0_0_,
        course0_.COMMENT as COMMENT0_0_ 
    from
        clas course0_ 
    where
        course0_.ID=?
//Computer Principle
//computer network
//Principle of database
C language
//College English A
Java
Linux
//Advanced mathematics
//Chinese
//College Physics
//software engineering
----------

Because the query cache is opened and the second level cache is not opened, the object ID is only cached in the first Session, and the whole object is not cached. So the HQL "from Course" in the second Session does not generate the SQL statement because it is the same as the previous one, but because there is no second level cache, the whole object is not cached, so the SQL statement can only be generated once according to each ID. If both the query cache and the secondary cache are opened at the same time, the second Session will no longer need to generate the SQL statement according to the ID:

Hibernate: 
    select
        course0_.ID as ID0_,
        course0_.NAME as NAME0_,
        course0_.COMMENT as COMMENT0_ 
    from
        clas course0_
Computer Principle
computer network
Principle of database
C language
College English A
Java
Linux
Advanced mathematics
Chinese
College Physics
software engineering
----------
Computer Principle
computer network
Principle of database
C language
College English A
Java
Linux
Advanced mathematics
Chinese
College Physics
software engineering
----------

Posted by amotaz on Fri, 22 Mar 2019 14:24:54 -0700