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

advertisement

AddThis Social Bookmark Button

Persistence in Spring
Pages: 1, 2, 3

The DAO Implementation

All that remains is to implement the interface with SQL Map. Example 8-6 is the SQL Map implementation for Product.



Example 8-6. SqlMapProductDao.java

    public class SqlMapProductDao extends SqlMapDaoSupport implements ProductDao {

[1]  public List getProductListByCategory(String categoryId) throws DataAccessException {
       return getSqlMapTemplate( ).executeQueryForList("getProductListByCategory", 
     }

[1]  public Product getProduct(String productId) throws DataAccessException {
       return (Product) getSqlMapTemplate( ).executeQueryForObject("getProduct", productId);
     }

[1]  public List searchProductList(String keywords) throws DataAccessException {
       Object parameterObject = new ProductSearch(keywords);
       return getSqlMapTemplate( ).executeQueryForList("searchProductList", parameterObject);
     }

     /* Inner Classes */

[2]  public static class ProductSearch {

        private List keywordList = new ArrayList( );

        public ProductSearch(String keywords) {
          StringTokenizer splitter = new StringTokenizer(keywords, " ", false);
          while (splitter.hasMoreTokens( )) {
            this.keywordList.add("%" + splitter.nextToken( ) + "%");
          }
        }

        public List getKeywordList( ) {
          return keywordList;
        }
     }

    }

Here's what the annotations mean:

[1] These methods provide the SQL Map implementation of the interface. Other implementations might use Hibernate, JDO, or straight JDBC. In this case, the getTemplate call instructs Spring to get the template for iBATIS SQL Map support and execute the appropriate query using the framework.

[2] I'm not a big fan of inner classes, but that's what they used to implement the keyword search. In this case, the inner class supports the searchProductList method by implementing getKeywordList. The inner class helps to organize the code base, keeping all of the support in one location, with the rest of the DAO implementation.

Now you've seen the mapping, the model, and the DAO. You have a fully persistent model. Next, access the DAO layer with code. jPetStore funnels all DAO access through a façade layer.

Using the Model Through a Façade

Just as in Chapter 3, it often makes sense to have a higher-level interface for a model, called the façade. In this case, the jPetStore façade serves three purposes:

  • Consolidates all of the clients of the data access layer.
  • Presents a single, common user interface for the rest of the applications.
  • Serves as an attachment point for other services, such as transaction support.

In this case, the façade is a very thin layer around all of the DAO. Through configuration and method interceptors, Spring attaches declarative transaction support to the façade. In this case, the façade is in two parts: the interface and the implementation. The interface allows you to change the implementation of the façade without impacting the rest of the code. Example 8-7 shows the interface.

Example 8-7. PetStoreFacade.java

public interface PetStoreFacade {

  Account getAccount(String username);
  Account getAccount(String username, String password);
  void insertAccount(Account account);
  void updateAccount(Account account);

  List getUsernameList( );

  List getCategoryList( );
  Category getCategory(String categoryId);

  List getProductListByCategory(String categoryId);
  List searchProductList(String keywords);
  Product getProduct(String productId);

  List getItemListByProduct(String productId);
  Item getItem(String itemId);
  boolean isItemInStock(String itemId);

  void insertOrder(Order order);
  Order getOrder(int orderId);
  List getOrdersByUsername(String username);

}

Think of this interface as a consolidated list of every method that creates, reads, updates, or deletes any Pet Store object. Notice that you do not see every method from all of the DAO. You see only the methods that we wish to expose to the rest of the world. Also, notice the naming consistency within the interface. This is important because within our configuration file, you saw the transaction support configured to propagate methods beginning with get, search, update, or insert.

The implementation simply calls the underlying DAO to do the appropriate job. It must implement all of the methods in the interface. Example 8-8 is the implementation of the methods related to the ProductDAO.

Example 8-8. Excerpt fromPetStoreImpl.java

[1] private ProductDao productDao;

  ...

    public void setProductDao(ProductDao productDao) {
       this.productDao = productDao;
	}

  ...

[2] public List getProductListByCategory(String categoryId) {
       return this.productDao.getProductListByCategory(categoryId);
    }
	
    public List searchProductList(String keywords) {
       return this.productDao.searchProductList(keywords);
    }
  ...

Here's what the annotations mean:

[1] Shows the DAO access (includes the bold text). The Spring framework inserts the DAO into the façade using reflection. That means the façade must support a set method and a private member variable.

[2] The methods that provide data access use the underlying DAO to do the actual work (includes the bold text).

Of course, I haven't shown the implementation of all of the interface's methods. These are only the methods related to product. They come in two parts.

First, the application context wired each DAO to the façade. Spring uses reflection and the bean factory to create the product DAO and set it using the setProductDAO API. To support this, the façade needs a variable to hold the DAO and a set method to access it through reflection.

Second, the implementation is simple. The façade merely passes the request through to the model layer underneath. The ultimate implementation is much more powerful, though. The façade functions like an EJB session bean with respect to declarative transaction support. Through configuration, the POJO becomes a declarative transaction coordinator! It's also a central point of control for the entire database layer. All that remains is to configure the DAO layer.

Configuration for the DAO Layer

Recall that you have seen only the configuration for the model. Example 8-9 shows the configuration of the data layer for a single database with simple transaction management. As you'd expect, you'll see the configuration of the JDBC driver and the declaration of all of the DAO beans.

Example 8-9. dataAccessContext-local.xml

   <beans>

[1] <bean id="propertyConfigurer"
       class="org.springframework.beans.factory.
       config.PropertyPlaceholderConfigurer">
       <property name="location"><value>/WEB-INF/jdbc.properties</value></property>
    </bean>

[2] <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
         destroy-method="close">
       <property 
         name="driverClassName"><value>${jdbc.driverClassName}</value></property>
       <property name="url"><value>${jdbc.url}</value></property>
       <property name="username"><value>${jdbc.username}</value></property>
       <property name="password"><value>${jdbc.password}</value></property>
    </bean>

[3] <bean id="transactionManager"
       class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource"><ref local="dataSource"/></property>
    </bean>

[4] <bean id="sqlMap" class="org.springframework.orm.ibatis.SqlMapFactoryBean">
       <property name="configLocation">
       <value>classpath:/sql-map-config.xml</value></property>
    </bean>

[5] <bean id="accountDao" class=" jpetstore.dao.ibatis.SqlMapAccountDao">
       <property name="dataSource"><ref local="dataSource"/></property>
	   <property name="sqlMap"><ref local="sqlMap"/></property>
	</bean>
	 
	<bean id="categoryDao" class="jpetstore.dao.ibatis.SqlMapCategoryDao">
	   <property name="dataSource"><ref local="dataSource"/></property>
	   <property name="sqlMap"><ref local="sqlMap"/></property>
	</bean>
	 
	<bean id="productDao" class=" jpetstore.dao.ibatis.SqlMapProductDao">
	   <property name="dataSource"><ref local="dataSource"/></property>
	   <property name="sqlMap"><ref local="sqlMap"/></property>
	</bean>
	 
	<bean id="itemDao" class=" jpetstore.dao.ibatis.SqlMapItemDao">
	   <property name="dataSource"><ref local="dataSource"/></property>
	   <property name="sqlMap"><ref local="sqlMap"/></property>
	</bean>
	 
	<bean id="orderDao" class=" jpetstore.dao.ibatis.SqlMapOrderDao">
	   <property name="dataSource"><ref local="dataSource"/></property>
	   <property name="sqlMap"><ref local="sqlMap"/></property>
	   <property name="sequenceDao"><ref local="sequenceDao"/></property>
    </bean>
	
	<bean id="sequenceDao" class="jpetstore.dao.ibatis.SqlMapSequenceDao">
	   <property name="dataSource"><ref local="dataSource"/></property>
	   <property name="sqlMap"><ref local="sqlMap"/></property>
	</bean>

   </beans>

Here's what the annotations mean:

[1] This bean handles the JDBC configuration. The JDBC configuration properties are in a standard JDBC configuration file, making them easier to maintain and read. Spring provides a configuring class that makes it easy to read property files without converting them to XML.

[2] Here you see the data source. It's a standard J2EE data source. Many J2EE applications and frameworks hard-wire an application or framework to a given data source. Configuring them instead makes it easy to choose your own source (and thus your pooling strategy).

[3] The applicationContext.xml configuration sets the transaction policy. This configuration specifies the implementation. This application uses the data source transaction manager, which delegates transaction management to the database via JDBC (using commit and rollback).

[4] The iBATIS SQL Map utility for building DAO must be configured. It's done here.

[5] Finally, you see the actual DAO configuration. As you may remember, the applicationContext.xml file referred to each of these beans by name.

This configuration accomplishes more than just decoupling the persistence tier from the model or the view. We've also decoupled transaction management from the persistence layer, separated the transaction policy from the implementation, and isolated the data source. Take a look at the broader benefits that have been gained beyond configuration.

The Benefits

That's all of the persistence code for the Product. The code for the rest of jPetStore is similar. The application effectively isolates the entire domain model within a single layer. The domain has no dependencies on any services, including the data layer. You've also encapsulated all data access into a clean and concise DAO layer, which is independent of data store. Notice what you don't see:

Data source configuration

Handled by the Spring framework. You don't have to manage a whole bunch of singletons, for session management, data sources, and the like. You can also delay key decisions such as the type of data source until deployment time.

Connection processing

The Spring framework manages all of the connection processing. One of the most common JDBC errors is a connection leak. If you're not very careful about closing your connections, especially within exception conditions, your application can easily lose stability and crash.

Specialized exceptions

Many frameworks pass SQL exceptions to the top. They frequently have SQL codes built in that may be specialized to your own RDBMS, making it difficult to code portable applications. Spring has its own exception hierarchy, which insulates you from these issues. Further, should you change approaches to Hibernate or JDO, you won't need to change any of your exception processing.

The end result of what we've done so far is pretty cool. We have a clean, transparent domain model and a low-maintenance service layer that's independent of our database. Each layer is neatly encapsulated. Now that we have looked at the backend logic, it's time to put a user interface on this application.

Pages: 1, 2, 3

Next Pagearrow