Chapter 11. Data Access using O/R Mappers

11.1. Introduction

Spring provides integration with Hibernate, JDO, and iBATIS SQL Maps in terms of resource management, DAO implementation support, and transaction strategies. For Hibernate there is first-class support with lots of IoC convenience features, addressing many typical Hibernate integration issues. All of these comply with Spring's generic transaction and DAO exception hierarchies.

Spring's adds significant support when using the O/R mapping layer of your choice to create data-access applications. First of all you should know that once you started using Spring's support for O/R mapping, you don't have to go all the way. No matter to what extent, you're invited to review and leverage the Spring approach, before deciding to take the effort and risk of building a similar infrastructure in-house. Much of the O/R mapping support, no matter what technology you're using may be used in a library style, as everything is designed as a set of reusable JavaBeans. Usage inside an ApplicationContext or BeanFactory does provide additional benefits in terms of ease of configuration and deployment; as such, most examples in this section show configuration inside an ApplicationContext.

Some of the the benefits of using Spring to create your O/R mapping applications include:

  • To avoid vendor lock-in, and allow mix-and-match implementation strategies. While Hibernate is powerful, flexible, open source and free, it still uses a proprietary API. Furthermore one could argue that iBatis is a bit lightweight, although it's excellent for use in application that don't require complex O/R mapping strategies. Given the choice, it's usually desirable to implement major application functionality using standard or abstracted APIs, in case you need to switch to another implementation for reasons of functionality, performance, or any other concerns. For example, Spring's abstraction of Hibernate Transactions and Exceptions, along with its IoC approach which allows you to easily swap in mapper/DAO objects implementing data-access functionality, makes it easy to isolate all Hibernate-specific code in one area of your application, without sacrificing any of the power of Hibernate. Higher level service code dealing with the DAOs has no need to know anything about their implementation. This approach has the additional benefit of making it easy to intentionally implement data-access with a mix-and-match approach (i.e. some data-access performed using Hibernate, and some using JDBC, others using iBatis) in a non-intrusive fashion, potentially providing great benefits in terms of continuing to use legacy code or leveraging the strength of each technology.

  • Ease of testing. Spring's inversion of control approach makes it easy to swap the implementations and locations of Hibernate session factories, datasources, transaction managers, and mapper object implementations (if needed). This makes it much easier to isolate and test each piece of persistence-related code in isolation.

  • General resource management. Spring application contexts can handle the location and configuration of Hibernate SessionFactories, JDBC datasources, iBatis SQLMaps configuration objects, and other related resources. This makes these values easy to manage and change. Spring offers efficient, easy and safe handling of Hibernate Sessions. Related code using Hibernate generally needs to use the same Hibernate Session object for efficiency and proper transaction handling. Spring makes it easy to transparently create and bind a session to the current thread, using either a declarative, AOP method interceptor approach, or by using an explicit, template wrapper class at the Java code level. Thus Spring solves many of the usage issues that repeatedly arise on the Hibernate forums.

  • Exception wrapping. Spring can wrap exceptions from you O/R mapping tool of choice, converting them from proprietary, checked exceptions, to a set of abstracted runtime exceptions. This allows you to handle most persistence exceptions, which are non-recoverable, only in the appropriate layers, without annoying boilerplate catches/throws, and exception declarations. You can still trap and handle exceptions anywhere you need to. Remember that JDBC exceptions (including DB specific dialects) are also converted to the same hierarchy, meaning that you can perform some operations with JDBC within a consistent programming model.

  • Integrated transaction management. Spring allows you to wrap your O/R mapping code with either a declarative, AOP style method interceptor, or an explicit 'template' wrapper class at the Java code level. In either case, transaction semantics are handled for you, and proper transaction handling (rollback, etc.) in case of exceptions is taken care of. As discussed below, you also get the benefit of being able to use and swap various transaction managers, without your Hibernate related code being affected. As an added benefit, JDBC-related code can fully integrate transactionally with the code you use to do O/R mapping. This is useful for handling functionality not implemented in, for example, Hibernate or iBatis.

11.2. Hibernate

11.2.1. Resource Management

Typical business applications are often cluttered with repetitive resource management code. Many projects try to invent their own solutions for this issue, sometimes sacrificing proper handling of failures for programming convenience. Spring advocates strikingly simple solutions for proper resource handling: Inversion of control via templating, i.e. infrastructure classes with callback interfaces, or applying AOP interceptors. The infrastructure cares for proper resource handling, and for appropriate conversion of specific API exceptions to an unchecked infrastructure exception hierarchy. Spring introduces a DAO exception hierarchy, applicable to any data access strategy. For direct JDBC, the JdbcTemplate class mentioned in a previous section cares for connection handling, and for proper conversion of SQLException to the DataAccessException hierarchy, including translation of database-specific SQL error codes to meaningful exception classes. It supports both JTA and JDBC transactions, via respective Spring transaction managers. Spring also offers Hibernate and JDO support, consisting of a HibernateTemplate / JdoTemplate analogous to JdbcTemplate, a HibernateInterceptor / JdoInterceptor, and a Hibernate / JDO transaction manager. The major goal is to allow for clear application layering, with any data access and transaction technology, and for loose coupling of application objects. No more business object dependencies on the data access or transaction strategy, no more hard-coded resource lookups, no more hard-to-replace singletons, no more custom service registries. One simple and consistent approach to wiring up application objects, keeping them as reusable and free from container dependencies as possible. All the individual data access features are usable on their own but integrate nicely with Spring's application context concept, providing XML-based configuration and cross-referencing of plain JavaBean instances that don't need to be Spring-aware. In a typical Spring app, many important objects are JavaBeans: data access templates, data access objects (that use the templates), transaction managers, business objects (that use the data access objects and transaction managers), web view resolvers, web controllers (that use the business objects), etc.

11.2.2. Resource Definitions in an Application Context

To avoid tying application objects to hard-coded resource lookups, Spring allows you to define resources like a JDBC DataSource or a Hibernate SessionFactory as beans in an application context. Application objects that need to access resources just receive references to such pre-defined instances via bean references (the DAO definition in the next section illustrates this). The following excerpt from an XML application context definition shows how to set up a JDBC DataSource and a Hibernate SessionFactory on top of it:

<beans>

    <bean id="myDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName">
            <value>java:comp/env/jdbc/myds</value>
        </property>
    </bean>

   <bean id="mySessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
       <property name="mappingResources">
           <list>
               <value>product.hbm.xml</value>
           </list>
       </property>
       <property name="hibernateProperties">
           <props>
               <prop key="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</prop>
           </props>
       </property>
       <property name="dataSource">
           <ref bean="myDataSource"/>
       </property>
   </bean>

   ...

</beans>

Note that switching from a JNDI-located DataSource to a locally defined one like a Jakarta Commons DBCP BasicDataSource is just a matter of configuration:

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName">
        <value>org.hsqldb.jdbcDriver</value>
    </property>
    <property name="url">
        <value>jdbc:hsqldb:hsql://localhost:9001</value>
    </property>
    <property name="username">
        <value>sa</value>
    </property>
    <property name="password">
        <value></value>
    </property>
</bean>

You can also use a JNDI-located SessionFactory, but that's typically not necessary outside an EJB context (see the "container resources vs local resources" section for a discussion).

11.2.3. Inversion of Control: Template and Callback

The basic programming model for templating looks as follows, for methods that can be part of any custom data access object or business object. There are no restrictions on the implementation of the surrounding object at all, it just needs to provide a Hibernate SessionFactory. It can get the latter from anywhere, but preferably as bean reference from a Spring application context - via a simple setSessionFactory bean property setter. The following snippets show a DAO definition in a Spring application context, referencing the above defined SessionFactory, and an example for a DAO method implementation.

<beans>

    <bean id="myProductDao" class="product.ProductDaoImpl">
        <property name="sessionFactory">
            <ref bean="mySessionFactory"/>
        </property>
    </bean>

    ...

</beans>
public class ProductDaoImpl implements ProductDao {

    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public List loadProductsByCategory(final String category) {
        HibernateTemplate hibernateTemplate =
            new HibernateTemplate(this.sessionFactory);

        return (List) hibernateTemplate.execute(
            new HibernateCallback() {
                public Object doInHibernate(Session session) throws HibernateException {
                    List result = session.find(
                        "from test.Product product where product.category=?",
                        category, Hibernate.STRING);
                    // do some further stuff with the result list
                    return result;
                }
            }
        );
    }
}

A callback implementation can effectively be used for any Hibernate data access. HibernateTemplate will ensure that Sessions are properly opened and closed, and automatically participate in transactions. The template instances are thread-safe and reusable, they can thus be kept as instance variables of the surrounding class. For simple single step actions like a single find, load, saveOrUpdate, or delete call, HibernateTemplate offers alternative convenience methods that can replace such one line callback implementations. Furthermore, Spring provides a convenient HibernateDaoSupport base class that provides a setSessionFactory method for receiving a SessionFactory, and getSessionFactory and getHibernateTemplate for use by subclasses. In combination, this allows for very simple DAO implementations for typical requirements:

public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {

    public List loadProductsByCategory(String category) {
        return getHibernateTemplate().find(
            "from test.Product product where product.category=?", category,
            Hibernate.STRING);
    }
}

11.2.4. Applying an AOP Interceptor Instead of a Template

An alternative to using a HibernateTemplate is Spring's AOP HibernateInterceptor, replacing the callback implementation with straight Hibernate code within a delegating try/catch block, and a respective interceptor configuration in the application context. The following snippets show respective DAO, interceptor, and proxy definitions in a Spring application context, and an example for a DAO method implementation.

<beans>

    ...

    <bean id="myHibernateInterceptor" 
        class="org.springframework.orm.hibernate.HibernateInterceptor">
        <property name="sessionFactory">
            <ref bean="mySessionFactory"/>
        </property>
    </bean>

    <bean id="myProductDaoTarget" class="product.ProductDaoImpl">
        <property name="sessionFactory">
            <ref bean="mySessionFactory"/>
        </property>
    </bean>

    <bean id="myProductDao" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyInterfaces">
            <value>product.ProductDao</value>
        </property>
        <property name="interceptorNames">
            <list>
                <value>myHibernateInterceptor</value>
                <value>myProductDaoTarget</value>
            </list>
        </property>
    </bean>

    ...

</beans>
public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {

    public List loadProductsByCategory(final String category) throws MyException {
        Session session = SessionFactoryUtils.getSession(getSessionFactory(), false);
        try {
            List result = session.find(
                "from test.Product product where product.category=?",
                category, Hibernate.STRING);
            if (result == null) {
                throw new MyException("invalid search result");
            }
            return result;
        }
        catch (HibernateException ex) {
            throw SessionFactoryUtils.convertHibernateAccessException(ex);
        }
    }
}

This method will only work with a HibernateInterceptor for it, caring for opening a thread-bound Session before and closing it after the method call. The "false" flag on getSession makes sure that the Session must already exist; otherwise SessionFactoryUtils would create a new one if none was found. If there is already a SessionHolder bound to the thread, e.g. by a HibernateTransactionManager transaction, SessionFactoryUtils automatically takes part in it in any case. HibernateTemplate uses SessionFactoryUtils internally - it's all the same infrastructure. The major advantage of HibernateInterceptor is that it allows any checked application exception to be thrown within the data access code, while HibernateTemplate is restricted to unchecked exceptions within the callback. Note that one can often defer the respective checks and throwing of application exceptions to after the callback, though. The interceptor's major drawback is that it requires special setup in the context. HibernateTemplate's convenience methods offers simpler means for many scenarios.

11.2.5. Programmatic Transaction Demarcation

On top of such lower-level data access services, transactions can be demarcated in a higher level of the application, spanning any number of operations. There are no restrictions on the implementation of the surrounding business object here too, it just needs a Spring PlatformTransactionManager. Again, the latter can come from anywhere, but preferably as bean reference via a setTransactionManager method - just like the productDAO should be set via a setProductDao method. The following snippets show a transaction manager and a business object definition in a Spring application context, and an example for a business method implementation.

<beans>

    ...

    <bean id="myTransactionManager" 
        class="org.springframework.orm.hibernate.HibernateTransactionManager">
        <property name="sessionFactory">
            <ref bean="mySessionFactory"/>
        </property>
    </bean>

    <bean id="myProductService" class="product.ProductServiceImpl">
        <property name="transactionManager">
            <ref bean="myTransactionManager"/>
        </property>
        <property name="productDao">
            <ref bean="myProductDao"/>
        </property>
    </bean>

</beans>
public class ProductServiceImpl implements ProductService {

    private PlatformTransactionManager transactionManager;
    private ProductDao productDao;

    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    public void setProductDao(ProductDao productDao) {
        this.productDao = productDao;
    }

    public void increasePriceOfAllProductsInCategory(final String category) {
        TransactionTemplate transactionTemplate = new TransactionTemplate(this.transactionManager);
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        transactionTemplate.execute(
            new TransactionCallbackWithoutResult() {
                public void doInTransactionWithoutResult(TransactionStatus status) {
                    List productsToChange = productDAO.loadProductsByCategory(category);
                    ...
                }
            }
        );
    }
}

11.2.6. Declarative Transaction Demarcation

Alternatively, one can use Spring's AOP TransactionInterceptor, replacing the transaction demarcation code with an interceptor configuration in the application context. This allows you to keep business objects free of repetitive transaction demarcation code in each business method. Furthermore, transaction semantics like propagation behavior and isolation level can be changed in a configuration file and do not affect the business object implementations.

<beans>

    ...

    <bean id="myTransactionManager" 
        class="org.springframework.orm.hibernate.HibernateTransactionManager">
        <property name="sessionFactory">
            <ref bean="mySessionFactory"/>
        </property>
    </bean>

    <bean id="myTransactionInterceptor" 
        class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <property name="transactionManager">
            <ref bean="myTransactionManager"/>
        </property>
        <property name="transactionAttributeSource">
            <value>
                product.ProductService.increasePrice*=PROPAGATION_REQUIRED
                product.ProductService.someOtherBusinessMethod=PROPAGATION_MANDATORY
            </value>
        </property>
    </bean>

    <bean id="myProductServiceTarget" class="product.ProductServiceImpl">
        <property name="productDao">
            <ref bean="myProductDao"/>
        </property>
    </bean>

    <bean id="myProductService" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyInterfaces">
            <value>product.ProductService</value>
        </property>
        <property name="interceptorNames">
            <list>
                <value>myTransactionInterceptor</value>
                <value>myProductServiceTarget</value>
            </list>
        </property>
    </bean>

</beans>
public class ProductServiceImpl implements ProductService {

    private ProductDao productDao;

    public void setProductDao(ProductDao productDao) {
        this.productDao = productDao;
    }

    public void increasePriceOfAllProductsInCategory(final String category) {
        List productsToChange = this.productDAO.loadProductsByCategory(category);
        ...
    }

    ...

}

As with HibernateInterceptor, TransactionInterceptor allows any checked application exception to be thrown with the callback code, while TransactionTemplate is restricted to unchecked exceptions within the callback. TransactionTemplate will trigger a rollback in case of an unchecked application exception, or if the transaction has been marked rollback-only by the application (via TransactionStatus). TransactionInterceptor behaves the same way by default but allows configurable rollback policies per method. A convenient alternative way of setting up declarative transactions is TransactionProxyFactoryBean, particularly if there are no other AOP interceptors involved. TransactionProxyFactoryBean combines the proxy definition itself with transaction configuration for a particular target bean. This reduces the configuration effort to one target bean plus one proxy bean. Furthermore, you do not need to specify which interfaces or classes the transactional methods are defined in.

<beans>

    ...

    <bean id="myTransactionManager" 
        class="org.springframework.orm.hibernate.HibernateTransactionManager">
        <property name="sessionFactory">
            <ref bean="mySessionFactory"/>
        </property>
    </bean>

    <bean id="myProductServiceTarget" class="product.ProductServiceImpl">
        <property name="productDao">
            <ref bean="myProductDao"/>
        </property>
    </bean>

    <bean id="myProductService" 
        class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="transactionManager">
            <ref bean="myTransactionManager"/>
        </property>
        <property name="target">
            <ref bean="myProductServiceTarget"/>
        </property>
        <property name="transactionAttributes">
            <props>
                <prop key="increasePrice*">PROPAGATION_REQUIRED</prop>
                <prop key="someOtherBusinessMethod">PROPAGATION_MANDATORY</prop>
            </props>
        </property>
    </bean>

</beans>

11.2.7. Transaction Management Strategies

Both TransactionTemplate and TransactionInterceptor delegate the actual transaction handling to a PlatformTransactionManager instance, which can be a HibernateTransactionManager (for a single Hibernate SessionFactory, using a ThreadLocal Session under the hood) or a JtaTransactionManager (delegating to the JTA subsystem of the container) for Hibernate applications. You could even use a custom PlatformTransactionManager implementation. So switching from native Hibernate transaction management to JTA, i.e. when facing distributed transaction requirements for certain deployments of your application, is just a matter of configuration. Simply replace the Hibernate transaction manager with Spring's JTA transaction implementation. Both transaction demarcation and data access code will work without changes, as they just use the generic transaction management APIs. For distributed transactions across multiple Hibernate session factories, simply combine JtaTransactionManager as a transaction strategy with multiple LocalSessionFactoryBean definitions. Each of your DAOs then gets one specific SessionFactory reference passed into its respective bean property. If all underlying JDBC data sources are transactional container ones, a business object can demarcate transactions across any number of DAOs and any number of session factories without special regard, as long as it is using JtaTransactionManager as the strategy.

<beans>

    <bean id="myDataSource1" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName">
            <value>java:comp/env/jdbc/myds1</value>
        </property>
    </bean>

    <bean id="myDataSource2" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName">
            <value>java:comp/env/jdbc/myds2</value>
        </property>
    </bean>

    <bean id="mySessionFactory1" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
        <property name="mappingResources">
            <list>
                <value>product.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</prop>
            </props>
        </property>
        <property name="dataSource">
            <ref bean="myDataSource1"/>
        </property>
    </bean>

    <bean id="mySessionFactory2" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
        <property name="mappingResources">
            <list>
                <value>inventory.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">net.sf.hibernate.dialect.OracleDialect</prop>
            </props>
        </property>
        <property name="dataSource">
            <ref bean="myDataSource2"/>
        </property>
    </bean>

    <bean id="myTransactionManager" 
        class="org.springframework.transaction.jta.JtaTransactionManager"/>

    <bean id="myProductDao" class="product.ProductDaoImpl">
        <property name="sessionFactory">
            <ref bean="mySessionFactory1"/>
        </property>
    </bean>

    <bean id="myInventoryDao" class="product.InventoryDaoImpl">
        <property name="sessionFactory">
            <ref bean="mySessionFactory2"/>
        </property>
    </bean>

    <bean id="myProductServiceTarget" class="product.ProductServiceImpl">
        <property name="productDao">
            <ref bean="myProductDao"/>
        </property>
        <property name="inventoryDao">
            <ref bean="myInventoryDao"/>
        </property>
    </bean>

    <bean id="myProductService" 
        class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="transactionManager">
            <ref bean="myTransactionManager"/>
        </property>
        <property name="target">
            <ref bean="myProductServiceTarget"/>
        </property>
        <property name="transactionAttributes">
            <props>
                <prop key="increasePrice*">PROPAGATION_REQUIRED</prop>
                <prop key="someOtherBusinessMethod">PROPAGATION_MANDATORY</prop>
            </props>
        </property>
    </bean>

</beans>

Both HibernateTransactionManager and JtaTransactionManager allow for proper JVM-level cache handling with Hibernate - without container-specific transaction manager lookup or JCA connector (as long as not using EJB to initiate transactions). Additionally, HibernateTransactionManager can export the JDBC Connection used by Hibernate to plain JDBC access code. This allows for high level transaction demarcation with mixed Hibernate/JDBC data access completely without JTA, as long as just accessing one database!

Note, for an alternative approach to using TransactionProxyFactoryBean to declaratively demarcate transactions, please see Section 7.4.1, “BeanNameAutoProxyCreator, another declarative approach”.

11.2.8. Container Resources versus Local Resources

Spring's resource management allows for simple switching between a JNDI SessionFactory and a local one, same for a JNDI DataSource, without having to change a single line of application code. Whether to keep the resource definitions in the container or locally within the application, is mainly a matter of the transaction strategy being used. Compared to a Spring-defined local SessionFactory, a manually registered JNDI SessionFactory does not provide any benefits. If registered via Hibernate's JCA connector, there is the added value of transparently taking part in JTA transactions, especially within EJBs. An important benefit of Spring's transaction support is that it isn't bound to a container at all. Configured to any other strategy than JTA, it will work in a standalone or test environment too. Especially for the typical case of single-database transactions, this is a very lightweight and powerful alternative to JTA. When using local EJB Stateless Session Beans to drive transactions, you depend both on an EJB container and JTA - even if you just access a single database anyway, and just use SLSBs for declarative transactions via CMT. The alternative of using JTA programmatically requires a J2EE environment too. JTA does not just involve container dependencies in terms of JTA itself and of JNDI DataSources. For non-Spring JTA-driven Hibernate transactions, you have to use the Hibernate JCA connector, or extra Hibernate transaction code with JTATransaction being configured, for proper JVM-level caching. Spring-driven transactions can work with a locally defined Hibernate SessionFactory nicely, just like with a local JDBC DataSource - if accessing a single database, of course. Therefore you just have to fall back to Spring's JTA transaction strategy when actually facing distributed transaction requirements. Note that a JCA connector needs container-specific deployment steps, and obviously JCA support in the first place. This is far more hassle than deploying a simple web app with local resource definitions and Spring-driven transactions. And you often need the Enterprise Edition of your container, as e.g. WebLogic Express does not provide JCA. A Spring app with local resources and transactions spanning one single database will work in any J2EE web container (without JTA, JCA, or EJB) - like Tomcat, Resin, or even plain Jetty. Additionally, such a middle tier can be reused in desktop applications or test suites easily. All things considered: If you do not use EJB, stick with local SessionFactory setup and Spring's HibernateTransactionManager or JtaTransactionManager. You will get all benefits including proper transactional JVM-level caching and distributed transactions, without any container deployment hassle. JNDI registration of a Hibernate SessionFactory via the JCA connector only adds value for use within EJBs.

11.2.9. Samples

The Petclinic sample in the Spring distribution offers alternative DAO implementations and application context configurations for Hibernate, JDBC, and Apache OJB. Petclinic can therefore serve as working sample app that illustrates the use of Hibernate in a Spring web app. It also leverages declarative transaction demarcation with different transaction strategies.

11.3. JDO

ToDo

11.4. iBATIS

Through the org.springframework.orm.ibatis package, Spring supports iBATIS SqlMaps 1.3.x and 2.0. The iBATIS support much resembles Hibernate support in that it supports the same template style programming and just as with Hibernate, iBatis support works with Spring's exception hierarchy and let's you enjoy the all IoC features Spring has.

11.4.1. Overview and differences between 1.3.x and 2.0

Spring supports both iBATIS SqlMaps 1.3 and 2.0. First let's have a look at the differences between the two.

Table 11.1. iBATIS SqlMaps supporting classes for 1.3 and 2.0

Feature1.3.x2.0
Creation of SqlMapSqlMapFactoryBeanSqlMapClientFactoryBean
Template-style helper classSqlMapTemplateSqlMapClientTemplate
Callback to use MappedStatementSqlMapCallbackSqlMapClientCallback
Super class for DAOsSqlMapDaoSupportSqlMapClientDaoSupport

11.4.2. Setting up the SqlMap

Using iBATIS SqlMaps involves creating SqlMap configuration files containing statements and result maps. Spring takes care of loading those using the SqlMapFactoryBean or SqlMapClientFactoryBean where the latter is to be used in combination with SqlMaps 2.0.

		
public class Account {
	private String name;
	private String email;
	
	public String getName() {
		return this.name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getEmail() {
		return this.email;
	}
	
	public void setEmail(String email) {
		this.email = email;
	}
}

Suppose we would want to map this class. We'd have to create the following SqlMap. Using the query, we can later on retrieve users through their email addresses. Account.xml:

<sql-map name="Account">
	<result-map name="result" class="examples.Account">
		<property name="name" column="NAME" columnIndex="1"/>
		<property name="email" column="EMAIL" columnIndex="2"/>
	</result-map>
	
	<mapped-statement name="getAccountByEmail" result-map="result">
		select
			  ACCOUNT.NAME,
			  ACCOUNT.EMAIL
		from ACCOUNT
		where ACCOUNT.EMAIL = #value#
	</mapped-statement>
	
	<mapped-statement name="insertAccount">
		insert into ACCOUNT (NAME, EMAIL) values (#name#, #email#)
	</mapped-statement>
</sql-map>

After having defined the Sql Map, we have to create a configuration file for iBATIS (sqlmap-config.xml):

<sql-map-config>

	<sql-map resource="example/Account.xml"/>

</sql-map-config>

iBATIS loads resources from the classpath so be sure to add the Account.xml file to the classpath somewhere.

Using Spring, we can now very easily set up the SqlMap, using the SqlMapFactoryBean:

<bean id="sqlMap" class="org.springframework.orm.ibatis.SqlMapFactoryBean">
	<property name="configLocation"><value>WEB-INF/sqlmap-config.xml</value></property>
</bean>

11.4.3. Using SqlMapDaoSupport

The SqlMapDaoSupport class offers a supporting class similar to the HibernateDaoSupport and the JdbcDaoSupport types. Let's implement a DAO:

public class SqlMapAccountDao extends SqlMapDaoSupport implements AccountDao {

	public Account getAccount(String email) throws DataAccessException {
		return (Account) getSqlMapTemplate().executeQueryForObject("getAccountByEmail", email);
	}

	public void insertAccount(Account account) throws DataAccessException {
		getSqlMapTemplate().executeUpdate("insertAccount", account);
	}
}

As you can see, we're using the SqlMapTemplate to execute the query. Spring has initialized the SqlMap for us using the SqlMapFactoryBean and when setting up the SqlMapAccountDao as follows, you're all set to go:

<!-- for more information about using datasource, have a look at the JDBC chapter -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
	<property name="driverClassName"><value>${jdbc.driverClassName}</value></property>
	<property name="url"><value>${jdbc.url}</value></property>
	<property name="username"><value>${jdbc.username}</value></property>
	<property name="password"><value>${jdbc.password}</value></property>
</bean>

<bean id="accountDao" class="example.SqlMapAccountDao">
	<property name="dataSource"><ref local="dataSource"/></property>
	<property name="sqlMap"><ref local="sqlMap"/></property>
</bean>

11.4.4. Transaction management

It's pretty easy to add declarative transaction management to applications using iBATIS. Basically the only thing you need to do is adding a transaction manager to you application context and declaratively set your transaction boundaries using for example the TransactionProxyFactoryBean. More on this can be found in Chapter 7, Transaction management

TODO elaborate!