Chapter 5. OR Persistence

Near where I live, there’s a notorious mountain biking trail with a hill called Beer Mountain. I have no idea how it got its name, because you’ve got to be stone-cold sober to even contemplate going down. As far as I know, it’s never been cleanly climbed (or climbed without stepping off of the bike). Most think it’s ridiculous to even try. The hill is long and has an insanely steep gradient. It’s covered with loose rock and lots of ledges. I must be too bored with the rest of my life, because I’ve been working to climb that hill for two and a half years. A hill like that one looks to me like a difficult programming problem. I have started over many times, trying different approaches to the many ledges and boulder gardens on the way to the top. To climb Beer Mountain, I needed to improve my bike, my technique, and my conditioning.

Integrating iBATIS

In a fiasco that’s become famous, a sample application from Sun was used as a centerpiece for a benchmark in a widely publicized competition between J2EE and .NET. The .NET version soundly beat the EJB-based J2EE version, and the heat was on. Clinton Begin built a simplified version of PetStore around his persistence framework called iBATIS, and it’s grown in popularity ever since. Spring provides excellent iBATIS integration, and we’ll look at it in this chapter.

Not all problems are well-suited for full-blown persistence frameworks. They’re moderately complicated in the best of circumstances. Without the right skills or with an inappropriate problem, they can be disastrous. In a class that we teach together, Ted Neward, the author of Effective Enterprise Java, often compares building or adopting a persistence framework to the United States war in Vietnam, claiming that they’re both seductive wars to enter, but both wars are difficult to win, and there’s no effective exit strategy in either case. You can still find the blog at http://www.neward.net.

While I won’t go quite that far, it’s nice to have a framework like iBATIS SqlMaps that gives you a taste of an OR usage model without forcing you to eat a whole elephant with one bite. Specifically, iBATIS lets you:

  • Map columns and SQL statements to keywords

  • Use the full power of SQL without the tedium of JDBC

  • Break your SQL away from your code

Spring with iBATIS gives you these advantages and more. Let’s get busy.

How do I do that?

First, you need to install iBATIS. You don’t have to configure it right away, because the configuration will go into your Spring application context. You can find it at http://www.ibatis.com/. Download it and set it up. We’re using Version 1.3.1 for this book. Place the iBATIS-provided jars (ibatis-sqlmap.jar, ibatis-dao.jar, and ibatis-common.jar) as well as the Spring-provided jdom.jar (in Spring’s /lib folder) in the /war/WEB-INF/lib folder of your project directory).

You’ve already got the interface for the façade and the model, so you need an implementation of the façade and the SQL statements. First, you can implement the façade for the application, as in Example 5-1.

Example 5-1. IBatisRentABike.java
public class IBatisRentABike extends SqlMapDaoSupport
   implements RentABike {
    
   private String storeName = "";

   public void setStoreName(String name) {this.storeName = name;}
   public String getStoreName( ) {return this.storeName;}


   public List getBikes( ) {
      return getSqlMapTemplate( ).executeQueryForList("getBikes", null);
   }

   public Bike getBike(String serialNo) {
      return (Bike) getSqlMapTemplate( ).
         executeQueryForObject("getBikeBySerialNo", serialNo);
   }

   public Bike getBike(int bikeId) {
      return (Bike) getSqlMapTemplate( ).
         executeQueryForObject("getBikeByID", new Integer(bikeId));
   }

   public void saveBike(Bike bike) {
      getSqlMapTemplate( ).executeUpdate("saveBike", bike);
   }

   public void deleteBike(Bike bike) {
      getSqlMapTemplate( ).executeUpdate("deleteBike", bike);
   }

   public List getCustomers( ) {
      return getSqlMapTemplate( ).
         executeQueryForList("getCustomers", null);
   }

   public Customer getCustomer(int custId) {
      return (Customer) getSqlMapTemplate( ).
         executeQueryForObject("getCustomer", new Integer(custId));
   }

   public List getReservations( ) {
      return getSqlMapTemplate( ).
         executeQueryForList("getReservations", null);
   }

   public List getReservations(Customer customer) {
      return getSqlMapTemplate( ).
         executeQueryForList("getReservationsForCustomer", customer);
   }

   public List getReservations(Bike bike) {
      return getSqlMapTemplate( ).
         executeQueryForList("getReservationsForBike", bike);
   }

   public List getReservations(Date date) {
      return getSqlMapTemplate( ).
         executeQueryForList("getReservationsForDate", date);
   }
   
   public Reservation getReservation(int resId) {
      return (Reservation) getSqlMapTemplate( ).
         executeQueryForObject("getReservation", new Integer(resId));
   }
}

