22
Improve persistence with Apache Derby and iBATIS, Part 3: Transactions, caching, and dynamic SQL Skill Level: Intermediate Daniel Wintschel ([email protected]) Freelance writer 07 Mar 2006 This tutorial series has been demonstrating how you can improve persistence in your database-driven Java™ applications by combining Apache Derby's power as a small-footprint embeddable database with the iBATIS object-relational (OR) mapping framework. In Part 3, the final in the series, learn how iBATIS handles three advanced features of database-driven applications: transactions, caching, and dynamic SQL. Plus, find out how the Data Access Objects (DAO) framework can operate on its own without the Data Mapper framework. Section 1. Before you start About this series This tutorial is the third in a three-part series that introduces you to the iBATIS Data Mapper and DAO frameworks and shows you how to use iBATIS with Apache Derby. Part 1 discusses the concepts underlying iBATIS, including descriptions of the Data Mapper and DAO frameworks, and outlines the semantics and terminology that iBATIS uses. You look at some basic examples of how to configure Apache Derby with iBATIS and perform simple OR mapping using Data Mapper. Part 2 covers the DAO and the Data Mapper frameworks in more depth, using the Transactions, caching, and dynamic SQL © Copyright IBM Corporation 1994, 2008. All rights reserved. Page 1 of 22

Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

  • Upload
    others

  • View
    5

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

Improve persistence with Apache Derby andiBATIS, Part 3: Transactions, caching, anddynamic SQLSkill Level: Intermediate

Daniel Wintschel ([email protected])Freelance writer

07 Mar 2006

This tutorial series has been demonstrating how you can improve persistence in yourdatabase-driven Java™ applications by combining Apache Derby's power as asmall-footprint embeddable database with the iBATIS object-relational (OR) mappingframework. In Part 3, the final in the series, learn how iBATIS handles threeadvanced features of database-driven applications: transactions, caching, anddynamic SQL. Plus, find out how the Data Access Objects (DAO) framework canoperate on its own without the Data Mapper framework.

Section 1. Before you start

About this series

This tutorial is the third in a three-part series that introduces you to the iBATIS DataMapper and DAO frameworks and shows you how to use iBATIS with ApacheDerby.

Part 1 discusses the concepts underlying iBATIS, including descriptions of the DataMapper and DAO frameworks, and outlines the semantics and terminology thatiBATIS uses. You look at some basic examples of how to configure Apache Derbywith iBATIS and perform simple OR mapping using Data Mapper.

Part 2 covers the DAO and the Data Mapper frameworks in more depth, using the

Transactions, caching, and dynamic SQL© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 1 of 22

Page 2: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

JPetStore example application to illustrate important iBATIS concepts. Using theDAO with the Data Mapper provides you with a clean and consistent way to accessyour application's underlying data structure.

About this tutorial

Part 3 continues the use of the two frameworks with JPetStore and coverstransaction handling, data caching, and creating dynamic SQL statements. It alsotakes a brief look at, and provides an example of, how to use the DAO frameworkwithout the Data Mapper framework to integrate it as a stand-alone framework withinan application.

At this point you should have a good idea of how the DAO and Data Mapperframeworks work within iBATIS and how iBATIS avoids the N+1 select problem. Inthis tutorial you look at how iBATIS can help you with three advanced features thatare important when you use any database with a Java application:

• Transaction handling: Transactions are essential for data integrity andare available to both the DAO and Data Mapper frameworks. In thistutorial's example, the DAO framework delegates transactionmanagement to the underlying Data Mapper framework.

