The Data Access Object pattern in the Core J2EE Pattern Catalog suggests that while creating a robust J2EE application, you should abstract and encapsulate all access to a data source behind a common API. In terms of programming, what this means is to create an interface defining all of the business methods that your application needs. Inside your application, you use this interface when you want to interact with your data store, and then create a separate class that implements this interface and has the logic for interacting with that particular data store.
For example, consider the sample application developed in the
article iBatis: SQL Maps. This is a Struts application that allows you to
fire SELECT, INSERT, UPDATE and DELETE SQL queries on a
CONTACT table. In this application, we are using SQL
Maps as the persistence framework. Now say we want to change this
application so that the CONTACT table is stored in an
XML file instead of a RDBMS, or want
to use Hibernate for SELECT queries and SQL Map for
the other three queries, perhaps because Hibernate provides better
support for caching. This will be very hard to implement, and if we
are able to change it at all, it won't be a very clean
solution.
A better approach for this type of problem would be to create a
ContactDAO interface and define business methods for
SELECT, INSERT, UPDATE, and
DELETE queries in this interface. Then you'd create
separate classes containing persistence logic for each method. So
there would be a class that knows how to interact with the
CONTACT table using SQL Maps, another class that
knows how to interact with the CONTACT table if it is
stored in a XML file instead of a RDBMS, and so on.
Inside your application, you'd choose from different
implementations of ContactDAO, depending on your requirements. This
relationship is shown in Figure 1.