The SqlMapTemplate is provided by Spring’s SqlMapDaoSupport class which our RentABike implementation must extend. It handles the establishment and management of connections to the underlying data store as well as interpretation of the map files you provide. You can look at the template as a default implementation for the things you’d like to do with an iBATIS named query.

You’ll also need to create the SQL statements. You can give each SQL statement a name. Then, you’ll map the results to a Java bean. You have two choices here. You can either reference every bean property in an SQL AS alias, or build an explicit map between the query and the bean, as in Example 5-2.

Example 5-2. Bike.xml (an IBatis SQL map file)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sql-map
    PUBLIC "-//iBATIS.com//DTD SQL Map 1.0//EN"
    "http://www.ibatis.com/dtd/sql-map.dtd">

<sql-map name="Bike">

  <result-map name="result" class="com.springbook.Bike">
    <property name="bikeId" column="bikeId" columnIndex="1"/>
    <property name="manufacturer" column="manufacturer" columnIndex="2"/>
    <property name="model" column="model" columnIndex="3"/>
    <property name="frame" column="frame" columnIndex="4"/>
    <property name="serialNo" column="serialNo" columnIndex="5"/>
    <property name="weight" column="weight" columnIndex="6"/>
    <property name="status" column="status" columnIndex="7"/>
  </result-map>

  <mapped-statement name="getBikes" result-map="result">
    select bikeId, manufacturer, model, frame, serialNo, weight, status
    from bikes
  </mapped-statement>

  <mapped-statement name="getBikeBySerialNo" result-map="result">
    select bikeId, manufacturer, model, frame, serialNo, weight, status
    from bikes
    where serialNo=#value#
  </mapped-statement>

  <mapped-statement name="getBikeByID" result-map="result">
    select bikeId, manufacturer, model, frame, serialNo, weight, status
    from bikes
    where bikeId=#value#
  </mapped-statement>

  <mapped-statement name="saveBike">
    insert into bikes
      (bikeId, manufacturer, model, frame, serialNo, weight, status)
      values (#bikeId#, #manufacturer#, #model#, #frame#, #serialNo#, 
      #weight#, #status#)
  </mapped-statement>

  <mapped-statement name="deleteBike">
      delete from bikes
      where bikeId = #bikeId#
  </mapped-statement>
</sql-map>

The <result-map> portion provides an explicit map between columns in the database and properties of a persistent class. The <mapped-statements> can then simply define the SQL queries necessary to execute the needed functionality, and the map handles creation of the resultant Java object. In addition to the Bike version above, your application currently also requires a map for Customer and Reservation.

You’re going to have to do a little work that an OR framework would normally do for us, like create identifiers. In this case, you’re going to use MySQL sequences (which are just AUTO_INCREMENT columns on a table). You simply define our table with the bikeId column marked as AUTO_INCREMENT and, when inserting a new row, ignore the bikeId column. The SaveBike statement from our map now becomes Example 5-3.

Example 5-3. Bike.xml
<mapped-statement name="saveBike">
   insert into bikes
   (manufacturer, model, frame, serialNo, weight, status)
   values (#manufacturer#, #model#, #frame#, #serialNo#, #weight#, 
    #status#)
</mapped-statement>

If you are using Oracle, Spring and iBatis support Oracle sequences as well.

Next, you can update the application context. You’ll need to specify our new façade, which requires the SQL maps. Introduce the SQL maps in a properties file, like we do the JDBC parameters. You’ll also need to configure a transaction strategy (Example 5-4).

Example 5-4. RentABikeApp-servlet.xml
<beans>
   <bean id="dataSource" 
      class="org.springframework.jdbc.datasource.DriverManagerDataSource">
      <property name="driverClassName">
         <value>com.mysql.jdbc.Driver</value>
      </property>
      <property name="url">
         <value>jdbc:mysql://localhost/bikestore</value>
      </property>
      <property name="username">
         <value>bikestore</value>
      </property>
   </bean>

   <bean id="rentaBike" class="com.springbook.IBatisRentABike">
      <property name="storeName"><value>Bruce's Bikes</value></property>
      <property name="dataSource"><ref local="dataSource"/></property>
      <property name="sqlMap"><ref local="sqlMap"/></property>
   </bean>

   <bean id="sqlMap" 
      class="org.springframework.orm.ibatis.SqlMapFactoryBean">
      <property name="configLocation">
         <value>/WEB-INF/ibatis.config</value>
      </property>
   </bean>

   <bean id="transactionManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource"><ref local="dataSource"/></property>
   </bean>
Example 5-5. ibatis.config
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sql-map-config
    PUBLIC "-//iBATIS.com//DTD SQL Map Config 1.0//EN"
    "http://www.ibatis.com/dtd/sql-map-config.dtd">
<sql-map-config>
    <sql-map resource="Bike.xml" />
    <sql-map resource="Customer.xml" />
    <sql-map resource="Reservation.xml" />
</sql-map-config>

We’ll talk about the transaction strategy more later.

Note

This transaction strategy is in the context. You won’t have to manage commits, because Spring will do those for you. You’ll see a more complete treatment of transactions in Chapter 6 .

What just happened?

You’re not seeing OR mapping. An OR framework will typically tie a database table to a class, or classes. In this case, iBATIS attaches the results from a query to a class. That means that iBATIS makes no attempt to hide SQL from you at all. In fact, it embraces SQL.

Spring is going to simplify iBATIS usage through templates. The iBATIS template will give you a similar usage model to the JDBC template. You’ll specify a datasource and an iBATIS mapped SQL statement.

When you execute the statement, Spring works with iBATIS to manage the resources for you, creating and closing connections as required. Spring will pass the mapped statement to iBATIS, which executes the mapped statement, and if necessary, puts the result set into the bean that you specified when you mapped the SQL statement. If you’ve got any parameters, you place those in a hash map and pass those to the template with the mapped statement.

While the internals might not look like an OR framework, the usage model definitely has an OR flavor to it. Build data access objects with methods to manipulate the data store. You don’t see SQL in your code; it remains in configuration files. Work with collections of objects instead of result sets. In short, it’s an elegant compromise between full OR and JDBC.

What about...

...using iBATIS for everything? Since this usage model is so similar to an OR model and since many applications need more control over the SQL that they might generate, you may be tempted to use iBATIS pervasively. Still, there’s room for OR frameworks like JDO and Hibernate. OR frameworks give you more flexibility and power:

  • Some advanced object models lead to very complex SQL, which is best delivered through ORM. For example, inheritance often complicates native JDBC.

  • Some advanced performance features, like lazy loading and fetch groups, require a more formal model for effective automation. While perfectly tuned JDBC will always be at least as fast as ORM, the performance tuning options afforded by ORM make it easier to get better performance for some types of problems than native JDBC.

  • ORM makes some problems less tedious. It’s simply easier to manipulate an object than to create a set of SQL queries. Applications with simple finders and create, update, read-by-primary-key, and delete may be easier with ORM.

If you have a rapidly changing data model and object model, iBATIS can come up short. If you’ve got a CRUD style application, then iBATIS may be a little tedious for you. If, instead, you’re looking for good access to SQL and an effective compromise between ORM and native JDBC, then iBATIS can probably give you everything you need.

Using Spring with JDO

JDO is the non-EJB standard for persistence in Java applications. In this section, we’ll use our favorite JDO implementation, Kodo, to implement transparent persistence for our application. In this section, I’m not going to try to teach you JDO, but I will show you how to use it to provide persistence to our application.

If you’ve followed JDO for any length of time, it probably brings to mind all sorts of feverish battles with the intensity of the Crusades. Until recently, most people avoided JDO. With the pending release of JDO 2.0 and several solid commercial and open source JDO implementations, this persistence standard seems to be gathering momentum as a stronger player in the ORM space. In fact, my favorite ORM implementation is Solar Metric’s Kodo, possibly the leading JDO implementation. It seems to be more robust than the alternatives when it comes to commercial implementations. It’s got more flexible mapping support, it’s easier to manage, and it’s got far richer support for hardcore enterprise persistence. Consider these advantages:

  • You can choose between a variety of open source JDO implementations if you’re looking for something that’s free, or better control of the source code.

  • You can choose a commercial implementation, and get support and maintenance for a reasonable price if you don’t.

  • With the top commercial vendors, you get incredible power and performance, from better management to flexible mapping.

  • You get all of this with the protection and security that an open standard provides.

How do I do that?

You’ll use JDO to create a persistent model, and then use that model through a façade layer. The application already has a business domain model created. It’s not yet persistent, though. You’ve also got the interface for the façade layer. You need only do the following steps to enable our application for JDO:

  1. Make the domain model persistent. Do that through the byte code enhancer.

  2. Configure Spring to use Kodo by making a few simple changes in the configuration file.

  3. Build a façade that uses a persistent model through the JDO templates.

That’s it. Spring will manage the core JDO resources, consisting of the PersistenceManagerFactory and the PersistenceManager. Think of these special options as the data source and connection for JDO. You can let Spring manage transactions. The three steps above are all that you need.

First, you need to download and install Kodo. Start with the trial version, which you can find at http://www.solarmetric.com. You can use Version 3.2.1 for this book. Once again, you have to add the libraries found in /kodo-jdo-3.2.1/lib to our /lib folder.

To make the model persistent, modify the Ant task to add JDO’s byte code enhancement step: add an Ant task to do so, like in Example 5-6.

Example 5-6. build.xml
<taskdef name="jdoc" classname="kodo.ant.JDOEnhancerTask"/>

<target name="jdo.enhance">
    <jdoc>
            <fileset dir="${src.dir}">
                   <include name="**/*.jdo" />
            </fileset>
    </jdoc>
</target>

You’ll also add path elements to your Ant build file for kodo-jdo.jar, jdo-1.0.1.jar, and jakarta-commons-lang-1.0.1.jar.

Next, build the persistence mapping. The easiest way is through Kodo’s wizard. Launch the Workbench (in the in directory of your Kodo install) and choose MetaData Create MetaData from the menu. Conversely, you can use the metadatatool and mappingtool scripts that can be found in /kodo-jdo-3.2.1/bin, which are just launchers for kodo.jdbc.meta.MappingTool and kodo.meta.JDOMetaDataTool, respectively.

To keep things consistent with other JDO versions, though, you’re going to build a mapping from scratch, with XML. Generate a .jdo file with our class metadata as well as a .mapping file. Both files reside in the /war/WEB-INF/classes/com/springbook folder.

Example 5-7 shows the metadata file.

Example 5-7. package.jdo
<?xml version="1.0" encoding="UTF-8"?>
<jdo>
   <package name="com.springbook">
      <class name="Bike">
         <extension vendor-name="kodo" key="detachable" value="true"/>
         <field name="reservations">
            <collection element-type="Reservation"/>
            <extension vendor-name="kodo" key="inverse-owner" value="bike"/>
            <extension vendor-name="kodo" key="element-dependent" 
               value="true"/>
         </field>
      </class>
      <class name="Customer">
         <extension vendor-name="kodo" key="detachable" value="true"/>
         <field name="reservations">
            <collection element-type="com.springbook.Reservation"/>
            <extension vendor-name="kodo" key="inverse-owner" 
               value="customer"/>
            <extension vendor-name="kodo" key="element-dependent" 
               value="true"/>
         </field>
      </class>
      <class name="Reservation">
         <extension vendor-name="kodo" key="detachable" value="true"/>
      </class>
   </package>
</jdo>

Example 5-8 shows the mapping file.

Example 5-8. package.mapping
<?xml version="1.0" encoding="UTF-8"?>
<mapping>
   <package name="com.springbook">
      <class name="Bike">
         <jdbc-class-map type="base" pk-column="BIKEID" table="BIKES"/>
         <field name="bikeId">
            <jdbc-field-map type="value" column="BIKEID"/>
         </field>
         <field name="frame">
            <jdbc-field-map type="value" column="FRAME"/>
         </field>
         <field name="manufacturer">
            <jdbc-field-map type="value" column="MANUFACTURER"/>
         </field>
         <field name="model">
            <jdbc-field-map type="value" column="MODEL"/>
         </field>
         <field name="reservations" default-fetch-group="true">
            <jdbc-field-map type="one-many" ref-column.BIKEID="BIKEID" 
               table="RESERVATIONS"/>
         </field>
         <field name="serialNo">
            <jdbc-field-map type="value" column="SERIALNO"/>
         </field>
         <field name="status">
            <jdbc-field-map type="value" column="STATUS"/>
         </field>
         <field name="weight">
            <jdbc-field-map type="value" column="WEIGHT"/>
         </field>
      </class>
      <class name="Customer">
         <jdbc-class-map type="base" pk-column="CUSTID" 
            table="CUSTOMERS"/>
         <field name="custId">
            <jdbc-field-map type="value" column="CUSTID"/>
         </field>
         <field name="firstName">
            <jdbc-field-map type="value" column="FIRSTNAME"/>
         </field>
         <field name="lastName">
            <jdbc-field-map type="value" column="LASTNAME"/>
         </field>
         <field name="reservations" default-fetch-group="true">
            <jdbc-field-map type="one-many" ref-column.CUSTID="CUSTID" 
               table="RESERVATIONS"/>
         </field>
      </class>
      <class name="Reservation">
         <jdbc-class-map type="base" pk-column="RESID" 
            table="RESERVATIONS"/>
         <field name="bike">
            <jdbc-field-map type="one-one" column.BIKEID="BIKEID"/>
         </field>
         <field name="customer">
            <jdbc-field-map type="one-one" column.CUSTID="CUSTID"/>
         </field>
         <field name="reservationDate">
            <jdbc-field-map type="value" column="RESDATE"/>
         </field>
         <field name="reservationId">
            <jdbc-field-map type="value" column="RESID"/>
         </field>
      </class>
   </package>
</mapping>

It’s almost too easy. There’s no persistence in the model itself, and that’s why you use OR technologies. Still, you’ll need a layer of code to use that persistence model for your application. That’s the façade layer. You’ll see a series of calls to the template. Specify the JDO query language statements for the finders, and the objects to persist for deletes, updates, and inserts. You’ve already got an interface, but we still need to implement the façade (Example 5-9).

Example 5-9. KodoRentABike.java
public class KodoRentABike extends JdoDaoSupport implements RentABike {
    private String storeName;

    public List getBikes( ) {
        return (List)getJdoTemplate( ).find(Bike.class);
    }

    public Bike getBike(String serialNo) {
        Collection c = getJdoTemplate( ).find(Bike.class, 
           "serialNo == '" + serialNo + "'");
        Bike b = null;
        if(c.size( ) > 0) {
            b = (Bike)c.iterator( ).next( );
        }
        return b;
    }

    public Bike getBike(int bikeId) {
        return (Bike)getJdoTemplate( ).
           getObjectById(Bike.class, new Long(bikeId));
    }

    public void saveBike(Bike bike) {
        getJdoTemplate( ).makePersistent(bike);
    }

    public void deleteBike(Bike bike) {
        getJdoTemplate( ).deletePersistent(bike);
    }

    //etc.

Finally, you need to set up some configuration to wire it all together. First, Example 5-10 gives the JDO configuration.

Example 5-10. kodo.properties
# To evaluate or purchase a license key, visit http://www.solarmetric.com
kodo.LicenseKey: YOUR_LICENSE_KEY_HERE

javax.jdo.PersistenceManagerFactoryClass: kodo.jdbc.runtime.JDBCPersistenceManagerFactory
javax.jdo.option.ConnectionDriverName: com.mysql.jdbc.Driver
javax.jdo.option.ConnectionUserName: bikestore
javax.jdo.option.ConnectionPassword: 
javax.jdo.option.ConnectionURL: jdbc:mysql://localhost/bikestore
javax.jdo.option.Optimistic: true
javax.jdo.option.RetainValues: true
javax.jdo.option.NontransactionalRead: true
javax.jdo.option.RestoreValues: true

kodo.Log: DefaultLevel=WARN, Runtime=INFO, Tool=INFO
kodo.PersistenceManagerImpl: DetachOnClose=true

The Spring context will need to wire together the JDO persistence manager, the persistence manager factory, the façade, and any services on the façade. That’s done in the context (Example 5-11).

Note

Notice the detachOnClose. This makes sure that JDO loads anything that’s lazy before the connection goes away, so that other parts of your application, like the view, can only access beans that have already been loaded.

Example 5-11. RentABikeApp-servlet.xml
<bean id="jdofactory" class="org.springframework.orm.jdo.
LocalPersistenceManagerFactoryBean">
    <property name="configLocation">
           <value>E:RentABikeAppwarWEB-INFkodo.properties</value>
    </property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jdo.JdoTransactionManager">
    <property name="persistenceManagerFactory">
           <ref local="jdofactory"/>
    </property>
</bean>    
    
<bean id="rentaBike" class="com.springbook.KodoRentABike">
    <property name="storeName"><value>Bruce's Bikes</value></property>
    <property name="persistenceManagerFactory">
           <ref local="jdofactory"/>
    </property>
</bean>

Recall that you’ve already got a test case that uses the façade, so you can build it and let it rip.

What just happened?

This is a perfect example of the power of Spring. You’ve radically changed the implementation of your persistence layer, but you haven’t affected the rest of the application at all. Here’s how it works.

Spring first uses dependency injection to resolve all of the dependencies. Loading the context configures JDO with the data source that you provided, and then sets the persistence manager factory in the façade JDO implementation. Then, when you call a method on the façade, Spring gets you a persistence manager and uses it to process the query that you supply. You can look at it this way: Spring provides a generic JDO façade method, called the template. You plug in the details, and give control to Spring.

When you’re biking or when you’re coding, one of the most important metrics is efficiency. How much work can you do with each turn of the pedals, or each line of code? Consider the JDO version of the application. The most compelling thing about the Spring programming model is the efficiency. To see what I mean, think about what you don’t see here:

  • You don’t have exception management cluttering the lower levels of your application. With Spring’s unchecked exceptions, you can get the exception where it’s appropriate to do something with it.

  • You don’t have resource management. Where JDBC has connections, JDO has the persistence manager. Spring configures the persistence manager factory, and manages the persistence manager within the template for you.

  • You aren’t forced to manage transactions and security in the façade. Spring lets you configure these things easily so that you can strip all of the ugly details out of your façade layer and let it concentrate on using the persistent model.

All of this is done for you in the Spring template, in code that comes with the Spring framework, which you can read for better understanding or debug in a pinch. In short, you can get more leverage with each line of code, much like running your bike in a higher gear. That’s the bottom line for all of the most successful frameworks and programming languages.

Using Hibernate with Spring

Hibernate has long been the persistence framework of choice for Spring developers. Although the Spring team continues to improve the integration with other persistence frameworks, Hibernate remains the most widely used persistence framework with Spring. It’s fitting that these two lightweight solutions helped each other succeed. They go quite well together. In this example, we’ll show how to integrate Spring and Hibernate.

Hibernate is an outstanding persistence framework. It’s popular, it’s relatively fast, and it’s free. It has rich mapping support and a convenient usage model that’s made it popular with developers across the world. Hibernate holds up very well in small- and intermediate-size projects. In fact, though it’s nonstandard, you could say that behind EJB, Hibernate is the most popular persistence framework in the world.

How do I do that?

Now that you’ve configured Spring with an ORM, you know the basic flow, with a persistent domain model and a façade. Since Spring ships with Hibernate dependencies, you can just copy them from Spring’s /dist folder to your /lib folder: hibernate2.jar, aopalliance.jar, cglib-full-2.0.2.jar, dom4j.jar, ehcache-1.1.jar, and odmg.jar.

Since Hibernate uses reflection, there’s no byte code enhancement step. All you’ve got to do to make the model persistent is to create the mappings, and refer to them in the context. Examples Example 5-12, Example 5-13, and Example 5-14 show the mappings.

Example 5-12. Bike.hbm.xml
<hibernate-mapping>
   <class name="com.springbook.Bike" table="bikes">
      <id name="bikeId" column="bikeid" type="java.lang.Integer" 
           unsaved-value="-1">
        <generator class="native"></generator>
      </id>
      <property name="manufacturer" column="manufacturer" type="string"/>
      <property name="model" column="model" type="string"/>
      <property name="frame" column="frame" type="int"/>
      <property name="serialNo" column="serialno" type="string"/>
      <property name="weight" column="weight" type="java.lang.Double"/>
      <property name="status" column="status" type="string"/>
      <set name="reservations">
           <key column="bikeId"/>
           <one-to-many class="com.springbook.Reservation"/>
      </set>
   </class>
</hibernate-mapping>
Example 5-13. Customer.hbm.xml
<hibernate-mapping>
   <class name="com.springbook.Customer" table="customers">
      <id name="custId" column="custid" type="java.lang.Integer" 
           unsaved-value="-1">
        <generator class="native"></generator>
      </id>
      <property name="firstName" column="firstname" type="string"/>
      <property name="lastName" column="lastname" type="string"/>
      <set name="reservations">
           <key column="custId"/>
           <one-to-many class="com.springbook.Reservation"/>
      </set>
   </class>
</hibernate-mapping>
Example 5-14. Reservation.hbm.xml
<hibernate-mapping>
   <class name="com.springbook.Reservation" table="reservations">
      <id name="reservationId" column="resid" type="java.lang.Integer" 
           unsaved-value="-1">
        <generator class="native"></generator>
      </id>
      <property name="reservationDate" column="resdate" type="date"/>
      <many-to-one name="bike" column="bikeId" class="com.springbook.Bike"
                    cascade="none"/>
      <many-to-one name="customer" column="custId" 
           class="com.springbook.Customer"
                    cascade="none"/>
   </class>
</hibernate-mapping>

In the context, you need to configure Hibernate properties, configure the session factory, and plug the session factory into the façade. The transaction strategies with the JDO context, the iBATIS context and the Hibernate context are the same, as they should be. That’s part of what dependency injection is doing for you. Example 5-15 shows the changes to the context.

Example 5-15. RentABikeApp-servlet.xml
<bean name="rentaBike" class="com.springbook.HibRentABike">
    <property name="storeName"><value>Bruce's Bikes</value></property>
    <property name="sessionFactory">
          <ref local="sessionFactory"/>
    </property>
</bean>

<bean id="sessionFactory" 
    class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
    <property name="dataSource"><ref local="dataSource"/></property>
    <property name="mappingResources">
           <list>
                   <value>com/springbook/Bike.hbm.xml</value>
                   <value>com/springbook/Customer.hbm.xml</value>
                   <value>com/springbook/Reservation.hbm.xml</value>
           </list>
    </property>
    <property name="hibernateProperties">
           <props>
                  <prop key="hibernate.dialect">
                  net.sf.hibernate.dialect.MySQLDialect
                  </prop>
                  <prop key="hibernate.show_sql">true</prop>
           </props>
    </property>
</bean>

Since you’ve already got a façade interface, you need only a Hibernate implementation. You can use templates, just as with JDO. For finders, specify a Hibernate query language (HQL) statement. For updates, all that you need to specify is the new object to be stored. Example 5-16 is the remarkably thin façade.

Example 5-16. HibRentABike.java
package com.springbook;

import org.springframework.orm.hibernate.support.HibernateDaoSupport;

import java.util.List;
import java.util.Date;
import java.util.Set;

import net.sf.hibernate.Query;

public class HibRentABike extends HibernateDaoSupport implements RentABike {
    private String name;

    public List getBikes( ) {
        return getHibernateTemplate( ).find("from Bike");
    }

    public Bike getBike(String serialNo) {
        Bike b = null;
        List bikes = getHibernateTemplate( ).
           find("from Bike where serialNo = ?", serialNo);
        if(bikes.size( ) > 0) {
            b = (Bike)bikes.get(0);
        }
        return b;
    }

    public Bike getBike(int bikeId) {
        return (Bike)getHibernateTemplate( ).
           load(Bike.class, new Integer(bikeId));
    }

    public void saveBike(Bike bike) {
        getHibernateTemplate( ).saveOrUpdate(bike);
    }

    public void deleteBike(Bike bike) {
        getHibernateTemplate( ).delete(bike);
    }

    public void setStoreName(String name) {
        this.name = name;
    }

    public String getStoreName( ) {
        return this.name;
    }

    public List getCustomers( ) {
       return getHibernateTemplate( ).find("from Customer");
    }

    public Customer getCustomer(int custId) {
       return (Customer)getHibernateTemplate( ).
           load(Customer.class, new Integer(custId));
    }

    public List getReservations( ) {
        return getHibernateTemplate( ).find("from Reservation");
    }

    public List getReservations(Customer customer) {
        return getHibernateTemplate( ).
           find("from Reservation where custId = ?", customer.getCustId( ));
    }

    public List getReservations(Bike bike) {
        return getHibernateTemplate( ).
        find("from Reservation where bikeId = ?", bike.getBikeId( ));
    }

    public List getReservations(Date date) {
        return getHibernateTemplate( ).
           find("from Reservation where resdate = ?", date);        
    }

    public Reservation getReservation(int resId) {
        return (Reservation)getHibernateTemplate( ).
           load(Reservation.class, new Integer(resId));
    }
}

What just happened?

The Hibernate flow is pretty much the same as the JDO flow. The Spring JDO template represents a set of default DAO methods. All that you need to do is customize them, usually with a HQL query statement, and possibly the values for parameters to any HQL parameterized queries. Then Spring takes control, getting a session, firing the query, and managing any exceptions.

Once again, you see exceptional leverage. Example 5-17 gives the typical Hibernate method that you’d need to write without Spring.

Example 5-17. HibRentABike.java
public List getBikesOldWay( ) throws Exception {
        // Relies on other static code for configuration
        // and generation of SessionFactory.  Might look like:

        // Configuration config = new Configuration( );
        // config.addClass(Bike.class).addClass(Customer.class).
        //        addClass(Reservation.class);
        // SessionFactory mySessionFactory = Configuration.
        //        buildSessionFactory( );

        List bikes = null;
        Session s = null;
        try {
            s = mySessionFactory.openSession( );
            bikes = s.find("from Bike");
        }catch (Exception ex) {
            //handle exception gracefully
        }finally {
            s.close( );
        }
        return bikes;
    }

Example 5-18 shows, again, the Spring counterpart.

Example 5-18. HibRentABike.java
    public List getBikes( ) {
        return getHibernateTemplate( ).find("from Bike");
    }

Strong typing is for weak programmers. Each building block of an application should strive do one thing well, and only once.

What about...

...alternatives to Hibernate? Hibernate is indeed free, fast, effective, and popular. It’s been proven under fire, and has excellent performance and flexibility. Most tools have good Hibernate support, and Hibernate supports every database that matters. I still recommend Hibernate for small and intermediate applications.

So far, as an open source framework, the persistence community can’t be quick enough to praise Hibernate or condemn the alternatives. Such blind worship leads to cultish religious decision making. There are some things that competitors do better. If your application has some of these characteristics, you might be best served to look elsewhere:

Standards

JDO and JDBC solutions conform to a standard. Although it’s open source code, Hibernate does not conform to a standard. You have to trust the motivations of JBoss group and the founders to do the right thing for decades, and for any framework, that’s proven to be a dicey proposition so far.

Management

Other solutions are easier to manage. For example, Kodo JDO and TopLink have management consoles that make it easier to manage caching scenarios, and eager or lazy-loading scenarios.

Mapping

Other frameworks have more powerful and flexible mapping support. If you do not control your schema, you may be best served with another solution. Also, if you prefer to map your schema with GUI tools, then something like JDO Genie or Cayenne may work best for you.

In general, using mainstream frameworks may be the right choice in the end, but often you can find a much better fit with a little digging. Hibernate certainly deserves consideration, but there are other good choices out there, too.

Running a Test Case

The test case is easy to run. You’ve already got it. It’s the one that you ran with the façade.

How do I do that?

Since the test case exists, you can run the existing façade test. You’ll just have to make sure that you set up your test data correctly, and you can use the application context unchanged. That’s the power, and testability, of Spring.

What just happened?

You used the existing test case. That’s nice, because you only need to manage the incremental details for the database. In the next chapter, you’ll start to dig into services that you can add to your application with Spring’s AOP.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.72.176