• Data caching: Data caching enables large database applications to scaleunder heavy load. This feature pertains to the iBATIS Data Mapperframework only, not to the DOA. (The DAO provides access to underlyingdata and isn't responsible for where the data comes from or how it works.)

• Creating dynamic SQL statements: Dynamic SQL lets you generateSQL statements based on certain criteria without needing to writecomplex and extensive Java code. Like data caching, this feature pertainsonly to the Data Mapper framework.

You use Apache Ant to execute most of the examples from the command line, whichprovides a clear view of what's happening in the database. You also continue to usethe modified version of the JPetStore application from Part 2 to build up anyadditional examples that are needed.

Finally, you learn how to use the DAO framework separately from the Data Mapperframework. The ability to use the two frameworks together or individually illustratesthe power and flexibility of iBATIS.

Prerequisites

This tutorial assumes you're comfortable working with basic SQL statements, that

developerWorks® ibm.com/developerWorks

Transactions, caching, and dynamic SQLPage 2 of 22 © Copyright IBM Corporation 1994, 2008. All rights reserved.

Page 3: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

you understand basic XML semantics, and that you can read Java code. You needto know how to run Apache Ant to follow the tutorial's examples and create thedatabase.

System requirements

To run the example code in this tutorial, perform the following steps:

1. Download and install the following applications:

• Apache Derby, Version 10.1.1.0

• Apache Ant, Version 1.6.5

• Java 2 Platform, Standard Edition (J2SE) 1.4.2_09You don't need to download iBATIS separately, because the necessary.jar files are included with the source code for this tutorial, which youdownload in step 3.

2. Make sure that the environment variables outlined in Table 1 are definedin your shell.Table 1. Setting the environmental variables

Variable name Required setting

DERBY_HOME Set to the root folder of your Derbyinstallation.

ANT_HOME Set to the root folder of your Antinstallation.

JAVA_HOME Set to the root folder of your Javainstallation.

PATH Ensure that ANT_HOME/bin is inyour path.

3. Extract the supplied .zip file (see the Download section) to your preferredlocation. (This is the project root.) The project is laid out in Listing 1:Listing 1. Project layout

/iBATIS_JPetStore-4.0.5//build/ Ant build.xml and build output/ddl/ DDL to create our database and data load SQL/devlib/ JARs needed for app compilation/doc/ licensing and stuff/lib/ JARs needed for application execution/src/ Java source code and iBATIS config files/web/ JSP, Struts, and webapp stuff

ibm.com/developerWorks developerWorks®

Transactions, caching, and dynamic SQL© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 3 of 22

Page 4: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

4. Modify the /src/properties/database.properties file to specify where youwould like Derby to create your database. I've set the url property in thedatabase.properties file to be jdbc:derby:c:/temp/ibatis. See thedatabase.properties file, shown in Listing 2, and change the path toinclude something appropriate for your file system.Listing 2. The database.properties file

##################################### Database Connectivity Properties####################################

driver=org.apache.derby.jdbc.EmbeddedDriverurl=jdbc:derby:c:/temp/ibatisusername=password=

Make sure that the value you specify is an existing empty directory. You can leavethe username and password blank, as they are. If you're on a UNIX® or Linux®system, your path looks something like /tmp/ibatis, but you can create this directorywherever you want.

Section 2. Transactions using Derby and iBATIS

This section introduces transactions in iBATIS (both in the Data Mapper and DAOframeworks) and, using code examples, shows how to use transactions when youwrite an application that uses iBATIS. Transactions are critical to any nontrivialdatabase-driven application, because they ensure the consistency and integrity ofdata within your database.

Why transactions are important

As you continue to develop the JPetStore application toward commercialdeployment, the demands on the application will grow. You must now considerissues like large numbers of data requests and transaction failures that are due toerrors. Transactions are a necessary part of any Java 2 Platform, Enterprise Edition(J2EE) application, and you use them often when you access databases.Transactions are critical to maintaining data integrity in case of unforeseen failures,given that a transaction is an atomic set of actions that can be revoked if an erroroccurs. If any of the actions fail, none of the actions is taken.

developerWorks® ibm.com/developerWorks

Transactions, caching, and dynamic SQLPage 4 of 22 © Copyright IBM Corporation 1994, 2008. All rights reserved.

Page 5: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

Transactions in the Data Mapper Framework

Within the iBATIS Data Mapper framework, the sql-map-config.xml file that you useto configure the Data Mapper contains a <transactionManager/> XML element.Built into the Data Mapper are three types of transaction managers: JDBC, JTA, andEXTERNAL. The JTA transaction manager provides transactions through the use ofthe Java Transaction API (JTA). To use JTA transactions, you must tell the <transactionManager /> element where it can find a UserTransactionimplementation in the Java Naming and Directory Interface (JNDI). You use theEXTERNAL transaction manager if you want to manage transactions on your own --for example, if you have a nontransactional data source, such as flat files. Thistutorial covers only the JDBC transaction manager.

The configuration of the Data Mapper < transactionManager /> element withinthe JPetStore application looks like Listing 3.

Listing 3. Configuration of the Data Mapper <transactionManager/> element

<transactionManager type='JDBC'><dataSource type='SIMPLE'><property value='${driver}' name='JDBC.Driver'/><property value='${url}' name='JDBC.ConnectionURL'/><property value='${username}' name='JDBC.Username'/><property value='${password}' name='JDBC.Password'/><property value='15' name='Pool.MaximumActiveConnections'/><property value='15' name='Pool.MaximumIdleConnections'/><property value='1000' name='Pool.MaximumWait'/></dataSource></transactionManager>

The JDBC transaction manager manages transactions by turning off auto commitand using the java.sql.Connection interface's commit() and rollback()methods. The only nested elements within the <transactionManager.../>element pertain to the configuration of the database connectivity properties and theconnection pool. This demonstrates how easy it is to set up the Data Mapper to dealwith JDBC-style transactions for you.

Now take a look at what a transaction might look like in the Java language, asshown in Listing 4. This example covers transaction basics before you examineJDBC transactions in the JPetStore application.

Listing 4. A transaction in the Java language

public void test(SqlMapClient dataMapper) throws SQLException {try {dataMapper.startTransaction(); // Here we go!Sequence sequence = new Sequence('ProductSeq', -1);sequence = (Sequence)dataMapper.queryForObject('getSequence', sequence);

ibm.com/developerWorks developerWorks®

Transactions, caching, and dynamic SQL© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 5 of 22

Page 6: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

int nextId = sequence.getNextId();sequence.setNextId(sequence.getNextId() + 1);dataMapper.update('updateSequence', sequence);Product product = new Product(nextId, 'iBATIS Sandwich','A Yummy Sandwich', 24);product = (Product)dataMapper.insert('insertProduct', product);dataMapper.commitTransaction(); //commit!} catch (SQLException e) {// do you want to handle the exception?} finally {dataMapper.endTransaction(); //Clean up!}

}

The first thing to notice in Listing 4 is this code:

dataMapper.startTransaction(); // Here we go!This call does exactly what you expect it to: It starts a transaction. Next, you queryfor the next ID you want to use from the Sequence table. Once you have the nextID, you update the next ID value in the Sequence table by incrementing it. Then youcreate a Product object, using the ID you got from the database as the product ID,and you attempt to insert that product into the database. The nexttransaction-related statement appears after the product insert:

dataMapper.commitTransaction(); //commit!This call commits the transaction. After that, you also want to look at thefinally{...} block:

dataMapper.endTransaction(); //Clean up!You must always make the call to endTransaction(). That's why you put it in afinally block. If the transaction was not committed previously,endTransaction() rolls back the transaction. No matter what, it's alsoresponsible for cleaning up the underlying resources, such as ResultSet andPreparedStatement, as well as closing your Connection object or returning it tothe pool.

So what does all this transaction-related code actually do? Suppose that prior tobeginning this transaction, the current value of the NEXT_ID column in theSequence table was 1000. The transaction in Listing 4 does something like this:

1. Selects the NEXT_ID from Sequence

2. Updates NEXT_ID, incrementing it by one

3. Inserts the iBATIS Sandwich product into the database, using theNEXT_ID value that you retrieved from the Sequence table beforeincrementing it

If any of the SQL statements represented by steps one through three (represented

developerWorks® ibm.com/developerWorks

Transactions, caching, and dynamic SQLPage 6 of 22 © Copyright IBM Corporation 1994, 2008. All rights reserved.

Page 7: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

by calls to the Data Mapper framework) fails, the database state won't change. Thevalue of NEXT_ID is still 1000, and there is no sandwich for you in the database.

If all of the statements succeed, and the call to commitTransaction() succeeds:

• The state of the database is updated.

• The new value of NEXT_ID in the Sequence table becomes 1001.

• An iBATIS sandwich is in the database, which is a good thing.

Now examine how JDBC transactions occur in JPetStore using the DAO and DataMapper frameworks.

Transactions in the JPetStore application

In a moment, you can walk through a transaction in JPetStore that uses the DAOand Data Mapper frameworks. But first take a look at transactions solely in the DAO.The configuration file for the DAO framework, which is typically called dao.xml, alsocontains a <transactionManager.../> element. Because the DAO frameworkis not specifically tied to the Data Mapper framework, you'll notice somethinginteresting about the types of transaction managers available to the DAO framework.They include:

• SQLMAP

• HIBERNATE

• JDBC

• JTA

• EXTERNAL

You might be wondering why HIBERNATE is in that list. This is an example of howyou can use the Data Mapper and DAO frameworks separately. The DAOframework includes a built-in transaction manager for Hibernate that managestransactions at the underlying level. It uses the Hibernate classes that typically dealwith transactions (primarily org.hibernate.SessionFactory,org.hibernate.Session, and org.hibernate.Transaction) when you useHibernate as your persistence framework.

In the JPetStore example, you're using the Data Mapper Framework for persistence,so your dao.xml takes advantage of the transaction manager for the SQLMAPframework. When you use the SQLMAP transaction manager (or the Hibernatetransaction manager for that matter), iBATIS provides you with DAO templates thatyou can subclass, making your life easier. For example, because you're using the

ibm.com/developerWorks developerWorks®

Transactions, caching, and dynamic SQL© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 7 of 22

Page 8: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

SQLMAP transaction manager, your DAO implementations subclasscom.ibatis.dao.client.template.SqlMapDaoTemplate (indirectly, by wayof BaseSqlMapDao). If you had chosen Hibernate as your persistence framework,you might have your DAO implementations subclasscom.ibatis.dao.client.template.HibernateDaoTemplate instead.

With that tangent out of the way, take a look at the JPetStore's dao.xml<transactionManager/> element (see Listing 5).

Listing 5. JPetStore's dao.xml

<transactionManager type='SQLMAP'><property name='SqlMapConfigResource'value='com/ibatis/jpetstore/persistence/sqlmapdao/sql/sql-map-config.xml'/></transactionManager>

As you can see, this implementation uses the SQLMAP transaction manager. Alsonotice that the configuration of the transaction manager isn't at all difficult.

Now look at a code example from JPetStore. The example in Listing 6 is atransaction that is responsible for updating the quantity of products available topurchase, then it inserts an order into the database. If the order insertion or any ofthe updating fails, the database remains unchanged.

Listing 6. Transaction responsible for updating quantity of available products

public class OrderService {private DaoManager daoManager = DaoConfig.getDaomanager();private ItemDao itemDao;private OrderDao orderDao;private SequenceDao sequenceDao;

public void insertOrder(Order order) {try {// Get the next id within a separate transactionorder.setOrderId(getNextId('ordernum'));daoManager.startTransaction();itemDao.updateQuantity(order);orderDao.insertOrder(order);

daoManager.commitTransaction();} finally {daoManager.endTransaction();

}}

}

Large portions of this class that don't apply to the example have been snipped out,but notice that syntactically, you execute the same three methods --startTransaction, commitTransaction, and endTransaction -- inperforming a transaction with the DAO framework as you would with the DataMapper framework. (The Data Mapper framework was used for the sandwich

developerWorks® ibm.com/developerWorks

Transactions, caching, and dynamic SQLPage 8 of 22 © Copyright IBM Corporation 1994, 2008. All rights reserved.

Page 9: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

example to demonstrate transactions in general.) The difference is that here you areinvoking the methods against the DaoManager instance rather than against theSqlMapClient instance.

Section 3. Caching in iBATIS

Caching is critical for large applications to scale under heavy load. Within iBATIS,caching specifically relates to the Data Mapper framework, so you configure it indata map definition XML files. You can then apply a given cache configuration to agiven Mapped Statement. This section describes the different types of caching theData Mapper framework provides and covers some example cache configurations.

Cache types

The iBATIS framework offers a myriad of cache types. They include:

• A memory-based cache that is managed by the Java Virtual Machine's(JVM's) garbage collector.

