iBATIS DataMapper
Added by Christian Poitras, last edited by Christian Poitras on Dec 02, 2009
Labels: 
(None)


Spring integration with iBATIS 3

Creating SqlSessionFactory

We want an SqlSessionFactory that can handle Spring's transactions.

SqlSessionFactoryBean
package ca.qc.ircm.mapper;
   
   import java.io.IOException;
   import java.io.InputStreamReader;
   import java.io.Reader;
   import java.sql.Connection;
   import java.sql.SQLException;
   
   import javax.annotation.PostConstruct;
   import javax.annotation.PreDestroy;
   import javax.sql.DataSource;
   
   import org.apache.ibatis.builder.xml.XMLConfigBuilder;
   import org.apache.ibatis.mapping.Environment;
   import org.apache.ibatis.session.Configuration;
   import org.apache.ibatis.session.ExecutorType;
   import org.apache.ibatis.session.SqlSession;
   import org.apache.ibatis.session.SqlSessionFactory;
   import org.apache.ibatis.session.SqlSessionFactoryBuilder;
   import org.slf4j.Logger;
   import org.slf4j.LoggerFactory;
   import org.springframework.core.NestedIOException;
   import org.springframework.jdbc.datasource.DataSourceUtils;
   import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
   import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
   
   /**
   * SQL session factory that uses spring.
   *
   * @author poitrac
   */
   public class SqlSessionFactoryBean implements SqlSessionFactory {
   
   private final Logger logger = LoggerFactory.getLogger(SqlSessionFactoryBean.class);
   
   /**
   * iBATIS environment.
   */
   private Environment environment;
   /**
   * Location of iBATIS configuration files.
   */
   private org.springframework.core.io.Resource configLocation;
   /**
   * SQL session factory for iBATIS.
   */
   private SqlSessionFactory sqlSessionFactory;
   
   
   @PostConstruct
   public void buildFactory() throws IOException {
   logger.debug("Configuring ibatis with file: {}", configLocation);
   
   try {
   Reader reader = new InputStreamReader(configLocation.getInputStream());
   XMLConfigBuilder parser = new XMLConfigBuilder(reader);
   Configuration configuration = parser.parse();
   configuration.setEnvironment(environment);
   sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
   } catch (IOException e) {
   throw new NestedIOException("Failed to parse config resource: " + configLocation, e);
   }
   }
   @PreDestroy
   public void cleanFactory() {
   sqlSessionFactory = null;
   }
   
   public void setConfigLocation(org.springframework.core.io.Resource configLocation) {
   this.configLocation = configLocation;
   }
   public void setEnvironment(Environment environment) {
   this.environment = environment;
   }
   public Configuration getConfiguration() {
   return sqlSessionFactory.getConfiguration();
   }
   public SqlSession openSession() {
   return openSession(this.getConfiguration().getDefaultExecutorType());
   }
   public SqlSession openSession(boolean autoCommit) {
   return openSession(this.getConfiguration().getDefaultExecutorType(), autoCommit);
   }
   public SqlSession openSession(Connection connection) {
   return openSession(this.getConfiguration().getDefaultExecutorType(), connection);
   }
   public SqlSession openSession(ExecutorType execType) {
   DataSource dataSource = environment.getDataSource();
   Connection connection;
   try {
   if (dataSource instanceof TransactionAwareDataSourceProxy) {
   connection = dataSource.getConnection();
   } else {
   connection = DataSourceUtils.doGetConnection(dataSource);
   }
   return sqlSessionFactory.openSession(execType, connection);
   } catch (SQLException ex) {
   throw (new SQLErrorCodeSQLExceptionTranslator(dataSource)).translate("iBATIS operation", null, ex);
   }
   }
   public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
   return openSession(execType);
   }
   public SqlSession openSession(ExecutorType execType, Connection connection) {
   return sqlSessionFactory.openSession(execType, connection);
   }
   }

This SqlSessionFactory can supply a connection that is inside a transaction, but it cannot close it by itself. For this, we need a custom implementation of Transaction that closes Spring's transaction when appropriate.

