Yet Another Simple Swing, Spring and Ibatis Integration Example

I was working on my side project while drinking a cup of (C) when suddenly i had an idea, why cant i rely on Spring’s Transaction instead of Ibatis’ plain transaction. I see that Spring’s Transaction with annotation is much much simpler than Ibatis’ old fashioned transaction with try-catch and commit-rollback method. Okay, enough with the chit-chat. Let me show you the code.

First, i create a very simple database, with one table consist of 2 simple columns.

CREATE DATABASE 'test'
USE 'test'

CREATE TABLE 'contoh' (
  'nama' varchar(30) NOT NULL,
  'alamat' varchar(100) DEFAULT NULL,
  PRIMARY KEY ('nama')
) 

next step is creating a java bean for database mapping

package com.edw.bean;

public class Contoh {

    private String nama;
    private String alamat;

    public String getAlamat() {
        return alamat;
    }

    public void setAlamat(String alamat) {
        this.alamat = alamat;
    }

    public String getNama() {
        return nama;
    }

    public void setNama(String nama) {
        this.nama = nama;
    }
    
}

and an xml mapping for database queries, i named it contoh.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" 
"http://ibatis.apache.org/dtd/sql-map-2.dtd" >
<sqlMap namespace="contoh" >

  <resultMap id="result" class="com.edw.bean.Contoh">
        <result property="nama" column="NAMA" columnIndex="1"/>
        <result property="alamat" column="ALAMAT" columnIndex="2"/>
    </resultMap>

    <insert id="insertContoh" parameterClass="com.edw.bean.Contoh" >
    insert into contoh (nama, alamat)
    values (#nama:VARCHAR#, #alamat:VARCHAR#)
    </insert>

    <select id="selectAllContoh" resultMap="result">
    select nama, alamat
    from contoh
    </select>

</sqlMap>

and ibatis’ main xml configuration, sqlmapconfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMapConfig
PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">

<sqlMapConfig>

    <settings
          cacheModelsEnabled="true"
          enhancementEnabled="true"
          lazyLoadingEnabled="true"
          maxRequests="128"
          maxSessions="24"
          maxTransactions="12"
          defaultStatementTimeout="60"
          useStatementNamespaces="true"
       />

    <sqlMap resource="com/edw/sqlmap/contoh.xml"/>

</sqlMapConfig>

because im using Spring, i need one main xml configuration for my Spring app, i named it spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">


    <context:component-scan base-package="com.edw"/>
    <tx:annotation-driven transaction-manager="transactionManager"/>
    <context:annotation-config/>

    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="configLocation" value="com/edw/sqlmap/sqlmapconfig.xml"/>
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost/test"/>
        <property name="username" value="root"/>
        <property name="password" value="password"/>
    </bean>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

</beans>

and 1 singleton class to load my configuration,

package com.edw.config;

/**
 *
 * @author edw
 */
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationContextProvider {

    private Logger logger = Logger.getLogger(ApplicationContextProvider.class);
    private ApplicationContext applicationContext;
    private static final ApplicationContextProvider provider = new ApplicationContextProvider();

    private ApplicationContextProvider() throws ExceptionInInitializerError {
        try {
            this.applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        } catch (Throwable ex) {
            logger.error("Initial ApplicationContext creation failed -- " + ex.getMessage(), ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public synchronized static ApplicationContextProvider getInstance() throws ExceptionInInitializerError {
        return provider;
    }

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
}

i create a DAO class, to connect to my ibatis layer,

package com.edw.dao;

import com.edw.bean.Contoh;
import com.ibatis.sqlmap.client.SqlMapClient;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
import org.springframework.stereotype.Repository;

/**
 *
 * @author edw
 */
@Repository(value = "contohDao")
public class ContohDao extends SqlMapClientDaoSupport {

    // empty constructor
    public ContohDao() {       
    }
     
    // autowired constructor
    // i dont know why, but i cant autowired sqlMapClient directly
    // it will cause  java.lang.IllegalArgumentException: Property 'sqlMapClient' is required
    @Autowired
    public ContohDao(SqlMapClient sqlMapClient) {
        setSqlMapClient(sqlMapClient);
    }

    public void insert(Contoh contoh){
        getSqlMapClientTemplate().insert("contoh.insertContoh",contoh);
    }

    public List<Contoh> select(){
        return getSqlMapClientTemplate().queryForList("contoh.selectAllContoh");
    }
}

and my Service class, i use it to connect to my DAO layer,

package com.edw.service;

import com.edw.bean.Contoh;
import com.edw.dao.ContohDao;
import java.util.List;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

/**
 *
 * @author edw
 */
@Service("contohService")
public class ContohService  {

    @Autowired
    private ContohDao contohDao;
    
    private Logger logger = Logger.getLogger(ContohService.class);

    @Transactional(isolation=Isolation.SERIALIZABLE)
    public void insert(Contoh contoh) {
        logger.debug("begin insert at "+this.getClass().getName());
        contohDao.insert(contoh);
        logger.debug("end insert at "+this.getClass().getName());
    }

    public List<Contoh> select() {
        logger.debug("begin select at "+this.getClass().getName());
        List<Contoh> contohs = contohDao.select();
        logger.debug("end select at "+this.getClass().getName());
        return contohs;
    }    
}

please be careful with isolation level, from what i’ve read,

In a typical application, multiple transactions run concurrently, often working
with the same data to get their job done. Concurrency, while necessary, can lead
to the following problems:

  • Dirty read—Dirty reads occur when one transaction reads data that has been
    written but not yet committed by another transaction. If the changes are
    later rolled back, the data obtained by the first transaction will be invalid.
  • Nonrepeatable read—Nonrepeatable reads happen when a transaction performs
    the same query two or more times and each time the data is different.
    This is usually due to another concurrent transaction updating the data
    between the queries.
  • Phantom reads—Phantom reads are similar to nonrepeatable reads. These
    occur when a transaction (T1) reads several rows, and then a concurrent
    transaction (T2) inserts rows. Upon subsequent queries, the first transaction
    (T1) finds additional rows that were not there before.

That’s why, im using Isolation.SERIALIZABLE, because

ISOLATION_SERIALIZABLE
This fully ACID-compliant isolation level ensures that dirty
reads, nonrepeatable reads, and phantom reads are all
prevented. This is the slowest of all isolation levels
because it is typically accomplished by doing full table
locks on the tables involved in the transaction.

this is my UI

package com.edw.ui;

import com.edw.bean.Contoh;
import com.edw.config.ApplicationContextProvider;
import com.edw.service.ContohService;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;

/**
 *
 * @author edw
 */
public class MenuUtama extends JFrame implements ActionListener {

    private JTextField txtNama = new JTextField();
    private JTextField txtAlamat = new JTextField();
    private JButton cmdButton = new JButton("Save");
    private Logger logger = Logger.getLogger(MenuUtama.class);

    public MenuUtama() {
        setLayout(new GridLayout(3, 3));
        Container con = this.getContentPane();
        con.add(new JLabel("nama : "));
        con.add(txtNama);
        con.add(new JLabel("Alamat : "));
        con.add(txtAlamat);
        con.add(cmdButton);

        cmdButton.addActionListener(this);
    }

    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == cmdButton) {
            Contoh contoh = new Contoh();
            contoh.setNama(txtNama.getText());
            contoh.setAlamat(txtAlamat.getText());

            try {
                ApplicationContext ac = ApplicationContextProvider.getInstance().getApplicationContext();
                ContohService service = (ContohService) ac.getBean("contohService");
                service.insert(contoh);
                logger.debug("success");
            } catch (Exception ex) {
                logger.error(ex.getMessage(), ex);
            }
        }
    }

    public static void main(String[] args) {
        MenuUtama menuUtama = new MenuUtama();
        menuUtama.setVisible(true);
        menuUtama.setSize(300, 150);
        menuUtama.setLocationRelativeTo(null);
    }
}

this is my junit test case

package com.edw.test;

import com.edw.bean.Contoh;
import com.edw.config.ApplicationContextProvider;
import com.edw.service.ContohService;
import java.util.List;
import org.apache.log4j.Logger;
import org.junit.Test;
import org.springframework.context.ApplicationContext;


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

    @Test
    public void testingMain(){

        Logger logger = Logger.getLogger(TestKoneksi.class);

        ApplicationContext ac = ApplicationContextProvider.getInstance().getApplicationContext();
        ContohService service = (ContohService)ac.getBean("contohService");
        List<Contoh> contohs = service.select();

        for (Contoh contoh : contohs) {
            logger.debug(contoh.getNama()+" - "+contoh.getAlamat());
        }

        Contoh contoh = new Contoh();
        contoh.setNama("Ngorroc");
        contoh.setAlamat("Morroc");
        service.insert(contoh);

    }
}

this is the appereance of my swing app
ibatis swing spring app

this is what written on my console

[SqlMapClientTemplate] DEBUG org.springframework.orm.ibatis.SqlMapClientTemplate:177 - Opened SqlMapSession [com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl@b05acd] for iBATIS operation
[Connection] DEBUG java.sql.Connection:27 - {conn-100000} Connection
[SqlMapClientTemplate] DEBUG org.springframework.orm.ibatis.SqlMapClientTemplate:194 - Obtained JDBC Connection [com.mysql.jdbc.JDBC4Connection@17ec9f7] for iBATIS operation
[Connection] DEBUG java.sql.Connection:27 - {conn-100000} Preparing Statement:      insert into contoh (nama, alamat)     values (?, ?)     
[PreparedStatement] DEBUG java.sql.PreparedStatement:27 - {pstm-100001} Executing Statement:      insert into contoh (nama, alamat)     values (?, ?)     
[PreparedStatement] DEBUG java.sql.PreparedStatement:27 - {pstm-100001} Parameters: [ibatisInsert, testing masuk Ibatis]
[PreparedStatement] DEBUG java.sql.PreparedStatement:27 - {pstm-100001} Types: 1
[ContohService] DEBUG com.edw.service.ContohService:28 - end insert at com.edw.service.ContohService
[DataSourceTransactionManager] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager:730 - Initiating transaction commit
[DataSourceTransactionManager] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager:259 - Committing JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@17ec9f7]
[DataSourceUtils] DEBUG org.springframework.jdbc.datasource.DataSourceUtils:193 - Resetting isolation level of JDBC Connection [com.mysql.jdbc.JDBC4Connection@17ec9f7] to 4
[DataSourceTransactionManager] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager:314 - Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@17ec9f7] after transaction
[DataSourceUtils] DEBUG org.springframework.jdbc.datasource.DataSourceUtils:312 - Returning JDBC Connection to DataSource
[MenuUtama] DEBUG com.edw.ui.MenuUtama:51 - success