• Least Recently Used (LRU).

• First In, First Out (FIFO).

• Various caching options available through the OpenSymphony OSCacheAPI (see Resources later in this tutorial for a link to the OSCache Website). (Use of OSCache for caching, an optional dependency of iBATIS,isn't covered in this tutorial.)

Under the LRU algorithm, the object that has been used the least is the first object tobe expelled from the cache when the cache reaches its maximum capacity. Listing 7shows a cache model definition (borrowed from the iBATIS documentation) that isan LRU cache that's flushed every 24 hours and has a maximum size of 1,000objects:

Listing 7. LRU cache model definition

<cacheModel id='product-cache' type='LRU'><flushInterval hours='24'/><flushOnExecute statement='insertProduct'/><flushOnExecute statement='updateProduct'/><flushOnExecute statement='deleteProduct'/><property name='size' value='1000' /></cacheModel>

ibm.com/developerWorks developerWorks®

Transactions, caching, and dynamic SQL© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 9 of 22

Page 10: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

To change this cache to a FIFO cache, you only need to change the type attributefrom LRU to FIFO. In a FIFO cache, the object that has been in the cache for thelongest period of time is expelled from the cache when it reaches capacity.

The cache model XML snippet in Listing 7 also indicates that you wish to have thecache flushed any time the Mapped Statement with an ID of insertProduct,updateProduct, or deleteProduct has been executed. This helps to minimizeor eliminate problems that are due to stale data creeping into the cache.

Cache test

Now look at an example that proves the cache works and that demonstrates staledata in the process.

The JPetStore application has a few cache models defined in the SQL Map files thatit uses. But it doesn't use any of the cache models, so you'll look at a slightlychanged version.

Here is the cache model defined in the JPetStore product.xml file:

<cacheModel id='oneDayProduct' type='LRU'><flushInterval hours='24'/></cacheModel>This cache isn't flushed on the execution of product updates and deletes, whichquickly lead to a stale cache. But that's not what you're concerned about right now;you just want to see an example of this caching in action. Listing 8 shows theMapped Statement in product.xml that is responsible for querying for a product.

Listing 8. Mapped statement in product.xml responsible for querying for aproduct

<select id='getProduct' resultMap='productResult'cacheModel='oneDayProduct' parameterClass='string'>select PRODUCTID, NAME, DESCN, CATEGORY from PRODUCTwhere PRODUCTID = #value#</select>

The boldfaced portion of Listing 8 isn't part of the original JPetStore code. I've addedit so that you can test caching in JPetStore using a test that I've devised. (If you wantto look at the class responsible for the test, you can find it at test.CacheTest inthe download package.)

What the test does

developerWorks® ibm.com/developerWorks

Transactions, caching, and dynamic SQLPage 10 of 22 © Copyright IBM Corporation 1994, 2008. All rights reserved.

Page 11: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

Here are the steps the test goes through:

1. Uses the iBATIS framework to query Derby for the product with the ID ofFI-FW-02. (This product happens to have the name Goldfish.)

2. Prints the name of the product to the console.

3. Gets a direct connection to the Derby database using Derby's embeddeddriver directly from the java.sql.DriverManager, bypassing theiBATIS framework.

4. Updates the product's name in Derby from Goldfish to NinjaGoldfish!!!!

5. Uses iBATIS to query for the product with the ID of FI-FW-02.

6. Prints the name of the product to the console.

Test results you'd expect

This test is an example of what it means to have stale data in your cache. You'dexpect that the second time the name of the product is printed to the console, itwould read Goldfish even though within the database the product name has beenupdated to Ninja Goldfish!!!!. The reason is that when you query for theGoldfish product, the iBATIS framework caches the result. The next time you askfor the same product, it notices that it already has the product you asked for in itscache, so it returns the cached result instead of going to the database.

Run the test

I've added a few targets to the Ant build.xml file to let you play with this. Openyour favorite command prompt or terminal window, and change to the builddirectory. Run the test-cache target:

ant test-cacheTest results

You should get some output like that shown in Figure 1.

Figure 1. Cache and stale data example

ibm.com/developerWorks developerWorks®

Transactions, caching, and dynamic SQL© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 11 of 22

Page 12: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

Things are working as you expect them to -- not as you would want them to in reallife. Even after you've updated the Goldfish to be a Ninja Goldfish, iBATIS returns toGoldfish, which proves that the cache is working.

If this example got you so excited that you want to run it again, you first need toreset the name of the Goldfish in Derby, which you can do by running antreset-goldfish.

Flush the cache

The test you just ran shows why iBATIS provides functionality to have the cacheflushed upon the execution of certain Mapped Statements, like this:

<flushOnExecute statement='insertProduct'/><flushOnExecute statement='updateProduct'/><flushOnExecute statement='deleteProduct'/>Any cached product data is flushed or purged from the cache when aninsertProduct, updateProduct, or deleteProduct Mapped Statement isexecuted.

When using caching in this manner, you also need to be aware of any otherapplications or people who might be updating your database in a different mannerthan through whatever caching framework you're using. Otherwise you can end upwith all sorts of stale cache data.

This should be enough information on caching to get you started. For moreadvanced configuration information and discussion of read/write caches, you can

developerWorks® ibm.com/developerWorks

Transactions, caching, and dynamic SQLPage 12 of 22 © Copyright IBM Corporation 1994, 2008. All rights reserved.

Page 13: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

check out the iBATIS documentation (see Resources later in this tutorial for a link).

Section 4. Dynamic SQL in iBATIS

You've probably run into a situation where a static PreparedStatement wouldn'tdo what you needed, and you needed to generate ad hoc SQL dynamically. Bytaking advantage of dynamic SQL, you can write, say, five to 10 lines of XML ratherthan 200 lines of difficult-to-maintain if/else Java code. In this section, you take alook at a few mechanisms iBATIS offers for generating dynamic SQL statements.

Dynamic Mapped Statements

Suppose your Web application has a search form to let you search for accounts andthat you can enter multiple search criteria. Perhaps you're on the phone with acustomer who has forgotten his or her account ID. This isn't a problem, becauseyour search screen lets you type in the customer's account ID, first name, last name,or e-mail address, and all combinations/permutations of those pieces of data toretrieve the account record. Using iBATIS, you can write a Mapped Statement thatdynamically constructs the SQL based on whatever parameters are present (andeven the values of those parameters). Take a look at the example in Listing 9.

Listing 9. Mapped statement that dynamically constructs SQL-based presentparameters

<statement id='dynamicGetAccountList' resultMap='account-result' >select * from ACCOUNT<dynamic prepend='WHERE'><isNotNull prepend='AND' property='firstName'>(ACC_FIRST_NAME = #firstName#<isNotNull prepend='OR' property='lastName'>ACC_LAST_NAME = #lastName#</isNotNull>

)</isNotNull><isNotNull prepend='AND' property='emailAddress'>ACC_EMAIL like #emailAddress#</isNotNull><isGreaterThan prepend='AND' property='id' compareValue='0'>ACC_ID = #id#</isGreaterThan></dynamic>order by ACC_LAST_NAME</statement>

If you use the dynamic Mapped Statement in Listing 9, the SQL statements in Listing10 are only a few of the total number that can be dynamically generated and

ibm.com/developerWorks developerWorks®

Transactions, caching, and dynamic SQL© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 13 of 22

Page 14: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

executed for you.

Listing 10. SQL statements that could be dynamically generated

select * from ACCOUNT order by ACC_LAST_NAMEselect * from ACCOUNT WHERE ACC_ID = #id# order by ACC_LAST_NAMEselect * from ACCOUNT WHERE (ACC_FIRST_NAME = #firstName#) order by ACC_LAST_NAMEselect * from ACCOUNT WHERE (ACC_FIRST_NAME = #firstName# OR ACC_LAST_NAME = #lastName#)order by ACC_LAST_NAMEselect * from ACCOUNT WHERE (ACC_EMAIL LIKE #emailAddress#) order by ACC_LAST_NAMEselect * from ACCOUNT WHERE (ACC_FIRST_NAME = #firstName#) AND(ACC_EMAIL LIKE #emailAddress#)order by ACC_LAST_NAMEselect * from ACCOUNT WHERE (ACC_FIRST_NAME = #firstName# OR ACC_LAST_NAME = #lastName#)AND (ACC_EMAIL LIKE #emailAddress#) order by ACC_LAST_NAME

You can see that writing Java code if/else statements to generate all of thesestatements dynamically could be an immense hassle that would take a significantamount of hard-to-read code that would be no fun to maintain.

Iterators

The other main form of dynamic SQL statements available to you when you workwith iBATIS come in the form of iterators. An iterator lets you dynamically generateSQL while iterating over a collection. This is most useful in areas related tosearching or querying. Take a look at Listing 11 for an example specifically fromJPetStore that has to do with product searching.

Listing 11. Product searching in the product.xml file

<select id='searchProductList' resultMap='productResult'>select PRODUCTID, NAME, DESCN, CATEGORY from PRODUCT<dynamic prepend='WHERE'><iterate property='keywordList' open='' close='' conjunction='OR'>lower(name) like #keywordList[]# OR lower(category) like#keywordList[]# OR lower(descn) like #keywordList[]#</iterate></dynamic></select>

For kicks, I wrote another little test that you can run with Ant. This one spits out thefull query based on the iterator in Listing 11 and the parameters used in the search.My amazingly simple test method looks like Listing 12.

Listing 12. Test generating full query

public void test() {String keywords = 'fish dog cat uncle jim';List l = CatalogService.getInstance().searchProductList(keywords);}

developerWorks® ibm.com/developerWorks

Transactions, caching, and dynamic SQLPage 14 of 22 © Copyright IBM Corporation 1994, 2008. All rights reserved.

Page 15: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

You can run this test yourself with ant test-iterator. To get the DEBUGlogging, I modified the log4j.properties file in the download package so that therootLogger looks like this: log4j.rootLogger=DEBUG, stdout.

My result from running 'ant test-iterator' looks like Figure 2.

Figure 2. Testing iBATIS dynamic SQL iterator

You can't quite get the full effect from the DOS box in Figure 2, so Listing 13 showsyou the SQL that Derby executed (from the debug output in the command window).

Listing 13. SQL that Derby executed

select PRODUCTID, NAME, DESCN, CATEGORY from PRODUCT WHERElower(name) like ? OR lower(category) like ? OR lower(descn) like ? ORlower(name) like ? OR lower(category) like ? OR lower(descn) like ? ORlower(name) like ? OR lower(category) like ? OR lower(descn) like ? ORlower(name) like ? OR lower(category) like ? OR lower(descn) like ? ORlower(name) like ? OR lower(category) like ? OR lower(descn) like ?

The parameters that iBATIS passed off to Derby to populate thePreparedStatement object were:

Parameters: [%fish%, %fish%, %fish%, %dog%, %dog%, %dog%,%cat%, %cat%, %cat%, %uncle%,%uncle%, %uncle%, %jim%, %jim%, %jim%]It's nice that such a relatively small amount of XML configuration can get you apowerful bit of functionality. I'm all about minimizing the amount of grunt type codingthat I need to do, and iBATIS is certainly helping out.

ibm.com/developerWorks developerWorks®

Transactions, caching, and dynamic SQL© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 15 of 22

Page 16: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

Section 5. Standalone DAO example

This section looks at a simple example of how you can use the iBATIS DAOframework in a manner that is totally separate from the Data Mapper framework.

Taking your time

This example builds a DAO interface and implementation that tries to use theNetwork Time Protocol (NTP) to get the current time from a remote server. Youtransparently fail over to the current time of the local machine if you have problemsor errors connecting to a time server. Remember, the DAO wants to provide aconsistent way to access data. The current time of day could likely be data pertinentto your application, so in this example the DAO Framework provides you with a wayto access that data.

The interface

You know that to write a DAO, you need an interface to implement. And the interfacemust define the methods that you need to access your data. For getting the time,you should have a getTime() method, as in Listing 14.

Listing 14. An interface

package com.ibatis.jpetstore.standalone;import java.util.Date;

/*** @author daniel*/public interface GetTimeDao {public Date getTime();}

Now, you implement the interface.

The implementation

Remember that in the implementation of the interface shown in Listing 14, you alsoneed to implement the iBATIS marker interface -- com.ibatis.dao.client.Dao-- to let the DAO framework know that you'd like to be managed. Listing 15 showsthis implementation.

developerWorks® ibm.com/developerWorks

Transactions, caching, and dynamic SQLPage 16 of 22 © Copyright IBM Corporation 1994, 2008. All rights reserved.

Page 17: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

Listing 15. Implementing the iBATIS marker interface

package com.ibatis.jpetstore.standalone;import com.ibatis.dao.client.Dao;import org.apache.commons.net.ntp.NTPUDPClient;import org.apache.commons.net.ntp.TimeInfo;import java.net.InetAddress;import java.util.Date;

public class GetTimeDaoImpl implements Dao, GetTimeDao {public Date getTime() {try {NTPUDPClient client = new NTPUDPClient();// We want to timeout if a response takes longer than 60 secondsclient.setDefaultTimeout(10000);TimeInfo out =client.getTime(InetAddress.getByName('pool.ntp.org'));return new Date(out.getReturnTime());} catch (Exception e) {//If something craps out, return the Date on this machine insteadreturn new Date();

}}

}

It's worth noting that I used the Commons Net package from the Apache Jakartaproject for the purpose of NTP access. Otherwise, you can see that the class inListing 15 is very straightforward. It tries to get a Date object from an NTP server,and if it can't, it returns the Date on the machine that it executes on.

Another thing that's important to notice about this class is that it has nothing to dowith the Data Mapper framework. It uses a completely different protocol (NTP) andno database at all. But as you'll see, where the data is coming from is abstractedaway from your application's business-logic tier. Follow along with the JPetStoreexample of providing a service-type class to front the DAO access.

The TimeService class

You want to write a TimeService class that you expose to the application'sbusiness-logic tier. It's through this class that the application can invoke the methodto get the time (see Listing 16).

Listing 16. TimeService class

//snip imports etc.public class TimeService {private static final TimeService instance = new TimeService();private DaoManager daoManager = DaoConfig.getDaomanager();private GetTimeDao timeDao;public TimeService() {timeDao = (GetTimeDao) daoManager.getDao(GetTimeDao.class);}

public static TimeService getInstance() {

ibm.com/developerWorks developerWorks®

Transactions, caching, and dynamic SQL© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 17 of 22

Page 18: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

return instance;}

public Date getTime() {return timeDao.getTime();}

}

