ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button O'Reilly Book Excerpts: Better, Faster, Lighter Java

Persistence in Spring

by and Justin Gehtland

Editor's note: It's usually better to solve problems with simplicity and finesse rather than muscle. That's the premise behind the recently released Better, Faster, Lighter Java. Its authors, Bruce Tate and Justin Gehtland, put this belief to the test in this two-part series on Spring, excerpted from Chapter 8 of their book. This week Bruce and Justin continue where they left off in part one, taking you through adding persistence to the Pet Store example, and looking at the area of presentation logic in the Spring framework.

Adding Persistence

The CartItem object does not necessarily need to be persistent. On the other hand, you'd expect to pull products and categories from a database. J2EE application developers have long searched for a clean approach to persistence without much success. The best persistence frameworks allow transparency and do not invade the domain model. Spring lets you separate your transparent object from the data access layer. Spring then makes it easy to layer on persistence. You can use a JDBC abstraction layer, which abstracts away many of the tedious and error-prone aspects of JDBC, such as connection management and error handling. The Spring JDBC layer uses a feature called callback templates to pass control from your application to the framework. With this strategy, Spring removes the need to manage connections, result sets, and RDBMS-specific errors. This framework is useful when you want to use JDBC directly to process relational queries.

Often, you'd rather deal with objects instead of relations. Spring also has an appropriate model for transparent persistence. The jPetStore uses Spring's OR mapping layer, which provides a variety of prepackaged choices. Spring now supports mapping layers for basic JDBC DAO, Hibernate, and JDO. This example uses a DAO framework called iBATIS SQL Maps to implement a Spring DAO layer.

The Model

Each of the Spring solutions starts with a transparent domain model. Example 8-3 starts with the transparent model object, a product.

Example 8-3. Product.java

public class Product implements Serializable {

  private String productId;
  private String categoryId;
  private String name;
  private String description;

  public String getProductId( ) { return productId; }
  public void setProductId(String productId) { this.productId = productId.trim( ); }

  public String getCategoryId( ) { return categoryId; }
  public void setCategoryId(String categoryId) { this.categoryId = categoryId; }

  public String getName( ) { return name; }
  public void setName(String name) { this.name = name; }

  public String getDescription( ) { return description; }
  public void setDescription(String description) { this.description = description; }

  public String toString( ) {
    return getName( );
  }

}

There's nothing special here. It consists purely of properties, accessed through getters and setters, and one utility method, toString. When you look into the jPetStore application, you'll find similar classes for each of the other persistent objects in the domain: Account, Order, Category, Item, and LineItem.

The Mapping

As with Hibernate, the iBATIS SQL Maps framework has a mapping file. In it, each persistent property in your Java bean maps onto a single database column. Using SQL Maps, create all of your SQL within that mapping file as well, isolating all SQL to your XML mapping files. Example 8-4 shows the XML mapping support for Product.

Example 8-4. Product.xml

[1] <sql-map name="Product">
 
[2]  <cache-model name="oneDayProduct" reference-type="WEAK"
        <flush-interval hours="24"/>
     </cache-model>

[3]  <result-map name="result" class="jpetstore.domain.Product">
       <property name="productId" column="PRODUCTID" columnIndex="1"/>
       <property name="name" column="NAME" columnIndex="2"/>
       <property name="description" column="DESCN" columnIndex="3"/>
       <property name="categoryId" column="CATEGORY" columnIndex="4"/>
     </result-map>

[4]  <mapped-statement name="getProduct" result-map="result"> 
       select PRODUCTID, NAME, DESCN, CATEGORY from PRODUCT where PRODUCTID = #value#
     </mapped-statement>

[5]  <mapped-statement name="getProductListByCategory" result-map="result"> 
       select PRODUCTID, NAME, DESCN, CATEGORY from PRODUCT where CATEGORY = #value#
     </mapped-statement>

[6]  <dynamic-mapped-statement name="searchProductList" result-map="result"> 
       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>
     </dynamic-mapped-statement>

    </sql-map>

Here's what the annotations mean:

[1] Each mapping file corresponds to a domain object. The domain object in this case relates to the return types of the queries specified for this DAO.

[2] Other information about the DAO layer, like caching strategies, also belong in the mapping file. Here, iBatis maintains a cache for 24 hours and then flushes it.

[3] Each of these queries returns, of course, a product. This mapping ties each column of the result set to one of the fields in product.

[4] This SQL statement finds a product, given a productID.

[5] This SQL statement finds all products in a category. It returns a list of products.

[6] This SQL statement is dynamic. iBatis iterates over the keyword list and forms a dynamic query.

So far, you've seen the domain model for Product and its mapping, which contains queries. You're most of the way home.

The DAO Interface

Somehow, the application must integrate with both Spring and SQL Maps. The application ties the two concepts together with a DAO interface, and a concrete implementation. Example 8-5 is the interface.

Example 8-5. ProductDAO.java

public interface ProductDao {

  List getProductListByCategory(String categoryId) throws DataAccessException;

  List searchProductList(String keywords) throws DataAccessException;

  Product getProduct(String productId) throws DataAccessException;

}

That's simple enough. You can see an interface for each of the queries defined in the mapping. Specifically, you can see an interface getProduct that finds a product by ID, one for getProductListByCategory that returns all products in a category, and one for the dynamic query based on keywords. Now, the DAO throws Spring exceptions; any logic that uses the DAO will have consistent exceptions, even if you later decide to change implementations.

Pages: 1, 2, 3

Next Pagearrow