Better Faster Lighter Java [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Better Faster Lighter Java [Electronic resources] - نسخه متنی

Justin Gehtland; Bruce A. Tate

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








8.4 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.


8.4.1 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.


8.4.2 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:

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.

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.

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.

This SQL statement finds a product, given a
productID.

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

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.


8.4.3 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.


8.4.4 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.


8.4.5 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.


8.4.6 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.


8.4.7 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.


/ 111