Now you have a service class that you expose to the application's business-logictier. When you want to get the time, you can now do this anywhere in yourapplication:

TimeService.getInstance().getTime();And what's the beauty of this? Well, it's a simple example, but the beauty is theconsistency and the fact that the code to do the work of getting the time is only everin a single location, not scattered throughout your application's business-logic tier.The DAO framework takes care of the underlying muck, and sitting below the DAOframework is your getTime() implementation, which you can change any time youwant. You don't need to worry about it. Because no one will ever be accessing yourGetTimeDaoImpl class directly, it will only ever be accessed by the DAOFramework, which is responsible for providing access to the underlying getTime()method to the rest of your application. And that, in a nutshell, is the power of theDAO framework.

Of course, you skipped one step, which is adding your GetTimeDao to the dao.xml,but that's easy:

<dao interface='com.ibatis.jpetstore.standalone.GetTimeDao'implementation='com.ibatis.jpetstore.standalone.GetTimeDaoImpl'/>And, just because I know how much you like to test this stuff out, I wrote an Ant taskand test class that lets you run this. Just run ant test-get-time from thecommand line, and you should get something like what's shown in Figure 3.

Figure 3. New TimeService DAO in action

developerWorks® ibm.com/developerWorks

Transactions, caching, and dynamic SQLPage 18 of 22 © Copyright IBM Corporation 1994, 2008. All rights reserved.