SpringTransaction
package ca.qc.ircm.mapper;
   
   import java.sql.Connection;
   import java.sql.SQLException;
   
   import javax.sql.DataSource;
   
   import org.apache.ibatis.transaction.Transaction;
   import org.springframework.jdbc.datasource.DataSourceUtils;
   import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
   
   /**
   * iBATIS 3 transaction that is managed by Spring.
   *
   * @author poitrac
   */
   public class SpringTransaction implements Transaction {
   
   private final DataSource dataSource;
   private final Connection connection;
   
   SpringTransaction(DataSource dataSource, Connection connection) {
   this.connection = connection;
   this.dataSource = dataSource;
   }
   
   public void close() throws SQLException {
   if (dataSource instanceof TransactionAwareDataSourceProxy) {
   connection.close();
   } else {
   DataSourceUtils.doReleaseConnection(connection, dataSource);
   }
   }
   public void commit() throws SQLException {
   // Commit is done elsewhere.
       }
   public Connection getConnection() {
   return connection;
   }
   public void rollback() throws SQLException {
   // Rollback is done elsewhere.
       }
   }

We also need to supply a TransactionFactory that iBATIS will use to get our Transaction implementation.

SpringTransactionFactory
package ca.qc.ircm.mapper;
   
   import java.sql.Connection;
   import java.util.Properties;
   
   import javax.sql.DataSource;
   
   import org.apache.ibatis.transaction.Transaction;
   import org.apache.ibatis.transaction.TransactionFactory;
   
   /**
   * Factory for Spring transactions.
   *
   * @author poitrac
   */
   public class SpringTransactionFactory implements TransactionFactory {
   
   /**
   * Connection's data source.
   */
   private final DataSource dataSource;
   
   SpringTransactionFactory(DataSource dataSource) {
   this.dataSource = dataSource;
   }
   
   public Transaction newTransaction(Connection conn, boolean autoCommit) {
   return new SpringTransaction(dataSource, conn);
   }
   public void setProperties(Properties props) {
   }
   }

Now the spring.xml to glue everything together.

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:aop="http://www.springframework.org/schema/aop"
          xmlns:tx="http://www.springframework.org/schema/tx"
          xmlns:util="http://www.springframework.org/schema/util"
          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
              http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
              http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
   
   <!-- Initialize iBATIS -->
   <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
   <property name="driverClassName" value="${driver}"/>
   <property name="url" value="${url}"/>
   <property name="username" value="${username}"/>
   <property name="password" value="${password}"/>
   <property name="validationQuery" value="${pingquery}"/>
   <property name="poolPreparedStatements" value="true"/>
   </bean>
   
   <!-- iBATIS 3 -->
   <bean id="transactionFactory" class="ca.qc.ircm.mapper.SpringTransactionFactory">
   <constructor-arg index="0" ref="dataSource"/>
   </bean>
   <bean id="environment" class="org.apache.ibatis.mapping.Environment">
   <constructor-arg index="0" value="default"/>
   <constructor-arg index="1" ref="transactionFactory"/>
   <constructor-arg index="2" ref="dataSource"/>
   </bean>
   <bean id="sqlSessionFactory" class="ca.qc.ircm.mapper.SqlSessionFactoryBean">
   <property name="configLocation" value="classpath:ca/qc/ircm/mapper/ibatisConfig.xml"/>
   <property name="environment" ref="environment"/>
   </bean>
   </beans>

The iBATIS 3 configuration file is quite simple.

ibatisConfig.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
   <!DOCTYPE configuration
   PUBLIC "-//ibatis.apache.org//DTD Config 3.0//EN"
       "http://ibatis.apache.org/dtd/ibatis-3-config.dtd">
   
   <configuration>
   <!-- Type aliases -->
   
   <!-- Mappers -->
   <mappers>
   <mapper resource="ca/qc/ircm/mapper/SomeMapper.xml"/>
   </mappers>
   </configuration>


Site running on a free Atlassian Confluence Open Source Project License granted to OSS. Evaluate Confluence today.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.5.5 Build:#811 Jul 25, 2007) - Bug/feature request - Contact Administrators