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

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

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

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

Justin Gehtland; Bruce A. Tate

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

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

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








10.6 Adding Hibernate


jPetStore uses a relatively

straightforward architecture for providing
database access. There is an interface layer that provides functional
mapping to the DAOs themselves without worrying about actual
implementation details. The specific DAOs vary based on the backend
database; we'll be examining the ones targeting
HSQLDB (Hypersonic SQL).


10.6.1 Existing Architecture


Let's look at how the Product
class is managed. Product is the domain object
that represents one item in the catalog.

package org.springframework.samples.jpetstore.domain;
import java.io.Serializable;
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( );
}
}

Its persistence is managed through an object that implements the
ProductDao interface. A
ProductDao must be able to load a specific product
given its ID, or load a list of products either from a category or
from a set of keywords.

public interface ProductDao {
List getProductListByCategory(String categoryId) throws DataAccessException;
List searchProductList(String keywords) throws DataAccessException;
Product getProduct(String productId) throws DataAccessException;
}

There currently exists a class called
SqlMapProductDao that looks up product information
in Hypersonic SQL through SQL mapping files.


10.6.2 Hibernate Mappings for Existing Domain Objects


To replace this architecture with one based on Hibernate, we first
have to create mapping files that define the relationship between the
domain objects and the database. Looking again at
Product, we'll create a mapping
file called Product.hbm.xml which looks like:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping
package="org.springframework.samples.jpetstore.domain">
<class name="Product" table="product">
<id name="productId"
column="productId"
type="string">
<generator class="native"/>
</id>
<property name="categoryId" column="category" type="string"/>
<property name="name" column="name" type="string"/>
<property name="description" column="description" type="string"/>
</class>
</hibernate-mapping>

In the mapping file, we first identify the package and particular
class
(org.springframework.samples.jpetstore.domain.Product)
that we are mapping. We have to tell it what table to map to
("product", in this case) and then map the
individual properties of the domain object to the columns in the
table. This file needs to be saved somewhere on the class path;
we'll create a new folder in the project structure
called "hibernate" to hold our map
files and our new DAOs.


10.6.3 Hibernate DAOs


The next step is to create a DAO that uses Hibernate as the
persistence layer instead of the SQL mappings used in the original
version. The new DAO needs to implement the
ProductDao interface, just like the original DAO.
However, the implementation of that interface will be totally
different.

Here is the code for the new DAO:

public class HibernateProductDao implements ProductDao {
SessionFactory factory;
Configuration cfg;
public HibernateProductDao( ) {
try {
cfg = new Configuration( ).addClass(
org.springframework.samples.jpetstore.domain.Product.class);
factory = cfg.buildSessionFactory( );
} catch (Exception ex) {
System.out.println("Hibernate configuration failed: " + ex);
}
}
public List getProductListByCategory(String categoryId)
throws DataAccessException {
List results = null;
try {
Session session = factory.openSession( );
results = session.find("from product where product.category = ?",
categoryId, Hibernate.STRING);
session.close( );
} catch (Exception ex) {
System.out.println("Failed to connect to database:" + ex);
}
return results;
}
public List searchProductList(String keywords) throws DataAccessException {
return null;
}
public Product getProduct(String productId) throws DataAccessException {
Product p = null;
try {
Session session = factory.openSession( );
p = (Product)session.load(Product.class, productId);
session.close( );
} catch (Exception ex) {
System.out.println("failed to connect to database: " + ex);
p = null;
}
return p;
}
}

First, we need a way to interact with Hibernate. As shown in Chapter 7, we need to create a Hibernate
SessionFactory and use it to get a
Session with which to interact with the database.
The DAO's constructor instantiates a new Hibernate
configuration, loading the mapping file from the class path based on
the name of the class added to the configuration. Then, it gets the
SessionFactory from the Configuration.

Each method uses the SessionFactory to open a new
Session with the database. The
getProduct method is the most straightforward;
first, we get the Session. Then, we ask the session to load an
instance of the Product class, given its
productId. Note that the result from the session.load(
)
call is of type Object, which we have
to cast to Product. Finally, we close the Session. Hibernate handles
all the SQL commands, looking up the mapping files, matching the
productId to the right column in the table, populating all the
fields, everything.

The getProductListByCategory( ) method is less
straightforward; it takes a categoryId and returns
a List of all the products that match that category. In this case, we
can't rely on the built-in SQL generation; we have
to create our own query. Again, we first grab a
Session from the
SessionFactory, then use the
session.find( ) method, which returns a
List of Objects. Find takes
three parameters in this case: the HSQL query (which contains a
placeholder for a query parameter, marked with a
"?"), the value to fill into the
query parameter, and the type of that parameter.

As shown in Chapter 7, HSQL (Hibernate SQL)
queries look a lot like regular SQL statements, except here we left
off the "SELECT [values]" part of
the statement, since Hibernate will fill those in for us based on the
mapping. This method will now look up all the rows in the Product
table where categoryId equals the value passed in
to the method, and create one instance of Product
for each row in the resultset. All the product instances are placed
in a List and returned.