Figure 1. ContactDAO interface and implementations
|
Related Reading
J2EE Design Patterns |
iBatis Data Access
Object (DAO) is an open source framework now hosted by Apache
and targeted at solving these type of problems. It provides you
with an infrastructure for creating applications based on the DAO
pattern. What this means is that you can create a XML file and
declare that XMLContactDAO.java is the implementation class
for ContactDAO, which knows how to read and write
contacts in an XML file instead of a RDBMS.
SQLMapContactDAO is an implementation class that
knows how to interact with the CONTACT table using SQL
Maps as its persistence framework. Inside your application, you ask
the DAO framework for an implementation of ContactDAO for
XML, and it will provide you with an XMLContactDAO
object. Also, the DAO framework provides you with a uniform
interface to handle transaction management, irrespective of what
persistence mechanism you use. It will also take care of the
low-level details of connection management and initializing
persistence frameworks.
This article is a step-by-step guide on how to use the iBatis DAO framework in your application. We will start by looking at how to change our sample application developed in the SQL Maps article to use the DAO framework. Then we will talk about the architecture of the DAO framework. Next we will look at which transaction managers are supported in DAO framework, and will finish with a section on how to create your own transaction manager.
We will start by changing our sample application developed in the SQL Maps article to use the DAO framework.
Copy the ibatis-dao-2.jar file to your WEB-INF/lib folder.
Create DAOMap.xml at the root of your JavaSource folder, like this:
<daoConfig>
<context id="sqlmap">
<transactionManager type="SQLMAP">
<property name="SqlMapConfigResource" value=
"com/sample/contact/dao/sqlmap/SqlMapConfig.xml"/>
</transactionManager>
<dao interface="com.sample.contact.dao.ContactDAO"
implementation=
"com.sample.contact.dao.sqlmap.SQLMapContactDAO"/>
</context>
</daoConfig>
DAOMap.xml file is a deployment descriptor for your
iBatis DAO framework. <daoConfig> is the root
element. Each <context> element represents one
persistence mechanism. In our example, we will use only SQL Maps for
persistence, so we so have only one <context>
element. Every persistence mechanism should have one
<transactionManager> element, which represents
the manager used for getting a connection to the underlying data
store and marking the transaction boundary. We will talk more about
transactionManager later.
The <context> element also contains a list of
DAOs for the given particular persistence mechanism. In our
example, we want to create a ContactDAO, which will use
SQL Maps for persistence, so we will add one
<dao> element defining
SQLMapContactDAO.
|
Create ContactDAO.java, like this:
public interface ContactDAO extends DAO {
public int insertContact(Contact contact);
public int updateContact(Contact contact);
public Contact selectContact(int contactId);
public int deleteContact(int contactId);
}
ContactDAO.java defines all the business methods required
by a client for interacting with the CONTACT table.
Please note that all the methods in ContactDAO.java take a
Contact object as a parameter, which is a data
transfer object for carrying data.
Create a SQLMapContactDAO.java file, like this:
public class SQLMapContactDAO extends
SqlMapDaoTemplate implements ContactDAO {
public SQLMapContactDAO(DaoManager arg0) {
super(arg0);
}
public int deleteContact(int contactId) {
return super.delete("deleteContact",
new Integer(contactId));
}
public int insertContact(Contact contact) {
Integer contactId =(Integer)super.insert
("insertContact",contact);
return contact.getContactId();
}
public Contact selectContact(int contactId) {
return (Contact)super.queryForObject("getContact",
new Integer(contactId));
}
public int updateContact(Contact contact) {
return super.update("updateContact",contact);
}
}
SQLMapContactDAO is a concrete implementation of the
ContactDAO interface, using SQL Maps as its
persistence mechanism. Note that we are not writing any code for
initializing SQL Maps, for getting a connection, or for marking a
transaction boundary in our class. Instead, we extend our class from
SqlMapDaoTemplate.java, which will take care of all
of the underlying repetitive operations for us. Business logic is only
thing that we need to worry about in our
SQLMapContactDAO class.Change the execute() method of
ContactSelectAction.java, like this:
Contact contactForm = (Contact) form;
Reader reader=
Resources.getResourceAsReader("DAOMap.xml");
DaoManager daoManager =
DaoManagerBuilder.buildDaoManager(reader);
ContactDAO contactDAO =
(ContactDAO) daoManager.getDao(
ContactDAO.class,"sqlmap");
request.setAttribute("contactDetail",
contactDAO.selectContact(
contactForm.getContactId()));
The last step is changing the execute() method
in our ContactSelectAction class to use the DAO framework.
In order to initialize the DAO framework, we need a
Reader object for DAOMap.xml. The iBatis framework
provides you with the Resources.getResourceAsReader()
utility method that will allow you to read a resource as a
Reader. Once you have a Reader object
representing the DAOMap.xml file, you can pass it to
DAOManagerBuilder.buildDaoManager(). This will return
an instance of DaoManager, which should be used for
interacting with the DAO framework in the future. Ideally, you should
initialize your DAO framework at application startup. In our
application, we can do that by putting this code in a Struts plugin, but we are initializing it in the execute method just
to keep our example simple.
Once you have an instance of DaoManager, you can
call the getDao() method with name of the interface
and persistence implementation (the value of the id
attribute in the <context> element) that you
want to use. In our example, we want an instance of
SQLMapContactDAO, so we will pass
ContactDAO as the name of the interface and
"sqlmap" as the persistence mechanism. Once you have
an instance of SQLMapContactDAO, you can start calling
business methods on it.
You can try this example by downloading the sample code from the Resources section on the last page of this article.
|
Since we now have a working application, thanks to the DAO framework, let's take a peek at what is going on under the hood. First take a look Figure 2, a sequence diagram of how DAO works.

