You are on page 1of 31

Introduction to iBATIS with

Spring

Hitting the Sweet Spot Between JDBC


and ORM

Copyright 2007 SpringSource. Copying, publishing or distributing without express written permission is prohibited.
Topics in this Session

• Introduction
• Basic Configuration
• Parameter Options
• Joins
• Advanced Usage
• Summary

2
Topics in this Session

• Introduction
• Basic Configuration
• Parameter Options
• Joins
• Advanced Usage
• Summary

3
What Is iBATIS? (1)

• Positioned in the sweet spot between ORM


and JDBC
• Lets you write maps to convert between
SQL statements and objects
– Core idea is to map statements rather than
tables
• Lets you continue to use the power of SQL
– Separate XML files contain SQL, tuneable by a
DBA
– Dynamic SQL

4
What Is iBATIS? (2)

• Virtually all the same power/control of


JDBC with simple but very powerful data-
binding
• Has some ORM-like features
– Configurable ability to load associations
automatically
• Eager with a normal n+1 select strategy
• Eager with a join
• Lazy
– Inheritance mapping since 2.1.0
– But there is no change tracking

5
Simple Example

• Tell iBATIS which statement to execute,


and it takes care of doing the data-
binding against the bean/map
sqlMapClientTemplate.update(“insertRestaurant", restaurant);

<insert id="insertRestaurant" parameterClass="restaurant">


INSERT INTO t_restaurant (merchant_number, name)
VALUES (#number#, #name#)
</insert>

6
Topics in this Session

• Introduction
• Basic Configuration
• Parameter Options
• Joins
• Advanced Usage
• Summary

7
Wiring iBATIS

<sqlMapConfig>
<sqlMap resource="rewards/internal/restaurant/Restaurant.xml"/>
</sqlMapConfig>

• Configuration normally in this file, like


DataSource and transaction
management, will be taken care of by
Spring
– May include <typeHandler ../>
– Also global settings:
<settings useStatementNamespaces=“true”
cacheModelsEnabled=“true”
lazyLoadingEnabled=“true”/>

8
Data Mapping

• Data is mapped in and out of the


PreparedStatement/ResultSet three basic
ways:
– JavaBean properties
– “Simple” types (e.g., primitives)
– Maps (the name in the mapping is the key in
the map)

9
SqlMap Files

<sqlMap namespace="Restaurant">
<typeAlias alias="restaurant" type="rewards.internal.restaurant.Restaurant"/>
<resultMap id="restaurant-result" class="restaurant">
<result property="number" column="MERCHANT_NUMBER"/>
<result property="name" column="NAME"/>
</resultMap>

<select id="findByMerchantNumber"
resultMap="restaurant-result" parameterClass="string">
select MERCHANT_NUMBER, NAME from T_RESTAURANT
where MERCHANT_NUMBER = #value#
</select>
Substitutes the value passed in
<insert id="insertRestaurant" parameterClass="restaurant">
insert into T_RESTAURANT (MERCHANT_NUMBER, NAME)
values (#number#, #name#)
</insert>
</sqlMap> Bean property names from “restaurant”

10
SqlMapClientTemplate

• Consistent with Spring’s general DAO


support
– Translates SQLExceptions to the
DataAccessException hierarchy
– Participates in Spring-managed transactions
automatically
– Provides convenience methods

11
Wiring Your Objects In Spring

<bean id="sqlMapClient"
class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation" value="rewards/internal/sqlmap-config.xml"/>
<property name="dataSource" value="dataSource"/>
</bean>

<bean id="restaurantRepository"
class="rewards.internal.IBatisRestaurantRepository">
<constructor-arg ref="sqlMapClient" />
</bean>

public class IBatisRestaurantRepository implements RestaurantRepository {


private SqlMapClientTemplate sqlMapClientTemplate;

public IBatisRestaurantRepository(SqlMapClient sqlMapClient) {


sqlMapClientTemplate = new SqlMapClientTemplate(sqlMapClient);
}
....
}

12
SqlMapClientDaoSupport

<bean id="sqlMapClient"
class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation" value="rewards/internal/sqlmap-config.xml"/>
<property name="dataSource" value="dataSource"/>
</bean>

<bean id="restaurantRepository"
class="rewards.internal.IBatisRestaurantRepository">
<constructor-arg ref="sqlMapClient" /> configuration looks the same
</bean>

public class IBatisRestaurantRepository extends SqlMapClientDaoSupport


implements RestaurantRepository {

public void insertRestaurant(Restaurant restaurant) {


getSqlMapClientTemplate().update(“insertRestaurant", restaurant);
}

} Note: this is a minor convenience


template is created by superclass at the cost of required inheritance

13
Topics in this Session

• Introduction
• Basic Configuration
• Parameter Options
• Joins
• Advanced Usage
• Summary

14
Type Handlers

• If the type isn't a “simple” conversion


from the database to the Java object
(i.e., string, data, number, etc) then a
“type handler” is used to make the
conversion
• Common examples:
– Java5 enums
– “Y” and “N” to boolean
– Rich user data types

15
Writing A Type Handler

public class MonetaryAmountTypeHandlerCallback implements TypeHandlerCallback {


public Object getResult(ResultGetter getter) throws SQLException {
double amount = getter.getDouble();
return new MonetaryAmount(amount);
}

public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {


double amount = ((MonetaryAmount)parameter).asDouble();
setter.setDouble(amount);
}

public Object valueOf(String amount) {


return MonetaryAmount.valueOf(amount);
}
}

<sqlMapConfig>
<typeHandler javaType="common.money.MonetaryAmount"
callback="rewards.internal.MonetaryAmountTypeHandlerCallback"/>
...
</sqlMapConfig>

16
Inline Parameter Attributes

• You can specify various attributes on


parameters
– #propertyName,javaType=?,jdbcType=?,
mode=?,nullValue=?,handler=?,
numericScale=?#
• Generally most useful for stored-procedures
or to resolve ambiguity

17
Stored Procedures

• Specified with the <procedure ..>


element
• OUT/INOUT parameters are mutated in
the calling parameter object
<procedure id="myproc" parameterClass="map">
call some_proc(#aKey,jdbcType=VARCHAR,mode=INOUT#)
</procedure>

assertEquals("oldval", paramMap.get("aKey"));
sqlMapClientTemplate.update("myProc", paramMap);
assertEquals("newval", paramMap.get("aKey"));

18
Topics in this Session

• Introduction
• Basic Configuration
• Parameter Options
• Joins
• Advanced Usage
• Summary

19
1:1 or N:1 (Lazy)

<resultMap id="get-product-result" class="com.ibatis.example.Product">


<result property="id" column="prd_id"/>
<result property="description" column="prd_description"/>
<result property="category" column="prd_cat_id" select="getCategory"/>
</resultMap>

<resultMap id="get-category-result" class="com.ibatis.example.Category">


<result property="id" column="cat_id"/>
<result property="description" column="cat_description"/>
</resultMap>

<select id="getProduct" parameterClass="int" resultMap="get-product-result">


SELECT prd_id, prd_description, prd_cat_id FROM product
WHERE prd_id = #value#
</select>

<select id="getCategory" parameterClass="int" resultMap="get-category-result">


SELECT cat_id, cat_description FROM category WHERE cat_id = #value#
</select>

20
1:1 or N:1 (Eager)

<resultMap id="get-product-result" class="com.ibatis.example.Product">


<result property="id" column="prd_id"/>
<result property="description" column="prd_description"/>
<result property="category" resultMap="get-category-result"/>
</resultMap>

<resultMap id="get-category-result" class="com.ibatis.example.Category">


<result property="id" column="cat_id"/>
<result property="description" column="cat_description"/>
</resultMap>

<select id="getProduct" parameterClass="int" resultMap="get-product-result">


SELECT prd_id, prd_description, cat_id, cat_description
FROM product INNER JOIN category ON prd_cat_id = cat_id
WHERE prd_id = #value#
</select>

21
1:N (Lazy)

<sqlMap namespace="ProductCategory">
<resultMap id="categoryResult" class="com.ibatis.example.Category">
<result property="id" column="cat_id"/>
<result property="description" column="cat_description"/>
<result property="productList" column="cat_id"
select="getProductsByCategoryId"/>
</resultMap>

<select id="getProductsByCategoryId" parameterClass=“int”


resultClass="com.ibatis.example.Product">
SELECT prd_id as id, prd_description as description
FROM product WHERE cat_id = #value#
</resultMap>

<select id="getCategory" parameterClass="int" resultMap="categoryResult">


SELECT cat_id, cat_description
FROM category WHERE cat_id = #value#
</select>
</sqlMap>

22
1:N (Eager)

<sqlMap namespace="ProductCategory">
<resultMap id="categoryResult" class="com.ibatis.example.Category" groupBy="id">
<result property="id" column="cat_id"/>
<result property="description" column="cat_description"/> What to base “parent” on
<result property="productList" resultMap="ProductCategory.productResult"/>
</resultMap>
Must be java.util.Collection or java.util.List
<resultMap id="productResult" class="com.ibatis.example.Product">
<result property="id" column="prd_id"/>
<result property="description" column="prd_description"/>
Namespace is required
</resultMap>

<select id="getCategory" parameterClass="int" resultMap="categoryResult">


SELECT c.cat_id, c.cat_description, p.prd_id, p.prd_description
FROM category c LEFT OUTER JOIN product p ON c.cat_id = p.prd_cat_id
WHERE cat_id = #value#
</select>
</sqlMap>

23
Topics in this Session

• Introduction
• Basic Configuration
• Parameter Options
• Joins
• Advanced Usage
• Summary

24
ResultObjectFactory

• Objects are normally created using the


default no-arg constructor (i.e., using
class.newInstance())
• If you need more control, you can use
ResultObjectFactory, which behaves much
like Spring's FactoryBean interface

25
Dynamic Queries

<select id="findRestaurant" resultClass="restaurant" parameterClass="int">


SELECT merchant_number, name
FROM t_restaurant
<isNotNull prepend="WHERE">
merchant_number = #value#
</isNotNull>
</select>

• There are a number of ways to make decisions


and combine clauses, making this a powerful
way to do Query By Example and similar
techniques.

26
Batching

Batch operations MUST be inside a transaction

@Transactional(propagation = REQUIRED)
public void insertAccounts(List<Account> accounts) {
sqlMapClientTemplate.execute(new SqlMapClientCallback() {
public Object doInSqlMapClient(SqlMapExecutor executor)
throws SQLException {
executor.startBatch();
for (Account account : accounts) {
executor.update("insertAccount", account);
}
executor.executeBatch();
}
});
}

27
Oracle RefCursors
CREATE OR REPLACE PACKAGE BODY refs_pck IS
FUNCTION get_restrs RETURN REF_CURSOR_T
IS l_cursor REF_CURSOR_T;
BEGIN
OPEN l_cursor FOR SELECT merchant_number, name FROM t_restaurant;
RETURN l_cursor;
END get_restrs;
END refs_pck;
INSERT INTO t_restaurant VALUES(1,'Applebees');
INSERT INTO t_restaurant VALUES(2,'Chillis');

<sqlMap>
<parameterMap id="output" class="map">
Omitted for slide space
<parameter property="theList" javaType="java.sql.ResultSet"
jdbcType="ORACLECURSOR" mode="OUT" resultMap="restaurant-result" />
</parameterMap>
<procedure id="getRestrs" parameterMap="output">{ ? = call refs_pck.get_restrs }</procedure>
</sqlMap>

Map map = new HashMap(); // map to populate


sqlMap.queryForObject("getRestrs", map);
List<Restaurant> restaurants = (List<Restaurant>)map.get("theList");
assertEquals(2, restaurants.size());

28
Topics in this Session

• Introduction
• Basic Configuration
• Parameter Options
• Joins
• Advanced Usage
• Summary

29
Summary

• Spring simplifies configuration and use of iBATIS


(e.g., transparent transactions and exception
handling)
• iBATIS Strengths
– Good compromise between ORM and JDBC
– Helps to organize the tedious details
– Less ‘magic’ to understand
– Breaks SQL out of code
– Greatly simplifies data-binding
• iBATIS Limitations
– Not a substitute for ORM

30
Lab

Data access with iBATIS

Copyright 2007 SpringSource. Copying, publishing or distributing without express written permission is prohibited.