Over a million developers have joined DZone.

Pitfalls of the MyBatis Caches With Apache Ignite

A tutorial on how to look at cache entries in Apache Ignite.

· Big Data Zone

Learn how you can maximize big data in the cloud with Apache Hadoop. Download this eBook now. Brought to you in partnership with Hortonworks.

UP1: New book "High performance in-memory computing with Apache Ignite" has been published and available at http://leanpub.com/ignite

A week ago, MyBatis and Apache Ignite announced of support apache ignite as a MyBatis cache (L2 cache). Technically MyBatis supports two levels of Caches:

  1. Local cache, which is always enabled by default
  2. L2 cache, optional

As Apache Ignite project is fast growing with lots of functionality, in this blog post we are going to examine the MyBatis support in some details.

The second level cache stores the entity data, but NOT the entities or objects themselves. The data is stored in a 'serialised' format which looks like a hash map where the key is the entity Id, and the value is a list of primitive values.

Here is an example how the cache entries looks like in Apache ignite:

Image title

Where:

Cache Key

CacheKey [
  idHash=1499858, hash=2019660929, checksum=800710994, 
  count=6, multiplier=37, hashcode=2019660929, 
  updateList=[
    com.blu.ignite.mapper.UserMapper.getUserObject, 0, 2147483647, 
    SELECT * FROM all_objects t where t.OBJECT_TYPE='TABLE' and t.object_name=?, 
    USERS, SqlSessionFactoryBean
  ]
]

Value class:
java.util.ArrayList

Cache Value:

[UserObject [
  idHash=243119413, hash=1658511469, owner=C##DONOTDELETE, 
  object_type=TABLE, object_id=94087, created=Mon Feb 15 13:59:41 MSK 2016, 
  object_name=USERS
]]

For the purposes of this example, I selected the 'all_objects' objects with the following queries from the Oracle Database:

SELECT count(*) FROM all_objects;

SELECT * FROM all_objects t where t.OBJECT_TYPE='TABLE' and t.object_name='EMP';

SELECT * FROM all_objects t where t.OBJECT_TYPE='TABLE';

In my case, this given query execution time is ~660 ms in average.

SELECT count(*) FROM all_objects;

And the next following query execution time is more than 700ms:

SELECT t.object_type, count(*) FROM all_objects t group by t.OBJECT_TYPE;

Lets add Apache Ignite as a second level (L2) cache and examine the result. If you want to know how to install and configure Apache Ignite with Spring and myBatis, please refer to my previous blog post. Moreover, all the source code can be found in github repositories.

As a quick start, lets add the myBatis maven dependency to the project:

<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ignite</artifactId>
    <version>1.0.0-beta1</version>
</dependency>

Then, specify it in the myBatis mapper XML definition as follows:

<mapper namespace="com.blu.ignite.mapper.UserMapper">

    <cache type="org.mybatis.caches.ignite.IgniteCacheAdapter" />

    <select id="getUserObject" parameterType="String" resultType="com.blu.ignite.dto.UserObject" useCache="true">
        SELECT * FROM all_objects t where t.OBJECT_TYPE='TABLE' and t.object_name=#{objectName}
    </select>
    <select id="getAllObjectsTypeByGroup" parameterType="String" resultType="com.blu.ignite.dto.UobjectGroupBy" useCache="true">
        SELECT t.object_type, count(*) as cnt FROM all_objects t group by t.OBJECT_TYPE
    </select>

    <select id="allObjectCount" parameterType="String" resultType="String" useCache="true">
        SELECT count(*) FROM all_objects
    </select>
</mapper>

I also have the following corresponding Java mapper interface:

public interface UserMapper {
    User getUser( String id);
    List<string> getUniqueJob();
    UserObject getUserObject(String objectName);
    String allObjectCount();
    List<uobjectgroupby> getAllObjectsTypeByGroup();
}

And the web service as follows:

@WebService(name = "BusinessRulesServices",
        serviceName="BusinessRulesServices",
        targetNamespace = "http://com.blu.rules/services")
public class WebServices {
    private UserServices userServices;

    @WebMethod(operationName = "getUserName")
    public String getUserName(String userId){
        User user = userServices.getUser(userId);
        return user.getuName();
    }

    @WebMethod(operationName = "getUserObject")
    public UserObject getUserObject(String objectName){
        return userServices.getUserObject(objectName);
    }

    @WebMethod(operationName = "getUniqueJobs")
    public List<string> getUniqueJobs(){
        return userServices.getUniqueJobs();
    }

    @WebMethod(exclude = true)
    public void setDao(UserServices userServices){
        this.userServices = userServices;
    }

  @WebMethod(operationName = "allObjectCount")
    public String allObjectCount(){
        return userServices.allObjectCount();
    }

    @WebMethod(operationName = "getAllObjectsTypeCntByGroup")
    public List<uobjectgroupby> getAllObjectsTypeCntByGroup(){
        return userServices.getAllObjectCntbyGroup();
    }
}

If i will invoke the web method 'getAllObjectsTypeCntByGroup' in soupUI, first time it will get very high response time, approximately 1700 ms, because the result is not in the cache. From the second times, response time will be ~4 to ~5 ms.

In Apache Ignite cache entry will look like as follows:

Cache Key

