Dashboard > iBATIS DataMapper > Home > Spring integration with iBATIS 3
Spring integration with iBATIS 3
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