Thursday, 17 July 2008

iBatis in Action By Clinton Begin, Brandon Goodin and Larry Meadors

Just now i completed reading iBatis in Action By Clinton Begin, Brandon Goodin and Larry Meadors

Nice book written but surely hibernate is better suited in most applications than iBatis.But as said it is always better to learn other alternatives just case you may use in your future applications.

I wanted to share few important points i found from this book.

1) iBATIS is a generalized framework for quicker JDBC coding.

2) iBATIS uses Extensible Markup Language (XML) to encapsulate SQL. XML was chosen because of its general portability across platforms, its industrywide adoption,and the fact that it’s more likely to live as long as SQL than any other language and any file format. Using XML, iBATIS maps the inputs and outputs of the
statement. Most SQL statements have one or more parameters and produce some sort of tabulated results. That is, results are organized into a series of columns and rows. iBATIS allows you to easily map both parameters and results to properties of objects.

3) Nearly any well-written piece of software uses a layered design. A layered design separates the technical responsibilities of an application into cohesive parts that isolate the implementation details of a particular technology or interface.This layering approach is inspired by the Law of Demeter, which in one form states,
“Each layer should have only limited knowledge about other layers: only layers closely related to the current layer.”.The idea is that each layer will only talk to the layer directly below it.This ensures that the dependency flows only in one direction and avoids the typical “spaghetti code” that is common of applications designed without layers.

4) iBATIS is what is known as a data mapper.

Martin Fowler describes the Data Mapper pattern as follows:

A layer of Mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself.

5) iBATIS is different in that it does not directly tie classes to tables or fields to columns,but instead maps the parameters and results (i.e., the inputs and outputs) of a SQL statement to a class.iBATIS is an additional layer of indirection between the classes and the tables,allowing it more flexibility in how classes and tables can be mapped, without requiring any changes to the data model or the object model. The layer of indirection we’re talking about is in fact SQL. This extra layer of indirection allows iBATIS to do a better job of isolating the database design from the object model.This means relatively few dependencies exist between the two.

6) A framework like iBATIS offers opportunities to inject architectural benefits into your application.

7) When not to use iBATIS

Every framework is built around rules and constraints. Lower-level frameworks like JDBC provide a flexible and complete feature set, but they are harder and more tedious to use. High-level frameworks like object/relational mapping tools are much easier to use and save you a lot of work, but they are built with more assumptions and constraints that make them applicable to fewer applications.

iBATIS is a mid-level framework. It’s higher level than JDBC, but lower level than an object relational mapper. That puts iBATIS in a unique position that makes it applicable to a unique set of applications.

8) Features of iBatis

Simplicity—iBATIS is widely regarded as the simplest persistence framework available.

Productivity—Concise code and simple configuration reduces the code to 62 percent of the corresponding JDBC code.

Performance—Architectural enhancements like join mapping speed up data access.

Separation of concerns—iBATIS improves design to ensure future maintainability.

Division of labor—iBATIS helps break up the work to allow teams to leverage expertise.

Portability—iBATIS can be implemented for any full-featured programming language.

9) iBATIS is not an object/relational mapping (O/RM) tool; it is a query mapping tool.

10) The queryForObject() methods

The queryForObject() methods are used to get a single row from the database into a Java object, and come with two signatures:

Object queryForObject(String id, Object parameter) throws SQLException;
Object queryForObject(String id, Object parameter, Object result) throws SQLException;


The first version is the more commonly used one, and creates the returned object for you if it has a default constructor (and throws a runtime exception if it does not).The second form accepts an object that will be used as the return value—after running the mapped statement the properties will be set on it instead of creating a new object. The second form is useful if you have an object that cannot be easily created because of a protected constructor or the lack of a default constructor.Something to remember when using queryForObject() is that if the query returns more than one row, this method will throw an exception, because it checks to make sure that only one row is returned.

11) The queryForList() methods

The queryForList() methods are used to get one or more rows from the database into a List of Java objects, and like queryForObject(), they also come in two versions:

List queryForList(String id, Object parameter) throws SQLException;
List queryForList(String id, Object parameter, int skip, int max) throws SQLException;

The first method returns all of the objects that are returned by the mapped statement.The second returns only a subset of them—it skips ahead up to the number indicated by the skip parameter, and returns only max rows from the query. So, if you have a mapped statement that returns 100 rows but you only want the first set of 10 rows, you can pass in 0 for skip and 10 for max to get them. If you want the
second set of 10 records, you can repeat the call with a value of 10 for skip to get the next 10 rows.

12) The queryForMap() methods

The queryForMap() methods return a Map (instead of a List) of one or more rows from the database as Java objects. Just like the other query methods, it has two forms as well:

Map queryForMap(String id, Object parameter, String key) throws SQLException;
Map queryForMap(String id, Object parameter, String key, String value) throws SQLException;


The first method will execute a query and return a Map of objects, where the key to the objects is identified by the property named by the key parameter, and the value objects are the complete objects from the mapped statement. The second form will return a similar Map, but the objects will be the property of the objects
identified by the value parameter.This is one of those times when an example is worth a thousand words. Let’s consider an example where you have a mapped statement that returns a set of accounts. By using the first method, you could create a Map that had the accountId property as the key to the map and the full account bean as the value. Using the second method, you could create a Map that had the accountId property as the key to the map, and only the account name as the value:


Map accountMap = sqlMap.queryForMap(
"Account.getAll",
null,
"accountId");
System.out.println(accountMap);

accountMap = sqlMap.queryForMap(
"Account.getAll",
null,
"accountId",
"username");
System.out.println(accountMap);


Now that you have seen all the portions of the API that you need to start using iBATIS, let’s take a look at the different ways in which you can create mapped statement.

13) While iBATIS does not allow you to get a primitive result directly, it does allow you to get wrapped primitive results. For example, if you want a count of orders for a customer, you can do it as an Integer, but not as a primitive int, as the next example shows:


Integer count = (Integer)sqlMap.queryForObject("Account.getOrderCountByAccount",new Integer(1));


<select id="getOrderCountByAccount" resultClass="java.lang.Integer" >
select count(*) as value from order where accountId = #value#
</select>

14) iBATIS can map results into any type, but because it only returns Object instances, primitive values must be wrapped in one of the following: a simple value wrapper, a bean, or a Map.

15) Batch updates are one way to improve performance with iBATIS. By creating a batch of statements, the driver can perform such tasks as compression to improve performance.

One important tip for using batched statements is to wrap the batch in a single transaction. If you fail to do so, a new transaction will be started for each statement,and performance will suffer as the batch size grows.


Example:

public void saveOrder(SqlMapClient sqlMapClient, Order order) throws SQLException {
sqlMapClient.startTransaction();

try {
if (null == order.getOrderId()) {
sqlMapClient.insert("Order.insert", order);
} else {
sqlMapClient.update("Order.update", order);
}

sqlMapClient.startBatch();
sqlMapClient.delete("Order.deleteDetails", order);

for (int i=0;i<order.getOrderItems().size();i++) {
OrderItem oi = (OrderItem) order.getOrderItems().get(i);
oi.setOrderId(order.getOrderId());
sqlMapClient.insert("OrderItem.insert", oi);
}

sqlMapClient.executeBatch();
sqlMapClient.commitTransaction();
} finally {
sqlMapClient.endTransaction();
}
}


16) XML parameters

Using XML to pass parameters into a mapped statement can be accomplished with either a String value or a DOM object, both of which use the exact same structure.

Example



String parameter = "<parameter><id>1</id></parameter>";
Account retrievedAccount=(Account)SimpleExample.selectAccountByIdByXml(parameter);
System.out.println(retrievedAccount.getEmailAddress());

public static Account selectAccountByIdByXml (String id) throws SQLException {
return (Account) sqlMapper.queryForObject("selectAccountByIdByXml", id);
}


<select id="selectAccountByIdByXml" parameterClass="xml" resultClass="Account">
select
ACC_ID as id,
ACC_FIRST_NAME as firstName,
ACC_LAST_NAME as lastName,
ACC_EMAIL as emailAddress
from ACCOUNT
where ACC_ID = #id#
</select>

17) Mapping Inheritance

iBATIS supports inheritance hierarchies by using a special mapping called a discriminator.Using a discriminator you can determine the type of class to be instantiated based on a value in the database. The discriminator is a part of the Result Map and works much like a switch statement.

For example:
<resultMap id="document" class="testdomain.Document">
<result property="id" column="DOCUMENT_ID"/>
<result property="title" column="TITLE"/>
<result property="type" column="TYPE"/>
<discriminator column="TYPE" javaType="string" >
<subMap value="Book" resultMap="book"/>
<subMap value="Newspaper" resultMap="news"/>
</discriminator>
</resultMap>

18) The Data Access Object (DAO) pattern is used to hide the unique implementation quirks of these APIs. It provides a simple and common API for application developers so that the consumers of the data can be free of the complexities of the data access APIs.

By separating the data access implementation from the data access interface, we are able to provide a homogenous set of interfaces to heterogeneous data. An application can access data from multiple databases, each with a different underlying data access mechanism using a single consistent data access API.


About the Authors

Clinton Begin is a Senior Developer and Agile Mentor for ThoughtWorks Canada. He has been building enterprise applications for nine years based on platforms such as Java and .NET. Clinton has extensive experience with agile methodologies, persistence frameworks, and relational databases. He is the original creator of the iBATIS persistence framework, which he designed in response to the challenges faced by object oriented developers dealing with enterprise relational databases.

Brandon Goodin is an independent consultant residing in Franklin, TN. He has been involved in developing enterprise applications for over seven years, utilizing a varied set of languages and technologies. His industry experience spans manufacturing, health care, e-commerce, real estate and recreation. He has been contributing to the iBATIS project since 2003.

Larry Meadors is an independent consultant offering development, support, and training services. He has been building enterprise web applications with mutiple databases and multiple languages since the late 90s, and got involved with the iBATIS project way back in the 1.x days.

Hope you enjoy reading this book.

2 comments:

Anonymous said...

I chanced upon ur blog while facing a problem using iBatis. queryForList() only returns a List. I'd like to get a SortedSet instead. It appears there's no way to do that. Do you know anything about it ?

I'm using iBatis Cache and I want it to cache a SortedSet.

I was also curious as to why you have just written down stuff already in the iBatis user guide.

Also, iBatis and Hibernate can't really be compared. Therefore, one is not better that the other. Hibernate is when you develop from scratch. iBatis is a simple ORM mapping tool.

Anonymous said...

shanlz at yahoo dot com in case you have a solution to m iBatis problem.