basic

MyBatis Caching Using OSCache

In this example im trying to create a simple application using MyBatis cache ability. What is cache anyway? A cache is designed to reduce traffic between your application and the database by conserving data already loaded from the database and put it whether in memory or in file. Database access is necessary only when retrieving data that is not currently available in the cache. So basically not all queries are taken from database, but from cache instead.

As you can see here, MyBatis has lots of caching products. But on this example im using OSCache.
First, as always, a simple mysql table

CREATE TABLE contoh
    (
        nama VARCHAR(10) NOT NULL,
        alamat VARCHAR(200),
        PRIMARY KEY (nama)
    )
	
insert into contoh (nama, alamat) values ('edw', 'Jakarta');
insert into contoh (nama, alamat) values ('danu', 'Ciledug');
insert into contoh (nama, alamat) values ('kamplenk', 'Tangerang');
insert into contoh (nama, alamat) values ('tebek', 'BSD');
insert into contoh (nama, alamat) values ('nugie', 'Pamulang');
insert into contoh (nama, alamat) values ('samsu', 'Bandung');

And a simple java bean to represent my table,

package com.edw.bean;

import java.io.Serializable;

public class Contoh implements Serializable {
    private String nama;
    private String alamat;

	// setter and getter

    @Override
    public String toString(){
        return nama+" : "+alamat;
    }
}

And a simple java interface to create query method

package com.edw.mapper;

import com.edw.bean.Contoh;
import java.util.List;

public interface ContohMapper {    
    List<Contoh> selectAll();
}

My xml query, please take a look at line 5.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.edw.mapper.ContohMapper" >
    
	<cache type="org.mybatis.caches.oscache.OSCache"/>

    <resultMap id="ContohMap" type="com.edw.bean.Contoh" >
        <id column="nama" property="nama" jdbcType="VARCHAR" />
        <result column="alamat" property="alamat" jdbcType="VARCHAR" />
    </resultMap>
    
    <select id="selectAll" resultMap="ContohMap">
        SELECT * FROM contoh
    </select>
</mapper>

And my xml configuration to load all my xml queries,

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
   <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="UNPOOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost/test"/>
                <property name="username" value="root"/>
                <property name="password" value=""/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/edw/xml/ContohMapper.xml" />
    </mappers>
</configuration>

A java class to load all my xml configurations,

package com.edw.config;

import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class MyBatisSqlSessionFactory {

    protected static final SqlSessionFactory FACTORY;

    static {
        try {
            Reader reader = Resources.getResourceAsReader("com/edw/xml/Configuration.xml");
            FACTORY = new SqlSessionFactoryBuilder().build(reader);
        } catch (Exception e){
            throw new RuntimeException("Fatal Error.  Cause: " + e, e);
        }
    }

    public static SqlSessionFactory getSqlSessionFactory() {
        return FACTORY;
    }
}

Now i create my main java class.

package com.edw.main;

import com.edw.bean.Contoh;
import com.edw.config.MyBatisSqlSessionFactory;
import com.edw.mapper.ContohMapper;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;

public class Main {
    
    private Logger logger = Logger.getLogger(this.getClass());
    
    public Main(){       
    }
    
    private void execute(){
        // test caching by doing select queries for 10 times
        for (int i = 0; i < 10; i++) {
            SqlSession session = MyBatisSqlSessionFactory.getSqlSessionFactory().openSession();
            ContohMapper mapper = session.getMapper(ContohMapper.class);
            List<Contoh> contohs = mapper.selectAll();
            for (Contoh contoh : contohs) {
                logger.debug(contoh);
            }
            session.close();
            
            try {
                logger.debug("sleeping for 3 seconds");
                Thread.sleep(3000);
            } catch (Exception e) {
            }
        }        
    }

    public static void main(String[] args) {
        Main main = new Main();
        main.execute();
    }
}

Last, is create 2 properties file. One is for log4j configuration, and another one for oscache configuration.
This is my log4j.properties

# Global logging configuration
log4j.rootLogger=DEBUG,stdout

# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d [%c{1}] %-5p %c:%L - %m%n

And this is my oscache.properties

cache.capacity=1000
cache.memory=true
cache.algorithm=com.opensymphony.oscache.base.algorithm.LRUCache

If you run the application, you’ll notice that for the second queries it will take from cache instead of database. This is what written on my log console.