CacheKey [
  idHash=46158416, hash=1558187086, checksum=2921583030, 
  count=5, multiplier=37, hashcode=1558187086, 
  updateList=[
    com.blu.ignite.mapper.UserMapper.getAllObjectsTypeByGroup, 0, 2147483647, 
    SELECT t.object_type, count(*) as cnt FROM all_objects t group by t.OBJECT_TYPE, 
    SqlSessionFactoryBean
  ]
]

Value class:
java.util.ArrayList

Cache Value:

[UobjectGroupBy [idHash=2103707742, hash=1378996400, cnt=1, object_type=EDITION],
 UobjectGroupBy [idHash=333378159, hash=872886462, cnt=444, object_type=INDEX PARTITION], 
 UobjectGroupBy [idHash=756814918, hash=1462794064, cnt=32, object_type=TABLE SUBPARTITION], 
 UobjectGroupBy [idHash=931078572, hash=953621437, cnt=2, object_type=CONSUMER GROUP], 
 UobjectGroupBy [idHash=1778706917, hash=1681913927, cnt=256, object_type=SEQUENCE], 
 UobjectGroupBy [idHash=246231872, hash=1764800190, cnt=519, object_type=TABLE PARTITION],
 UobjectGroupBy [idHash=1138665719, hash=1030673983, cnt=4, object_type=SCHEDULE], 
 UobjectGroupBy [idHash=232948577, hash=1038362844, cnt=1, object_type=RULE], 
 UobjectGroupBy [idHash=1080301817, hash=646054631, cnt=310, object_type=JAVA DATA], 
 UobjectGroupBy [idHash=657724550, hash=1248576975, cnt=201, object_type=PROCEDURE], 
 UobjectGroupBy [idHash=295410055, hash=33504659, cnt=54, object_type=OPERATOR], 
 UobjectGroupBy [idHash=150727006, hash=499210168, cnt=2, object_type=DESTINATION], 
 UobjectGroupBy [idHash=1865360077, hash=727903197, cnt=9, object_type=WINDOW], 
 UobjectGroupBy [idHash=582342926, hash=1060308675, cnt=4, object_type=SCHEDULER GROUP], 
 UobjectGroupBy [idHash=1968399647, hash=1205380883, cnt=1306, object_type=PACKAGE], 
 UobjectGroupBy [idHash=1495061270, hash=1345537223, cnt=1245, object_type=PACKAGE BODY], 
 UobjectGroupBy [idHash=1328790450, hash=1823695135, cnt=228, object_type=LIBRARY], 
 UobjectGroupBy [idHash=1128429299, hash=1267824468, cnt=10, object_type=PROGRAM],
 UobjectGroupBy [idHash=760711193, hash=1240703242, cnt=17, object_type=RULE SET], 
 UobjectGroupBy [idHash=317487814, hash=61657487, cnt=10, object_type=CONTEXT], 
 UobjectGroupBy [idHash=1079028994, hash=1960895356, cnt=229, object_type=TYPE BODY], 
 UobjectGroupBy [idHash=276147733, hash=873140579, cnt=44, object_type=XML SCHEMA], 
 UobjectGroupBy [idHash=24378178, hash=1621363993, cnt=1014, object_type=JAVA RESOURCE], 
 UobjectGroupBy [idHash=1891142624, hash=90282027, cnt=10, object_type=DIRECTORY], 
 UobjectGroupBy [idHash=902107208, hash=1995006200, cnt=593, object_type=TRIGGER], 
 UobjectGroupBy [idHash=142411235, hash=444983119, cnt=14, object_type=JOB CLASS], 
 UobjectGroupBy [idHash=373966405, hash=1518992835, cnt=3494, object_type=INDEX], 
 UobjectGroupBy [idHash=580466919, hash=1394644601, cnt=2422, object_type=TABLE], 
 UobjectGroupBy [idHash=1061370796, hash=1861472837, cnt=37082, object_type=SYNONYM], 
 UobjectGroupBy [idHash=1609659322, hash=1543110475, cnt=6487, object_type=VIEW], 
 UobjectGroupBy [idHash=458063471, hash=1317758482, cnt=346, object_type=FUNCTION],
 UobjectGroupBy [idHash=1886921697, hash=424653540, cnt=7, object_type=INDEXTYPE], 
 UobjectGroupBy [idHash=1455482905, hash=1776171634, cnt=30816, object_type=JAVA CLASS], 
 UobjectGroupBy [idHash=49819096, hash=2110362533, cnt=2, object_type=JAVA SOURCE], 
 UobjectGroupBy [idHash=1916179950, hash=1760023032, cnt=10, object_type=CLUSTER], 
 UobjectGroupBy [idHash=1138808674, hash=215713426, cnt=2536, object_type=TYPE], 
 UobjectGroupBy [idHash=305229607, hash=340664529, cnt=23, object_type=JOB], 
 UobjectGroupBy [idHash=1365509716, hash=623631686, cnt=12, object_type=EVALUATION CONTEXT]
]

Conclusion:

Expensive database operation can be reduced by using L2 Cache. Properly using L2 cache in MyBatis can increase the application performance about 10 to 20 times. Apache Ignite in memory data grid is a very suitable candidate for this purpose, however, you can also try Hazelcast, EhCache or any other caching solutions. 

Hortonworks DataFlow is an integrated platform that makes data ingestion fast, easy, and secure. Download the white paper now.  Brought to you in partnership with Hortonworks

Topics:
mybatis ,apache ignite ,data grid ,orm ,mybatis-cache ,cache ,jcache

Published at DZone with permission of Shamim Bhuiyan. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}