Figure 2. DAO sequence diagram (click for full-size version)
To initialize the DAO framework, you start by calling
DaoManagerBuilder.buildDaoManager() and passing the
DAOMap.xml file to it. In this method, the DAO framework
will read DAOMap.xml and create a corresponding
DAOManager object from it. This object will contain
data representing the supported persistence mechanisms. Which
interfaces are implemented, and what's the implementation class for
a particular combination of interface and persistence mechanism?
Basically, this is the Java object equivalent of the
DAOMap.xml file.
Once you have a DAOManager object, you can query it
to get the SQL Map implementation of the ContactDAO
interface. The DAO framework will return a DaoProxy
object that wraps the implementation class. In our example, it will
return a DaoProxy object for the
SQLMapContactDAO class. The DaoProxy
object allows the DAO framework to intercept calls to business methods.
In our example, when you call
contactDAO.selectContact(), the DAO framework will
intercept the call and check whether the transaction is already
started. If not, it will start a new transaction call by calling
the startTransaction() method on the transaction manager.
Once the transaction is started, it will call the
selectContact() method of
SQLMapContactDAO within that transaction. The
DaoProxy object will intercept the call to
selectContact() method on its way back and use it for
committing the transaction.
By the way, if you don't want your transaction to be scoped on
the method level, or if you want to call multiple methods in one
transaction, you can call
daoManager.startTransaction() before calling a
business method on ContactDAO, and commit that
transaction when done by calling
daoManager.commitTransaction().
Now the only remaining issue is who takes care of initializing
the persistence mechanism and passing control to it. In our
example, that means deciding who passes the path of
SqlMapConfig.xml to the SQL Map framework and initializes
it. This also means deciding who takes care of the actual
interaction with the SQL Maps framework. The DAO framework provides us
with Template classes for every persistence mechanism. In your
application, you should extend your implementation class from this
Template class, and write only business logic in your method; after
that, pass control to this template class, which will take care of
interacting with persistence mechanism. Our example calls
super.queryForObject("getContact",new
Integer(contactId));, which means
SqlMapDaoTemplate will take care of initializing and
interacting with the SQL Maps framework.
Your persistence mechanism may need some information for initialization. In our example, it requires the path to SqlMapConfig.xml, which contains information like the name of the driver class, the JDBC URL, the login info, etc. Such information required by a particular transaction manager is passed as a property element to it in the DaoMap.xml file. In the next section, we will talk about which transaction managers are supported by the DAO framework and the initialization information required by each.
The DAO framework provides built-in support for a few
persistence mechanisms. To use one of the built-in
transactionManagers, you have to do two things:
<transactionManager> element and passing
required information to it as properties.Template class for that
transactionManager while creating your DAO
implementation class.Now we will go through the built-in
transactionManagers and find out how to use each of
them in your application.
The JDBC transaction manager is good if you don't want to use any framework for persistence and want to write your own JDBC code. If you're using JDBC as your persistence mechanism, you can use one of the three connection management options:
SIMPLE: Use SIMPLE as the value of
the DataSource element if you want to use iBatis' own
implementation of connection pooling. Pass the usual JDBC
properties (DriverManager class, JDBC URL, etc.) to it
as Properties. Look at the online iBatis
documentation to learn about advanced connection properties.
<transactionManager type="JDBC">
<property name="DataSource" value="SIMPLE"/>
<property name="JDBC.Driver"
value="com.ibm.db2j.jdbc.DB2jDriver"/>
<property name="JDBC.ConnectionURL"
value="jdbc:db2j:D:\cloudscape\wpsdb"/>
<property name="JDBC.Username"
value="db2admin"/>
<property name="JDBC.Password"
value="db2admin"/>
<property name="JDBC.DefaultAutoCommit"
value="true" />
</transactionManager>
|
DBCP: Use DBCP when you want to use Apache DBCP for
connection management. Please see the DAO online guide for
information on how to configure the DBCP connection pool.JNDI: When you want to use the application
server's implementation of the connection pool, all you have to do is
provide the JNDI name of your connection pool and the DAO framework
will use it for getting a connection.
<transactionManager type="JDBC">
<property name="DataSource" value="JNDI"/>
<property name="DBJndiContext"
value="java:comp/env/jdbc/MyDataSource"/>
</transactionManager>
Then you will have to create a class that extends
JdbcDaoTemplate.java to implement your business
interface. In our sample, we created JDBCContactDAO.java. In
the business method, you can ask your superclass for the connection
by calling getConnection(). Since we are not using any
persistence framework, we'll have to create our own
SQL queries and execute them.
public int updateContact(Contact contact) {
try {
Connection conn = getConnection();
PreparedStatement updateStmt =
conn.prepareStatement("UPDATE DB2ADMIN.CONTACT
SET FIRSTNAME=?,LASTNAME=? WHERE CONTACTID=?");
updateStmt.setString(1, contact.getFirstName());
updateStmt.setString(2, contact.getLastName());
updateStmt.setInt(3, contact.getContactId());
return updateStmt.executeUpdate();
} catch (SQLException ex) {
throw new DaoException(ex);
}
}
When using a JDBC transactionManager, the DAO framework will
control the transaction by calling commit and
rollback methods on the Connection
object, so transactions will be handled at the
Connection level instead of participating in global
transaction.
If you are creating a J2EE application, then it is a much better
idea to use the connection pool provided by your application
server, because it will perform much better than a SIMPLE or DBCP
connection pool. Also, with a J2EE application, RDBMS will be only
one of the transactional sources; in addition to RDBMS, you will
also have other things like JCA, MQ Server, etc. This means you
cannot start and commit transactions at the connection level;
instead, your code should participate in global transaction by
calling the begin() and commit() methods
on a UserTransaction. For this type of requirement, you
can use JTA as transctionManager and provide the JNDI URL of both
your DataSource pool and UserTransaction
object to it.
<transactionManager type="JTA">
<property name="DBJndiContext"
value="java:comp/env/jdbc/MyDataSource"/>
<property name="UserTransaction"
value="java:comp/env/UserTransaction"/>
</transactionManager>
Since Hibernate is a very popular persistence framework, iBatis
DAO offers support for it. To use Hibernate in your application,
add a <transactionManager> element in your
DAOMap.xml file, as follows.
<transactionManager type="HIBERNATE">
<property name="hibernate.dialect"
value="net.sf.hibernate.dialect.Cloudscape"/>
<property name="hibernate.connection.driver_class"
value="com.ibm.db2j.jdbc.DB2jDriver"/>
<property name="hibernate.connection.url"
value="jdbc:db2j:D:\cloudscape\wpsdb"/>
<property name="hibernate.connection.username"
value="db2admin/>
<property name="hibernate.connection.password"
value="db2admin"/>
<property name="class.1"
value="com.sample.contact.Contact"/>
</transactionManager>
You also need to create a DAO class extending
HibernateDaoTemplate. Inside your DAO, you can access
the Hibernate Session object by calling
the getSession() method.
Please look at the sample application (in the Resources section) for details about how to use the SQL Map persistence framework in your application.
<transactionManager type="EXTERNAL">
</transactionManager>
|
There may be situations when you want to read and write data from an XML file instead of an RDBMS. For example, imagine working on a banking project where you don't have direct access to bank's database, and instead the customer provides you with sample data in the form of XML files. Your requirement is that your application should use these XML files during development, and a RDBMS in production. We will take our contact application as example and change it so that it will read and write data from an XML file instead of an RDBMS. This requires two changes:
transactionManager in
your DAOMap.xml file.
public class XMLContactDAO implements ContactDAO {
public static final String
CONTACTXMLNAME = "c:\\Contact.xml";
public XMLContactDAO(DaoManager manager) {
super(manager);
}
public int insertContact(Contact contact) {
HashMap contactMap = loadChanges();
if (contactMap.get(new Integer
(contact.getContactId())) == null)
contactMap.put(new
Integer(contact.getContactId()), contact);
saveChanges(contactMap);
return contact.getContactId();
}
public Contact selectContact(int contactId) {
HashMap contactMap = loadChanges();
return (Contact) contactMap.get(
new Integer(contactId));
}
public HashMap loadChanges() {
HashMap contactMap = null;
try {
XStream xstream = new XStream(new DomDriver());
xstream.alias("contact", Contact.class);
contactMap =
(HashMap) xstream.fromXML(
new FileReader(CONTACTXMLNAME),HashMap.class);
} catch (FileNotFoundException e) {
e.printStackTrace();
return new HashMap();
}
return contactMap;
}
public void saveChanges(HashMap contactMap) {
try {
XStream xstream = new XStream();
xstream.alias("contact", Contact.class);
xstream.toXML(contactMap,
new FileWriter(CONTACTXMLNAME));
} catch (IOException e) {
e.printStackTrace();
}
}
}
In this example, XMLContactDAO implements the
ContactDAO business interface. Since we are using an
EXTERNAL transaction manager, we cannot use any of the
existing Template classes. In our class, we have created two simple
methods--loadChanges() and saveChanges--for reading and writing an XML file using the XStream framework. XStream is
open source framework that helps you read an XML file as a
Java object and save a Java object as an XML file.
Nowadays, a lot of new persistence frameworks are coming up. As a developer, this is both good and bad for you. It's good because now you have many more options to choose from. But it's bad because you have to make choices. And the bigger problem is that you have to commit to one persistence framework at the start of the project, at which point you may not be completely clear on project requirements or completely sure if a particular framework can fulfill all your requirements. DAO is a very easy-to-use and useful framework that guards against changes in persistence mechanisms. It may look like you're making some investment up front, but it will definitely help you in the long run.
Sunil Patil has worked on J2EE technologies for more than five years. His areas of interest include object relational mapping tools, UI frameworks, and portals.
Return to ONJava.com.
Copyright © 2009 O'Reilly Media, Inc.