2012-05-20 02:06:11,687 [Connection] DEBUG java.sql.Connection:27 - ooo Connection Opened
2012-05-20 02:06:11,688 [AbstractConcurrentReadCache] DEBUG com.opensymphony.oscache.base.algorithm.AbstractConcurrentReadCache:694 - get called (key=1247278887:1786588787:com.edw.mapper.ContohMapper.selectAll:0:2147483647:SELECT * FROM contoh)
2012-05-20 02:06:11,688 [LoggingCache] DEBUG org.apache.ibatis.cache.decorators.LoggingCache:27 - Cache Hit Ratio [com.edw.mapper.ContohMapper]: 0.6666666666666666
2012-05-20 02:06:11,689 [Main] DEBUG com.edw.main.Main:31 - edw : Jakarta
2012-05-20 02:06:11,689 [Main] DEBUG com.edw.main.Main:31 - danu : Ciledug
2012-05-20 02:06:11,692 [Main] DEBUG com.edw.main.Main:31 - kamplenk : Tangerang
2012-05-20 02:06:11,692 [Main] DEBUG com.edw.main.Main:31 - tebek : BSD
2012-05-20 02:06:11,692 [Main] DEBUG com.edw.main.Main:31 - nugie : Pamulang
2012-05-20 02:06:11,693 [Main] DEBUG com.edw.main.Main:31 - samsu : Bandung
2012-05-20 02:06:11,693 [Connection] DEBUG java.sql.Connection:27 - xxx Connection Closed
2012-05-20 02:06:11,693 [Main] DEBUG com.edw.main.Main:36 - sleeping for 3 seconds
BUILD STOPPED (total time: 8 seconds)

This is my project structure on Netbeans7
mybatis oscache project structure

Creating an MD5 String using Java

MD5 is a simple cryptographic hashing algorithm widely used for various application. In this tutorial im trying to generate MD5 value from a string and then compare it to mysql’s MD5 query result.

This is my java code to generate MD5, im using java’s MessageDigest.

package com.edw.util;

import java.security.MessageDigest;
import org.junit.Test;

/**
 *
 * @author edw
 */
public class MD5Test {

    public MD5Test() {
    }

    public String hexStringFromBytes(byte[] b) {
        char[] hexChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        String hex = "";
        int msb;
        int lsb = 0;
        int i;

        for (i = 0; i < b.length; i++) {
            msb = ((int) b[i] & 0x000000FF) / 16;
            lsb = ((int) b[i] & 0x000000FF) % 16;
            hex = hex + hexChars[msb] + hexChars[lsb];
        }
        return hex;
    }

    @Test
    public void testMD5() throws Exception {
        MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
		
		// get md5 for word "PASSWORD"
        digest.update("PASSWORD".getBytes());
        byte[] passwordBytes = digest.digest();

		// result = 319f4d26e3c536b5dd871bb2c52e3178
        System.out.println(hexStringFromBytes(passwordBytes));		
    }
}

compared to mysql’s md5 function

you can see that MD5 strings generated by java and mysql are both the same.
(H)

Beginning MyBatis 3 Part 3 : How to Get Table’s Generated Ids

I have a very simple MySql table with an auto increament primary key,

CREATE TABLE sampah
(
    id INT(10) NOT NULL AUTO_INCREMENT,
    name VARCHAR(30),
    PRIMARY KEY (id)
)

my question is, how can i get my object’s generated primary key if i insert a new object to table “sampah”?