Page 19: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

So, there you have the separation of the DAO and Data Mapper frameworks.

Section 6. Summary

In this tutorial, you learned how to use transactions with the JDBC transactionmanager provided with iBATIS and how to begin, commit, and end transactionsusing both the Data Mapper and the Data Access Objects frameworks. You alsolooked at caching examples in iBATIS and played with Derby and iBATIS to provethat the caching works the way it ought to. You then read about dynamic SQLstatements and how you can use some of the iBATIS data map configurationsemantics to do the work for you. You also looked at the actual queries executed byDerby after the dynamic SQL was processed. Finally, you saw an example that usedthe DAO alone without the Data Mapper framework.

With this tutorial series now under your belt, you should be ready to use iBATIS andDerby to develop your own Java-based database-driven applications.

ibm.com/developerWorks developerWorks®

Transactions, caching, and dynamic SQL© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 19 of 22

Page 20: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

Downloads

Description Name Size Download method

Part 3 source code ibatis.part3.code.zip45MB HTTP

Information about download methods

developerWorks® ibm.com/developerWorks

Transactions, caching, and dynamic SQLPage 20 of 22 © Copyright IBM Corporation 1994, 2008. All rights reserved.

Page 21: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

Resources

Learn

• Read Part 1 of this tutorial series, "Initial configuration, semantics, and a simpletest" (developerWorks, January 2006) for an introduction to iBATIS as apersistence mechanism and some basic examples of how to configure ApacheDerby with iBATIS and perform simple OR mapping using Data Mapper.

