Depending on how long you've been writing Java code, you may remember the days when you wrote a lot of classes with logic inside methods that mapped data in Java objects to and from the data in a relational database management system (RDBMS). For the most part, this manual intervention is now strongly discouraged, in favor of using ORM tools as a best practice.
ORM tools allow you to configure the mapping between data elements in a relational database with Java object properties. When configured, these tools allow you to use your Java objects without worrying about the details of how the data in the properties of the Java classes are put away or retrieved, freeing you from writing, debugging, and handling errors in a lot of repetitious code.
This article introduces the new features in iBATIS 3, an ORM tool from the Apache Foundation that you can use to build Java applications that connect to databases. To get the most out of this article, you should have the Java Development Kit (JDK) V5 or newer and Eclipse V3.4 or newer. This article uses iBATIS 3 beta 9. The iBATIS site indicates that beta 9 is close to general availability (GA), so the examples in this article should also apply to the GA version when it's available.
Because the purpose of iBATIS is to do mapping to an RDBMS, a database is also required to get the full picture of the examples. The examples in this article use Apache Derby as the database of choice. It's important to note that, in addition to relieving you from writing repetitious Java code that uses Java Database Connectivity (JDBC), ORM tools provide the advantage of offering better abstraction from the data layer. With a few changes to the iBATIS configuration and references to the correct JDBC libraries, you should be able to use the examples in this article with other databases.
iBATIS 3 is a persistence framework that allows you to configure mappings between the properties on your Java classes and table columns in an RDBMS. When configured, the framework handles the JDBC connection and assignments for you. You can configure iBATIS 3 using XML files. You can download the iBATIS framework as a compressed (ZIP) file archive from the iBATIS site (see Resources). Inside the archive is a Java Archive (JAR) file that you include in your Java project to provide the classes that you use.
Listing 1 contains an example of a Java class used by the example application.
Listing 1. The
Automobile class used in the sample application
package com.ibm.developerWorks.examples.ibatis.model;
public class Automobile {
private int id;
private String make;
private String model;
private int year;
public Automobile() {
super();
}
public Automobile(final int id, final String make, final String model,
final int year) {
super();
this.id = id;
this.make = make;
this.model = model;
this.year = year;
}
public int getId() {
return id;
}
public String getMake() {
return make;
}
public String getModel() {
return model;
}
public int getYear() {
return year;
}
public void setId(final int id) {
this.id = id;
}
public void setMake(final String make) {
this.make = make;
}
public void setModel(final String model) {
this.model = model;
}
public void setYear(final int year) {
this.year = year;
}
@Override
public String toString() {
return "Automobile [id=" + id + ", make=" + make + ", model=" + model + ",
year=" + year + "]";
}
}
|
This Automobile class is a simple plain old
Java object (POJO) that contains data used by the application. The iBATIS
framework, when configured, is able to persist this object to the database
or return it as a result of a method that selects it from the database.
Listing 2 shows a SQL script that creates the sample database table.
Listing 2. The SQL script for creating the automobiles table
CREATE TABLE automobiles (
id INT NOT NULL,
make VARCHAR(255) NOT NULL,
model VARCHAR(255) NOT NULL,
model_year INT NOT NULL
);
|
Execute this database script to create the table in your database. If
you're using Derby as your database, you can run this script using the
command-line utility that comes with Derby in the bin folder (see Listing 3). Before running the example, make sure
you assign the DERBY_HOME variable to the full
path to the directory in which Derby is installed and save the SQL script
in a file called create.sql.
Listing 3. Using the Derby command-line ij tool to run create.sql
$ cd $DERBY_HOME/bin
$ ./ij
> connect 'jdbc:derby:/tmp/MyDB';
> run create.sql
|
Listing 4 shows the XML mapping file that allows you to map the properties in the Java class to the data columns in the data table.
Listing 4. The XML mapping file (automobile-mapper.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<mapper namespace="com.ibm.developerWorks.examples.ibatis.model.Automobile">
<resultMap type="Automobile" id="automobileResult">
<result column="id" property="id" />
<result column="make" property="make" />
<result column="model" property="model" />
<result column="model_year" property="year" />
</resultMap>
<select id="select" parameterType="int" resultType="Automobile"
resultMap="automobileResult">
select * from
automobiles where id = #{id}
</select>
<insert id="insert" parameterType="Automobile">
insert into automobiles (id,
model, make, model_year)
values (#{id}, #{model}, #{make}, #{year})
</insert>
<delete id="delete" parameterType="int">
delete from automobiles where
id = #{id}
</delete>
<delete id="deleteAll">
delete from automobiles
</delete>
</mapper>
|
The XML mapping file contains <select>,
<insert>, and <delete> elements with code inside of them that looks
like regular ANSI SQL. The XML element names correspond to the type of SQL
statements you would expect them to — the <insert> elements correspond to SQL INSERT
statements and so on. The parameters are defined in the SQL code by the
notation #{parameter}, where
parameter is the name of the field in your Java class. For
instance, the Automobile object has a field
called make, so you can pass the value stored
in this field to the SQL statement using #{make}.
A new feature of iBATIS 3 is the ability to perform this same configuration using annotations on a Java interface. I explain how you can use Java 5 annotations instead of XML configuration files later (see Java 5 features).
Finally, Listing 5
demonstrates the XML configuration file for iBATIS 3 that specifies the
name of the database, the type of driver to use, and other database
properties, such as credentials. The names of the mapping files, such
as the one shown in Listing 4, are listed in
the <mappers> element of the
configuration file.
Listing 5. The XML configuration file (ibatis-config.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//ibatis.apache.org//DTD Config 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias type="com.ibm.developerWorks.examples.ibatis.model.Automobile"
alias="Automobile" />
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver"
value="org.apache.derby.jdbc.EmbeddedDriver" />
<property name="url" value="jdbc:derby:/tmp/MyDB" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="automobile-mapper.xml" />
</mappers>
</configuration>
|
If you use the Java annotations method of defining the mapping between the objects and the database, you don't need this configuration file because iBATIS 3 provides a Java application program interface (API) that allows you to perform this configuration programmatically.
An advantage of using iBATIS is that the XML configuration
enables it to be a good ORM framework for mapping objects to existing
relational databases. With the Mapper classes
and mapping files, emphasis is put on mapping objects to existing data
structures, rather than conforming a data structure to the object
structure. While the cost in time of configuration is more than that of
using a framework that isolates developers more from the data structure,
designing the database and object models independently does have its
advantages. Good relational database practitioners and object model
designers can have competing goals that make their respective
implementations quite different from each other.
In the past, I've used iBATIS extensively in projects where the database uses a relational structure and stored procedures, and where developers had far less control over the design of the database.
Creating the sample Java project
To test the
examples in this article, you need to create an empty Java project. In the
new Java project, create a class that includes a main() method as shown in Listing 6.
Listing 6. The
Main class
package com.ibm.developerWorks.examples.ibatis;
import java.io.IOException;
import javax.sql.DataSource;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.derby.jdbc.EmbeddedDataSource;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import com.ibm.developerWorks.examples.ibatis.data.AutomobileMapper;
import com.ibm.developerWorks.examples.ibatis.model.Automobile;
public class Main {
private static final String CREATE = "create";
private static final String DELETE = "delete";
private static final String IBATIS_CONFIG = "ibatis-config.xml";
private static final String SHOW = "show";
@SuppressWarnings("static-access")
private static Options createOptions() {
Options options = new Options();
options.addOption(CREATE, false, "creates the objects in the database");
options.addOption(OptionBuilder.withArgName(SHOW).hasArg().withDescription(
"shows the specified automobile").withLongOpt(SHOW).create());
options.addOption(DELETE, false, "deletes all of the objects in database");
return options;
}
private static SqlSessionFactory createSqlMapper() throws IOException {
Reader reader = Resources.getResourceAsReader(IBATIS_CONFIG);
return new SqlSessionFactoryBuilder().build(reader);
}
public static void main(final String[] args) {
Options options = createOptions();
try {
CommandLine cmd = new GnuParser().parse(options, args);
SqlSession session = createSqlMapper().openSession();
try {
if (cmd.hasOption(CREATE)) {
System.out.println("Creating the objects in the database...");
// Create the automobiles
session.insert(Automobile.class.getName() + ".insert", new
Automobile(1, "Toyota", "Tercel", 1993));
session.insert(Automobile.class.getName() + ".insert", new
Automobile(2, "Honda", "CR-V", 2000));
session.insert(Automobile.class.getName() + ".insert", new
Automobile(3, "Chevrolet", "Impala", 1964));
session.insert(Automobile.class.getName() + ".insert", new
Automobile(4, "Dodge", "Pickup", 1946));
session.commit();
} else if (cmd.hasOption(SHOW)) {
Automobile auto = (Automobile) session.selectOne(
Automobile.class.getName() + ".select", cmd.getOptionValue(SHOW));
if (auto == null) {
System.out.println("No matching results found!");
} else {
System.out.println(auto);
}
} else if (cmd.hasOption(DELETE)) {
session.delete(Automobile.class.getName() + ".deleteAll");
session.commit();
} else {
System.out.println("Doing nothing.");
}
} finally {
session.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
|
To avoid lines of arbitrary argument parsing code, this main method uses the Apache Commons CLI project
to parse the arguments sent to it from the command line. Using the Apache
Commons CLI project enables the example to respond to different command-line arguments, such as --create, --delete, or --show.
For more information about the Apache Commons CLI project, see Resources. This argument support allows the
main() method to do a few sample operations
with the objects and iBATIS.
This example uses the iBATIS Resources class to load the configuration from an
XML file. The Resources class loads the file as
a Reader and passes it to the SqlSessionFactoryBuilder. The SqlSessionFactoryBuilder is able to construct a SqlSessionFactory, which, in turn, is used to
create the SqlSession objects that allow your
code to interact with the database through the methods defined in the
mapping classes.
Before you can compile and run the example code, you need to import the iBATIS 3, Apache Commons CLI, and Apache Derby JAR files (ibatis-3-core-x.jar, commons-cli-1.2.jar, and derby.jar).
You can run this example by running the main class from Eclipse. If you want to add arguments to the Java call, open Run > Run Configurations and find the Java Application/Main run configuration. In the Arguments tab, specify the arguments you want to supply in Program Arguments (Figure 1 demonstrates).
Figure 1. Adding arguments to the run configuration
Alternatively, call the Java application from the command line, remembering to set the classpath to include the iBATIS 3, Apache Commons CLI, and Apache Derby JAR files. See Listing 7 for an example of how to call the application.
Listing 7. Running the application from the command line
$ java -classpath {jars} com.ibm.developerWorks.examples.ibatis.Main --create
Creating the objects in the database...
|
When you execute the Java application, you see that --create creates four new Automobile objects and adds them to the database in the
automobiles table. Using the --delete argument
deletes the objects from the database. Using --show with an ID runs the SQL script to get the matching
database record, creates an Automobile object
that has the data, and displays the results to the console.
After you get the XML configuration examples working, you're ready to see one of the key new features in iBATIS 3: Java annotation support.
iBATIS
3 offers new changes that allow you to take advantage of Java 5
annotations. Using annotations, you can create mapper interfaces that
allow you to do all of the mapping from the objects to the database in
Java code. Consider the code shown in Listing 8
that shows the AutomobileMapper interface used
for configuration, instead of the XML configuration.
Listing 8. The
AutomobileMapper interface
package com.ibm.developerWorks.examples.ibatis.data;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import com.ibm.developerWorks.examples.ibatis.model.Automobile;
public interface AutomobileMapper {
@Select("select id, make, model, model_year as \"year\" from automobiles
where id = #{id}")
Automobile selectAutomobile(final int id);
@Insert("insert into automobiles(id,make,model,model_year)
values (#{id}, #{make}, #{model}, #{year})")
void insertAutomobile(final Automobile arg);
@Delete("delete from automobiles")
void deleteAll();
}
|
In the XML configuration for the Automobile
mapper, the resultMap element is used to map
the model_year database column with the year field on the Automobile object. This is a fairly easy mapping that you can
do in the annotations simply by using the SQL feature of aliasing the
column, which is done in the SQL defined in the @Select annotation.
The Select annotation in the AutomobileMapper interface maps the selectAutomobile method to the SQL that can be
used to select a record from the automobiles table with the given value.
The value is specified as the id parameter to
the argument and is included in the SQL statement as #{id}, just as it is in the XML configuration. A strong
advantage of using Java interfaces for mapping the SQL methods is the
feedbac available to you in the Java editor in the form of
compile-time errors. You can be sure that the methods are expecting and
returning the correct types, whereas XML configuration usually requires
that you execute the code before finding errors.
Additionally, iBATIS 3 now supports interface inheritance, which allows you to refine your Java interfaces to reduce code duplication.
The documentation for iBATIS cautions that annotations can be much simpler and easier to read for smaller, simpler projects. However, annotations are limited compared to the XML configuration. If your project includes complex objects or a complex database structure, consider using the XML configuration instead of the Java annotations.
The annotation-based
configuration for iBATIS requires slightly different instantiation.
Instead of using a Reader class that reads an
XML configuration, you can add the mappers to the Configuration object, as shown in Listing
9.
Listing 9. The new Java code using annotation configuration
package com.ibm.developerWorks.examples.ibatis;
// snipped imports
public class Main {
// snipped constants declarations--didn't change
// new method for creating data source private static DataSource createDataSource() { EmbeddedDataSource dataSource = new org.apache.derby.jdbc.EmbeddedDataSource(); dataSource.setDatabaseName("/tmp/MyDB"); return dataSource; }
@SuppressWarnings("static-access")
private static Options createOptions() {
// snipped... no changes
}
private static SqlSessionFactory createSqlMapper() throws IOException {DataSource
datasource = createDataSource(); TransactionFactory transaction = new JdbcTransactionFactory(); Configuration configuration = new Configuration(new Environment("development", transaction, datasource)); configuration.addMapper(AutomobileMapper.class);
return new SqlSessionFactoryBuilder().build(configuration);
}
public static void main(final String[] args) {
Options options = createOptions();
try {
CommandLine cmd = new GnuParser().parse(options, args);
SqlSession session = createSqlMapper().openSession(); AutomobileMapper mapper = session.getMapper(AutomobileMapper.class);
try {
if (cmd.hasOption(CREATE)) {
System.out.println("Creating the objects in the database...");
// Create the automobiles mapper.insertAutomobile(new Automobile(1, "Toyota", "Tercel", 1993)); mapper.insertAutomobile(new Automobile(2, "Honda", "CR-V", 2000)); mapper.insertAutomobile( new Automobile(3, "Chevrolet", "Impala", 1964)); mapper.insertAutomobile(new Automobile(4, "Dodge", "Pickup", 1946));
session.commit();
} else if (cmd.hasOption(SHOW)) {
Automobile auto = mapper.selectAutomobile( Integer.parseInt(cmd.getOptionValue(SHOW)));
if (auto == null) {
System.out.println("No matching results found!");
} else {
System.out.println(auto);
}
} else if (cmd.hasOption(DELETE)) {
mapper.deleteAll();
session.commit();
} else {
System.out.println("Doing nothing.");
}
} finally {
session.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
|
The AutomobileMapper supplied to the addMapper() method is the same Java interface
shown back in Listing 8.
The Configuration object also requires a DataSource implementation. In this example, the
DataSource implementation is created easily
enough in the static createDataSource() method.
However, in a production application, you should modify this to make the
data source information — such as the name of the
database — more dynamic. If you're building an
application inside an application server or Web server that manages
persistence, you can use Java Naming and Directory Interface (JNDI) to get
the DataSource.
If you decide to use XML configuration instead of Java annotations for iBATIS while upgrading from a previous version of iBATIS, you will notice that there are some key differences in the XML between the old and new versions.
Previous versions of iBATIS use a parameterMap, which is similar to a resultMap, to map the parameters of the methods. However, the
parameterMap element has been deprecated
and should no longer be used. Instead, include the type of the object in
the parameterType and access the properties on
the type using the standard notation (for example, #{id} for the id field on a Java
object).
The configuration and mapper files' root elements are updated to include new Document Type Definition (DTD) declarations, and some of the elements have been moved to provide better organization.
For more information about the iBATIS XML configuration changes, see Resources. Be aware that, as of the beta 9 version of iBATIS, the documentation for porting older versions of XML to newer versions is still in progress.
The iBATIS Schema Migrations System (iBATIS Migrations) project is not a project for migrating older schemas of XML configuration to newer ones. Instead, it is a project aimed at making it easier to migrate your database changes while in development from one version of the database to another as the database evolves. You can use the tool to generate SQL scripts that allow you to apply changes automatically, which can greatly reduce errors.
For more information about the iBATIS Migrations project, see Resources.
iBATIS 3 is an ORM persistence framework that allows you to map the properties in Java objects to table columns in a database. The mapping-centric focus of iBATIS emphasizes mapping a solid object model with a solid relational database design.
New in iBATIS 3 is the use of Java annotations for mapping, which can introduce cleaner, more straight-forward mapping that is accessible in the Java source code for many projects. iBATIS 3 still offers the ability to map objects using XML configuration files. The dual configuration methods allow you to configure iBATIS using the easiest method for your project.
Learn
-
Read more about the iBATIS 3 XML configuration changes.
-
Check out the User Guide for the iBATIS Schema Migrations.
-
See tutorials about using iBATIS with Apache
Derby, Part 1, Part 2 and Part 3 (developerWorks, 2006).
- To learn more about using Apache Tomcat with
Eclipse to build Web applications, read "Developing Web applications with Tomcat and Eclipse"
(developerWorks, May 2007).
- Read about the Apache
License.
-
To listen to interesting interviews and discussions for software developers, check out developerWorks podcasts.
-
Stay current with developerWorks' Technical events and webcasts.
-
Follow developerWorks on Twitter.
-
Check out upcoming conferences, trade shows, webcasts, and other Events around the world that are of interest to IBM open source developers.
-
Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products, as well as our most popular articles and tutorials.
-
The My developerWorks community is an example of a successful general community that covers a wide variety of topics.
-
Watch and learn about IBM and open source technologies and product functions with the no-cost developerWorks On demand demos.
Get products and technologies
- Download iBATIS for Java.
-
Learn more about the Apache
Commons CLI project.
-
Innovate your next open source development project with IBM trial software, available for download or on DVD.
- Download
IBM product evaluation versions
or explore
the online trials in the IBM SOA Sandbox and get your hands on application development tools and middleware products from
DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
Discuss
-
Participate in developerWorks blogs and get involved in the developerWorks community.

Nathan A. Good lives in the Twin Cities area of Minnesota. Professionally, he does software development, software architecture, and systems administration. When he's not writing software, he enjoys building PCs and servers, reading about and working with new technologies, and trying to get his friends to make the move to open source software. He's written and co-written many books and articles, including Professional Red Hat Enterprise Linux 3, Regular Expression Recipes: A Problem-Solution Approach and Foundations of PEAR: Rapid PHP Development.