The answer is actually quite easy, as you can see here on my xml sql mapper, take a look an line 11.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.edw.mybatis.mapper.SampahMapper" >

    <resultMap id="SampahMap" type="com.edw.mybatis.bean.Sampah" >
        <id column="id" property="id" jdbcType="INTEGER" />
        <result column="name" property="name" jdbcType="VARCHAR" />
    </resultMap>

    <insert id="saveUsingXML" parameterType="com.edw.mybatis.bean.Sampah"
            useGeneratedKeys="true" keyProperty="id" >
    insert into sampah(name)
    values (#{name,jdbcType=VARCHAR})
    </insert>

</mapper>

Here is my main java class, you can see how i got my generated id in line 25.

package com.edw.mybatis.main;

import com.edw.mybatis.bean.Sampah;
import com.edw.mybatis.config.MyBatisSqlSessionFactory;
import com.edw.mybatis.mapper.SampahMapper;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;

public class Main {

    private Logger logger = Logger.getLogger(Main.class);

    public Main() {
    }

    private void testSampah() {
        SqlSession session = MyBatisSqlSessionFactory.getSqlSessionFactory().openSession();
        try {
            SampahMapper sampahMapper = session.getMapper(SampahMapper.class);
            Sampah sampah1 = new Sampah();
            sampah1.setName("satu satu");
            sampahMapper.saveUsingXML(sampah1);           

            // my generated ID
            logger.debug(sampah1.getId());

            session.commit();
        } finally {
            session.close();
        }
    }

    public static void main(String[] args) {
        Main main = new Main();
        main.testSampah();
    }
}

Easy isnt it? (H)

How to Handle Jasper Report’s NoSuchMethodException

I was generating an ordinary report, when suddenly i met a weird error. My Exception showed a net.sf.jasperreports.engine.JRException and a java.lang.NoSuchMethodException: Unknown property '' when im using Jasper Report’s JRBeanCollectionDataSource. I tought it was because my java bean is not Serializable, but changing my bean to Serializable still didnt fix my errors.
Here is my complete stacktrace

net.sf.jasperreports.engine.JRException: Error retrieving field value from bean : 
        at net.sf.jasperreports.engine.data.JRAbstractBeanDataSource.getBeanProperty(JRAbstractBeanDataSource.java:123)
        at net.sf.jasperreports.engine.data.JRAbstractBeanDataSource.getFieldValue(JRAbstractBeanDataSource.java:96)
        at net.sf.jasperreports.engine.data.JRBeanCollectionDataSource.getFieldValue(JRBeanCollectionDataSource.java:100)
        at net.sf.jasperreports.engine.fill.JRFillDataset.setOldValues(JRFillDataset.java:818)
        at net.sf.jasperreports.engine.fill.JRFillDataset.next(JRFillDataset.java:782)
        at net.sf.jasperreports.engine.fill.JRBaseFiller.next(JRBaseFiller.java:1433)
        at net.sf.jasperreports.engine.fill.JRVerticalFiller.fillReport(JRVerticalFiller.java:108)
        at net.sf.jasperreports.engine.fill.JRBaseFiller.fill(JRBaseFiller.java:908)
        at net.sf.jasperreports.engine.fill.JRBaseFiller.fill(JRBaseFiller.java:830)
        at net.sf.jasperreports.engine.fill.JRFiller.fillReport(JRFiller.java:85)
        at net.sf.jasperreports.engine.JasperFillManager.fillReport(JasperFillManager.java:624)

Caused by: java.lang.NoSuchMethodException: Unknown property ''
        at org.apache.commons.beanutils.PropertyUtilsBean.getSimpleProperty(PropertyUtilsBean.java:1122)
        at org.apache.commons.beanutils.PropertyUtilsBean.getNestedProperty(PropertyUtilsBean.java:686)
        at org.apache.commons.beanutils.PropertyUtilsBean.getProperty(PropertyUtilsBean.java:715)

Here is my source code snippet, take a look at line 12, that is where my exception happen.

  @Override
    public ActionForward execute(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {     

        List<Log> logs = logService.select();

        FileInputStream fis = new FileInputStream("/ejournal.jasper");
        BufferedInputStream bufferedInputStream = new BufferedInputStream(fis);

        Map<String, String> map = new HashMap<String, String>();
        JRBeanCollectionDataSource jrbcds = new JRBeanCollectionDataSource(logs);

        JasperReport jasperReport = (JasperReport) JRLoader.loadObject(bufferedInputStream);
        JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, map, jrbcds);
    }

Well actually it’s very easy to fix it, you should either

  • Remove the empty field descriptions from the JRXML.
  • Set the field descriptions to match the bean property names.
  • Pass false as isUseFieldDescription when creating the bean data source, e.g. new JRBeanCollectionDataSource(data, false).

This is how i fixed it,

 @Override
    public ActionForward execute(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response)
            throws Exception {     

        List<Log> logs = logService.select();

        FileInputStream fis = new FileInputStream("/ejournal.jasper");
        BufferedInputStream bufferedInputStream = new BufferedInputStream(fis);

        Map<String, String> map = new HashMap<String, String>();
        JRBeanCollectionDataSource jrbcds = new JRBeanCollectionDataSource(logs,false);

        JasperReport jasperReport = (JasperReport) JRLoader.loadObject(bufferedInputStream);
        JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, map, jrbcds);
    }

I hope it can help others, because i spend some ridicoulously amount of time looking for this workaround.
😉

Set MySQL Connection TimeOut

Sometimes you find a condition where your database connection (MySQL) is time out because of your queries are spending too much time. This is a hint on how you increase your MySQL time out configuration.

First you query your default connection timeout.

SHOW VARIABLES LIKE 'connect_timeout';

As you can see below, i have approximately 10 seconds before my mysql connection time out.

Next is updated it to 60 seconds.

SET GLOBAL connect_timeout=60;

Simple isn’t it. 😉