• Get the iBATIS DAO Official Documentation (PDF) and iBATIS Official DataMapper Documentation (PDF).

• See "Using Apache Derby with iBATIS JPetStore 4 on the WebSphere Server"for a document that demonstrates setting up the JPetStore 4.0 application torun on a J2EE application server using an embedded Apache Derby databaseas the persistence layer.

• Read "DB2® UDB, WebSphere, and iBATIS" (developerWorks, February 2005),which covers the iBATIS syntax, accessing data sources, and setting up IBMWebSphere® Studio Application Developer, Version 5.1.2 projects to supportiBATIS.

• Check out "Tired of hand coding JDBC? Use iBATIS as a data mappingframework instead" (developerWorks, October 2005) to learn about the use ofiBATIS as a data-mapping framework.

• See "Java validation with dynamic proxies" (developerWorks, September 2004)for an article that shows you how dynamic proxies can keep your coreapplication code free of validation routines and focused solely on businesslogic.

• Read the article "Using iBATIS SQL Maps for Java Data Access," whichdemonstrates how the latest production release (1.3.1) of SQL Maps works.

• Visit the official iBATIS site.

• Check out "Object-Relational Mapping with SQLMaps" for a step-by-step tutorialon using the iBATIS SQLMaps framework.

• See "iBatis DAO" for a step-by-step guide on how to use the iBatis DAOframework in your application.