The final method of the DAO, searchProductList,
would be a lot more complex, but luckily, we don't
have to implement it. Since we have already replaced the original
search functionality with the Simple Spider, this method will never
be called now, so we simply return null (we have
to do something, since the ProductDao interface
still mandates its inclusion).

To finish out the new architecture, we just repeat these steps for
each of the remaining five domain objects. Each gets a mapping file
and an implementation of the appropriate DAO interface.


10.6.4 Changing the Application Configuration


In order to get the new DAOs working with
jPetStore, we need to modify some configuration
files. First, we'll need to create the global
hibernate.properties file, which tells Hibernate
which database to use and how to use it. jPetStore
is currently configured to use a local instance of Hypersonic SQL,
with a username of "sa" and a blank
password (NEVER do this in a production environment). The
hibernate.properties file looks like this:

hibernate.connection.driver_class = org.hsqldb.jdbcDriver
hibernate.connection.url = jdbc:hsqldb:hsql://localhost:9002
hibernate.connection.username = sa
hibernate.connection.password =
hibernate.dialect=net.sf.hibernate.dialect.HSQLDialect
hibernate.show_sql=true

This file should be saved in the project root file, next to the other
global configuration files. Hibernate will look for it by name.

Next, open up jPetStore's
dataAccessContext-*.xml files (one is
dataAccessContext-jta.xml and the other is
<property name="dataSource"><ref local="dataSource"/></property>
<property name="sqlMap"><ref local="sqlMap"/></property>
</bean>

This now becomes:

<bean id="productDao" class="org.springframework.samples.jpetstore.dao.hibernate.
HibernateProductDao"/>

We can eliminate the properties because the Hibernate versions of the
DAOs do not require any configuration information to be passed in by
the controller; Hibernate manages those issues for us.

Once you have successfully changed all the DAO references, the last
remaining piece is to include the necessary jar
files in your class path. Hibernate requires the following
jars: hibernate2.jar,
cglib2.jar, ehcache.jar,
commons-collections.jar,
dom4j.jar, and jta.jar (all
of which are included in the Hibernate download).


10.6.5 Spring's Built-In Hibernate Support


Now that you have seen the explicit way to do things,
let's briefly take a look at the supporting
infrastructure Spring provides for Hibernate. Spring, through its
"inversion of control"
architecture, can fully manage the creation of the
SessionFactory for you. In addition, it provides a
new class, HibernateDaoSupport, which allows your
application-specific DAOs to reuse standard, template-derived calls
for interacting with the datasource.

To set it up, you need to change your DAOs to extend
HibernateDaoSupport. So, this:

public class HibernateProductDao implements ProductDao

becomes:

public class HibernateProductDao extends HibernateDaoSupport implements ProductDao

Then add the following code to enable Spring to pass in a
SessionFactory:

private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}

After adding this, your DAOs can use an object provided by
HibernateDaoSupport called
HibernateTemplate. This new class, accessed
through the new getHibernateTemplate( ) method
inherited from HibernateDaoSupport, exposes a
series of helper methods for interacting with the database, such as
load, save,
update, saveOrUpdate,
get, and find. Our
ProductDao becomes a lot simpler:

public class HibernateProductDao extends HibernateDaoSupport implements ProductDao {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public HibernateProductDao( ) {
}
public List getProductListByCategory(String categoryId) {
return getHibernateTemplate( ).find("from product where product.category = ?",
categoryId, Hibernate.STRING);
}
public List searchProductList(String keywords) throws DataAccessExcetption {
return null;
}
public Product getProduct(String ProductID) throws DataAccessException {
return (Product) getHibernateTemplate( ).load(Product.class, productId);
}
}

To configure all of this, you'll have to make some
changes to your configuration files. You now have to add a property
for the SessionFactory where you defined the
ProductDao bean:

<bean id="productDao"
class="org.springframework.samples.jpetstore.dao.hibernate.HibernateProductDao">
<property name="sessionFactory"/>
<ref bean="mySessionFactory"/>
</bean>

Then add a definition of the mySessionFactory bean:

<bean id="mySessionFactory"
class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="mappingResources">
<list>
<value>product.hbm.xml</value>
</list>
<!-- etc. -->
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">net.sf.hibernate.dialect.HSQLDialect</prop>
</props>
</property>
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>

Add as many entries to the mappingResources
property as you have map files, and make sure
that the dataSource property refers to your
already-configured dataSource bean. With these
minimal changes, your DAOs become much more standardized and
compacted, and Spring handles all your
SessionFactory and Session
implementation details for you. You are free to focus, yet again, on
the problem at hand rather than the supporting framework.

That's it! Once again, we've
managed to replace an entire swath of existing code without touching
the original codebase itself. We have simply added new classes to the
project and changed some configuration settings, and voila!
Hibernate.


10.6.6 Principles in Action


Keep it simple: domain objects remain unaware of persistence logic,
Hibernate manages all configuration

Choose the right tools: Hibernate

Do one thing, and do it well: the domain model is focused on the
business problem, the DAOs focus on data manipulation and are
database-agnostic

Strive for transparency: domain model is completely unaware of
persistence layer

Allow for extension: Spring configuration

and IoC allow us
to change persistence layers



/ 111