my NB Project Structure and libraries that i used
Netbeans Project structure, libraries and stuffs

btw, i highly recommend these books to understand more about Spring and Ibatis framework.

  1. Manning Spring in Action 2nd Edition
  2. Manning Ibatis in Action

thank you, have fun and good luck.
:-[

Google+

2 Comments

safii

about 5 years ago

mohon di bantu, setalah mencoba tutor diatas saya mengalami masalah error seperti ini : Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: Could not initialize class id.co.insurance.config.ApplicationContextProvider at id.co.insurance.ui.MenuUtama.actionPerformed(MenuUtama.java:65) at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2018) at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2341) at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402) at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259) at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252) at java.awt.Component.processMouseEvent(Component.java:6504) at javax.swing.JComponent.processMouseEvent(JComponent.java:3321) at java.awt.Component.processEvent(Component.java:6269) at java.awt.Container.processEvent(Container.java:2229) at java.awt.Component.dispatchEventImpl(Component.java:4860) at java.awt.Container.dispatchEventImpl(Container.java:2287) at java.awt.Component.dispatchEvent(Component.java:4686) at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4832) at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4492) at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4422) at java.awt.Container.dispatchEventImpl(Container.java:2273) at java.awt.Window.dispatchEventImpl(Window.java:2713) at java.awt.Component.dispatchEvent(Component.java:4686) at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:707) at java.awt.EventQueue.access$000(EventQueue.java:101) at java.awt.EventQueue$3.run(EventQueue.java:666) at java.awt.EventQueue$3.run(EventQueue.java:664) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87) at java.awt.EventQueue$4.run(EventQueue.java:680) at java.awt.EventQueue$4.run(EventQueue.java:678) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76) at java.awt.EventQueue.dispatchEvent(EventQueue.java:677) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:211) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:128) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:117) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:113) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:105) at java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

Reply

edwin

about 5 years ago

Hi safii, sorry bro, kurang lengkap errornya sekilas sih kayaknya ada class yg ketinggalan atau konfigurasi ApplicationContext.xml-nya yg agak kurang

Leave a Comment

Please be polite. We appreciate that.
Your email address will not be published and required fields are marked


:-[ (B) (^) (P) (@) (O) (D) :-S ;-( (C) (&) :-$ (E) (~) (K) (I) (L) (8) :-O (T) (G) (F) :-( (H) :-) (*) :-D (N) (Y) :-P (U) (W) ;-)