• Visit the developerWorks Open source zone for extensive how-to information,tools, and project updates to help you develop with open source technologiesand use them with IBM's products.

• Check out the Apache Derby project area for articles, tutorials, and otherresources to help you get started developing with Derby today.

• Browse all the Apache articles and free Apache tutorials available in the

ibm.com/developerWorks developerWorks®

Transactions, caching, and dynamic SQL© Copyright IBM Corporation 1994, 2008. All rights reserved. Page 21 of 22

Page 22: Improve persistence with Apache Derby and iBATIS, Part 3 ... · dataMapper.endTransaction(); //Clean up! You must always make the call to endTransaction(). That's why you put it in

developerWorks Open source zone.

• Visit the Apache Derby project Web site.

Get products and technologies

• Download the original unmodified source code for the JPetStore application.

• Get OSCache, a caching solution that includes a JavaServer Pages (JSP) taglibrary and set of classes to perform fine-grained dynamic caching of JSPcontent, servlet responses, or arbitrary objects.

About the author

Daniel WintschelDaniel Wintschel is a regular guy who derives great excitement from solving businessproblems, streamlining processes, and writing all sorts of Java code. He loves coffeeand is currently seeking development opportunities on a telecommuting basis fromSingapore. You can contact the author at [email protected].

developerWorks® ibm.com/developerWorks

Transactions, caching, and dynamic SQLPage 22 of 22 © Copyright IBM Corporation 1994, 2008. All rights reserved.