Saturday, February 8, 2014

How to enable caching in iBatis using OSCache

In previous post, we developed our first iBatis hello world application. Now lets make some additions to make it more advanced. I will start by adding caching capability.
Caching allows to retrieve data returned for specific query(s) to be stored in temporary memory, from where it can be fetched again for exactly same query. (Provided cache has not been refreshed from last fetch). If cache is refreshed, all queries will again go to database and store their data in cache.
iBatis can use multiple supported cache solution, and i am using here one of them i.e. OSCache.
Sections in this post:
Adding maven dependency
Update sql-map-config.xml
Update sql data mapping file
Test the code

Adding maven dependency

Update the pom.xml with following dependency for adding OSCache support to project.
?
1
2
3
4
5
6
7
8
9
10
11
12
13
<dependency>
    <groupId>opensymphony</groupId>
    <artifactId>oscache</artifactId>
    <version>2.4</version>
    <scope>compile</scope>
    
    <exclusions>
            <exclusion>
                    <groupId>javax.jms</groupId>
                    <artifactId>jms</artifactId>
            </exclusion>
    </exclusions>
</dependency>
OSCache is dependent on JMS also, which is not required for this demo… so exclude it. If you do not exclude it, you may find an error while updating the dependency using :
mvn eclipse:eclipse

Update sql-map-config.xml

To add caching support, update the settings tag in sql-map-config.xml file with cacheModelsEnabled=”true”.
The updated config file will look like this:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
xml version="1.0" encoding="UTF-8" ?>
        PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
        "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
     
    <settings useStatementNamespaces="true" cacheModelsEnabled="true"/>
     
    <transactionManager type="JDBC">
        <dataSource type="SIMPLE">
          <property name="JDBC.Driver" value="com.mysql.jdbc.Driver"/>
          <property name="JDBC.ConnectionURL"  value="jdbc:mysql://localhost:3306/demoDB"/>
          <property name="JDBC.Username" value="root"/>
          <property name="JDBC.Password" value="lg225295"/>
        </dataSource>
      </transactionManager>
     
    <sqlMap resource="user.xml"/>
 
</sqlMapConfig>

Update sql data mapping file

To enable caching for sql queries, two things needs to be done:
A) Define cache model like this:
?
1
2
3
4
<cacheModel id="cache-user" type="OSCACHE" readOnly="true">
        <flushInterval hours="24"></flushInterval>
        <property name="cache-size" value="50"></property>
</cacheModel>
  • id: Name of cache where all execution results will be stored for a query to which this model will be specified.
  • type: This attribute defines the cache model implementation used.
  • readOnly: This attribute defines that if cache will be used for reading only.
  • flushInterval: This tag defines the number of hours after which cache will be refreshed automatically.
  • cache-size: It defines that this cache will store maximum 50 statement’s results after which it will start deleting old cache and store new one.
B) Add cache model to SQL statements
Add cacheModel=”cache-user” to all select statements for which you want to store result in cache.
?
1
2
3
<select id="getUserById" parameterClass="java.lang.Integer" resultMap="userResultMap" cacheModel="cache-user">
       SELECT * FROM USERINFO WHERE ID = #value#
</select>

Test the code

Lets test the code.
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.howtodoinjava.ibatis.demo;
 
import java.io.Reader;
 
import com.howtodoinjava.ibatis.demo.dao.UserDao;
import com.howtodoinjava.ibatis.demo.dao.UserDaoIbatis;
import com.howtodoinjava.ibatis.demo.dto.UserTEO;
import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;
 
public class TestMain {
    public static void main(String[] args) throws Exception
    {
        UserDao manager = new UserDaoIbatis();
 
        Reader reader = Resources.getResourceAsReader("sql-maps-config.xml");
        SqlMapClient sqlmapClient = SqlMapClientBuilder.buildSqlMapClient (reader);
 
        /*UserTEO user = new UserTEO();
        user.setId(1);
        user.setName("Demo User");
        user.setPassword("password");
        user.setEmail("demo-user@howtodoinjava.com");
        user.setStatus(1);
 
        manager.addUser(user,sqlmapClient);
        */
        UserTEO createdUser = manager.getUserById(1, sqlmapClient);
        System.out.println(createdUser.getEmail());
 
        createdUser = manager.getUserById(1, sqlmapClient);
        System.out.println(createdUser.getEmail());
 
        createdUser = manager.getUserById(1, sqlmapClient);
        System.out.println(createdUser.getEmail());
 
        createdUser = manager.getUserById(1, sqlmapClient);
        System.out.println(createdUser.getEmail());
    }
}
 
Output:
 
DEBUG [main] - Cache 'user.cache-user': cache miss
DEBUG [main] - Created connection 32604499.
DEBUG [main] - {conn-100000} Connection
DEBUG [main] - {conn-100000} Preparing Statement:      SELECT * FROM USERINFO WHERE ID = ?
DEBUG [main] - {pstm-100001} Executing Statement:      SELECT * FROM USERINFO WHERE ID = ?
DEBUG [main] - {pstm-100001} Parameters: [1]
DEBUG [main] - {pstm-100001} Types: [java.lang.Integer]
DEBUG [main] - {rset-100002} ResultSet
DEBUG [main] - {rset-100002} Header: [ID, NAME, EMAIL, PASSWORD, STATUS]
DEBUG [main] - {rset-100002} Result: [1, Demo User, demo-user@howtodoinjava.com, password, 1]
DEBUG [main] - Cache 'user.cache-user': stored object 'com.howtodoinjava.ibatis.demo.dto.UserTEO@13917ef'
DEBUG [main] - Returned connection 32604499 to pool.
demo-user@howtodoinjava.com
DEBUG [main] - Cache 'user.cache-user': retrieved object 'com.howtodoinjava.ibatis.demo.dto.UserTEO@13917ef'
demo-user@howtodoinjava.com
DEBUG [main] - Cache 'user.cache-user': retrieved object 'com.howtodoinjava.ibatis.demo.dto.UserTEO@13917ef'
demo-user@howtodoinjava.com
DEBUG [main] - Cache 'user.cache-user': retrieved object 'com.howtodoinjava.ibatis.demo.dto.UserTEO@13917ef'
demo-user@howtodoinjava.com
As you can see, first user object is fetched from database, but last 3 calls get the user object from cache and database if not touch again.
Happy Learning !!

No comments: