The iBATIS support in the Spring Framework much resembles the JDBC support in that it supports the same template style programming, and as with JDBC and other ORM technologies, the iBATIS support works with Spring's exception hierarchy and lets you enjoy Spring's IoC features.
Transaction management can be handled through Spring's standard
facilities. No special transaction strategies are necessary for iBATIS,
because no special transactional resource involved other than a JDBC
Connection
. Hence, Spring's standard JDBC
DataSourceTransactionManager
or
JtaTransactionManager
are perfectly
sufficient.
Note | |
---|---|
Spring supports iBATIS 2.x. The iBATIS 1.x support classes are no longer provided. |
Using iBATIS SQL Maps involves creating SqlMap configuration files
containing statements and result maps. Spring takes care of loading
those using the SqlMapClientFactoryBean
. For the
examples we will be using the following Account
class:
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; } }
To map this Account
class with iBATIS 2.x we
need to create the following SQL map
Account.xml
:
<sqlMap namespace="Account"> <resultMap id="result" class="examples.Account"> <result property="name" column="NAME" columnIndex="1"/> <result property="email" column="EMAIL" columnIndex="2"/> </resultMap> <select id="getAccountByEmail" resultMap="result"> select ACCOUNT.NAME, ACCOUNT.EMAIL from ACCOUNT where ACCOUNT.EMAIL = #value# </select> <insert id="insertAccount"> insert into ACCOUNT (NAME, EMAIL) values (#name#, #email#) </insert> </sqlMap>
The configuration file for iBATIS 2 looks like this:
<sqlMapConfig> <sqlMap resource="example/Account.xml"/> </sqlMapConfig>
Remember that iBATIS loads resources from the class path, so be
sure to add theAccount.xml
file to the class
path.
We can use the SqlMapClientFactoryBean
in
the Spring container. Note that with iBATIS SQL Maps 2.x, the JDBC
DataSource
is usually specified on the
SqlMapClientFactoryBean
, which enables lazy
loading. This is the configuration needed for these bean
definitions:
<beans> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocation" value="WEB-INF/sqlmap-config.xml"/> <property name="dataSource" ref="dataSource"/> </bean> </beans>
The SqlMapClientDaoSupport
class offers a
supporting class similar to the SqlMapDaoSupport
.
We extend it to implement our DAO:
public class SqlMapAccountDao extends SqlMapClientDaoSupport implements AccountDao { public Account getAccount(String email) throws DataAccessException { return (Account) getSqlMapClientTemplate().queryForObject("getAccountByEmail", email); } public void insertAccount(Account account) throws DataAccessException { getSqlMapClientTemplate().update("insertAccount", account); } }
In the DAO, we use the pre-configured
SqlMapClientTemplate
to execute the queries,
after setting up the SqlMapAccountDao
in the
application context and wiring it with our
SqlMapClient
instance:
<beans> <bean id="accountDao" class="example.SqlMapAccountDao"> <property name="sqlMapClient" ref="sqlMapClient"/> </bean> </beans>
An SqlMapTemplate
instance can also be
created manually, passing in the SqlMapClient
as
constructor argument. The SqlMapClientDaoSupport
base
class simply preinitializes a
SqlMapClientTemplate
instance for us.
The SqlMapClientTemplate
offers a generic
execute
method, taking a custom
SqlMapClientCallback
implementation as argument. This
can, for example, be used for batching:
public class SqlMapAccountDao extends SqlMapClientDaoSupport implements AccountDao { public void insertAccount(Account account) throws DataAccessException { getSqlMapClientTemplate().execute(new SqlMapClientCallback() { public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException { executor.startBatch(); executor.update("insertAccount", account); executor.update("insertAddress", account.getAddress()); executor.executeBatch(); } }); } }
In general, any combination of operations offered by the native
SqlMapExecutor
API can be used in such a callback.
Any thrown SQLException
is converted automatically to
Spring's generic DataAccessException
hierarchy.
DAOs can also be written against plain iBATIS API, without any
Spring dependencies, directly using an injected
SqlMapClient
. The following example shows a
corresponding DAO implementation:
public class SqlMapAccountDao implements AccountDao { private SqlMapClient sqlMapClient; public void setSqlMapClient(SqlMapClient sqlMapClient) { this.sqlMapClient = sqlMapClient; } public Account getAccount(String email) { try { return (Account) this.sqlMapClient.queryForObject("getAccountByEmail", email); } catch (SQLException ex) { throw new MyDaoException(ex); } } public void insertAccount(Account account) throws DataAccessException { try { this.sqlMapClient.update("insertAccount", account); } catch (SQLException ex) { throw new MyDaoException(ex); } } }
In this scenario, you need to handle the
SQLException
thrown by the iBATIS API in a custom
fashion, usually by wrapping it in your own application-specific DAO
exception. Wiring in the application context would still look like it
does in the example for the
SqlMapClientDaoSupport
,
due to the fact that the plain iBATIS-based DAO still follows the
dependency injection pattern:
<beans> <bean id="accountDao" class="example.SqlMapAccountDao"> <property name="sqlMapClient" ref="sqlMapClient"/> </bean> </beans>