Spring Framework DB2

[article] [edit page] [discussion] [history]

From Humanitarian-FOSS Project Development Site

Contents

Tutorial: Spring Framework DB2

Setup

To continue these tutorials, we need to do three things: make a bit of a change to the petstore database, download some new libraries, and install two new projects into Eclipse.

  1. Change the petstore database to enable foreign key constraints. We have designed our petstore so that the inventory table has a supplier column, which is intended to be the ID of one of the suppliers in the suppliers table. However, right now the database does not enforce this---you can enter any number you like for this field in inventory. We will rectify this situation by using foreign key constraints. Specifically, we will set up the database so that for any row in inventory, the supplier column must be equal to one of the id field of one of the rows of the suppliers table (in other words, the values of the supplier column are constrained by the "foreign key" that is the id column of the suppliers table).
    1. Log into the database server and select your petstore database (remember, it will be named sahana2_username_petstore.
    2. Click on the "Structure" icon of the inventory table (the second icon from the left).
    3. Select the "Operations" tab. In the "Table options" box, set the Storage Engine to "InnoDB" and click on "Go."
    4. Repeat for the suppliers table.
    5. Go back to the "Operations" tab for inventory and click on the "Structure" icon. In the row for "Supplier" click on the "Index" icon (should be the right-most enabled icon).
    6. Click on "Relation view." On the drop-down menu for "Supplier" select "suppliers->ID" and click on "Save."
  2. Get the new libraries file, unpack it, and add the JAR files to the libs directory that you created in Part 1 of this tutorial.
  3. Get the Media:db-tut2.zip file which contains two new projects: tutorial-db-spring-cleaning and tutorial-hibernate-spring. Load these two projects into your workspace as you did in Part 1 of the tutorial. For tutorial-db-spring-cleaning, link against mysql-connector-java-5.1.6-bin.jar, spring.jar, and commons-logging.jar (just as for tutorial-jdbc-spring). For tutorial-hibernate-spring, link against all of the JAR files that are now in your libs directory.

Spring cleaning

Our first task is to clean up our JDBC-based Spring-framework application from Part 1. The key thing to do is to step back a bit an ask ourselves how beans are really intended to be used. An object should typically be created as a bean if it is intended to be a singleton object that is needed by many disparate classes in an application.

Without something like the Spring framework, such an object (let's call it b) would have to be created by some object (let's call it m) that is responsible (maybe indirectly) for creating all of those disparate objects that depend on b. In other words, the flow of control looks something like:

  • m creates b.
  • m creates many other objects, giving each a reference to b.
  • Each of those objects creates more objects, continuing to pass along this reference to b until finally all objects that depend on b have been created.

Notice that some of these objects many not even depend on b themselves; but they still need to know about it so they can pass it along this chain.

We can make this a little bit cleaner by implementing the Singleton design pattern ourselves, as we've seen. But even here the tt gets to be a little annoying, as every class that depends on b has to invoke the same tt to get it when an object is instantiated.

In effect, Spring's dependency injection is automatically writing and executing this Singleton-related tt for us. It is important to note, though, that Spring will only inject beans into other beans.

It can be helpful to see where a bean is not appropriate. Referring to our tutorial, an Inventory is a likely candidate for a bean. It is intended to be a singleton, and it might be used by several disparate classes (in tutorial-spring-cleaning it is used by several independent classes that provide an MVC-based application for examining the database). An InventoryItem, on the other hand, is not a good candidate for a bean. InventoryItems are not Singletons; we need a distinct object for each item in the inventory. Instantiation and access to InventoryItems is always mediated through the Inventory object. Although many different disparate objects may depend on one or another InventoryItems, they are always obtained from an Inventory. Thus an InventoryItem might as well be an ordinary Java object that is not managed by Spring. Ditto for Suppliers vs. Supplier. So now our Inventory-related bean definitions look like the following (tutorial-spring-cleaning/metadata/spring/petstore.xml):

<bean id="inventoryDAO" class="petstore.Inventory" >
  <constructor-arg ref="dataSource" />
  <constructor-arg ref="supplierDAO" />
</bean>

<bean id="inventoryView" class="petstore.InventoryView">
  <constructor-arg ref="inventoryDAO" />
</bean>

<bean id="inventoryAdd" class="petstore.InventoryAdd">
  <constructor-arg ref="inventoryDAO" />
  <constructor-arg ref="inventoryAddValidator" />
</bean>

<bean id="inventoryAddValidator" class="petstore.InventoryAddValidator">
  <constructor-arg ref="inventoryDAO" />
  <constructor-arg ref="supplierDAO" />
</bean>

InventoryView and InventoryAdd are two MVC-related classes that have no dependencies on each other in any way. However, each depends on the Inventory object: the former is a view of the object, and the latter allows adding new items. We are also making use of a validator object, which is used to ensure that the data comprising a potential new inventory item is legal. It depends on both the Inventory and Suppliers but not the InventoryView or InventoryAdd.

Something to note about our re-design is the following: except for the main method of PetStore, no class ``knows that it is or uses Spring-mananged beans. The main method looks as follows:

public static void main(String[] args) {
    ApplicationContext ctx =
        new ClassPathXmlApplicationContext("petstore.xml") ;

    ctx.getBean("mainApp") ;
}

mainApp is a bean that is created by instantiating a PetStore object. No other class invokes getBean or needs an ApplicationContext. Indeed, a search through the project shows that the only other Spring-related types are those involved in managing JDBC. In fact, this is how a Spring-managed application is supposed to look; classes are Plain Old Java Objects (POJOs) that know nothing of the framework that is managing them. This (at least in theory) allows for changes in the framework without significant disruption of the business/application logic, which is what the compiled tt should be focusing one.

Object/relational mapping and Hibernate

A not-so-pretty part of even our cleaned-up Spring application is that the DAOs potentially have a lot of SQL hard-ttd into them. In general, this is difficult to avoid with JDBC, as the DAOs must communicate directly with the database.

An object/relational mapper is something that provides an automatic mapping between persistent relational database data (for example, rows in tables) and in-memory objects in an application. In the simple case of mapping rows to objects, one typically specifies a schema that identifies what kind of object corresponds to a row in a given table and how to translate from column names to fields. One can then allow the O/R mapper to manage the corresponding objects, and then it is responsible for initializing the objects, ensuring that updates to the objects are reflected in the database, etc.

The O/R mapper that we will use is called Hibernate. Although we could use Hibernate directly, Spring provides a Hibernate module that handles some of the grungy details for us, so we will use that module. Hibernate actions are grouped together in a session, so first we configure Spring to make a SessionFactory, which we can use to get a session as needed:

<bean id="dataSource"
       class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost/petstore"/>
    <property name="username" value="pstore"/>
    <property name="password" value="pstore_pw"/>
</bean>

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="dataSource" ref="dataSource"/>
  <property name="mappingResources">
    <list>
    <value>petstore.hbm.xml</value>
    </list>
  </property>
  <property name="hibernateProperties"
    value="hibernate.dialect=org.hibernate.dialect.HSQLDialect" />
</bean>

Notice that we still need a DataSource to handle the underlying protocol for communicating with the database. We then create a SessionFactory on top of that DataSource. The mappingResource property specifies the name of a file that defines the mapping between the database and in-memory objects. The hibernateDialect property allows us to use an extension of SQL called HQL to access the database, which will make certain operations simpler. Now let's look at the mapping file (tutorial-hibernate-spring/metadata/hibernate/petstore.hbm.xml):

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

  <class name="petstore.Supplier" table="suppliers" lazy="false">
    <id name="id" column="ID">
      <generator class="native" />
    </id>
    <property name="name" column="Name" />
    <property name="street" />
    <property name="city" />
    <property name="state" />
    <property name="zip" />
  </class>

</hibernate-mapping>

Ignore the lazy attribute for now. This mapping says that Supplier objects are to be mapped from the database as rows from the suppliers table. The name, street, etc. fields are all to be set from the same-named columns in the table (if no column attribute is specified, it defaults to the value of the name attribute). In order for Hibernate to manage the objects, they must have an id field; this mapping tells Hibernate to use its own strategy for determining the value of that field (which in this case will be to set it according to the ID column of the table). Now let's look at the Supplier class definition:

package petstore;

public class Supplier {

    private int id ;
    private String name ;
    private String street ;
    private String city ;
    private String state ;
    private String zip ;

    public Supplier() {
    }

    public int getId() {
        return id ;
    }

    private void setId(int id) {
        this.id = id ;
    }

    // The rest of the getters and setters.
}

Hibernate requires getter and setter methods for each field that is to be mapped from the database. To initialize an object, Hibernate will create the object with the default constructor and then invoke the appropriate set methods. Note that these methods need not be public---in particular, it actually makes sense to make setId private so that only Hibernate can set the object ID. There is nothing more to this class. So how do we get Supplier objects into our application? One way is to explicitly request that Hibernate construct them, as in our Suppliers bean:

public Suppliers(SessionFactory sessionFactory) {

    // The HibernateTemplate (Spring) handles getting the current
    // Hibernate Session.
	HibernateTemplate template =
		new HibernateTemplate(sessionFactory) ;
	suppliers = template.find("from Supplier") ;

    observers = new ArrayList<SuppliersObserver>() ;
    notifyObservers() ;

}

HibernateTemplate is a Spring-provided convenience class that lets us perform database operations without dealing explicitly with setting up Hibernate sessions. The find method takes an HQL query as an argument and returns a java.util.List of objects constructed by executing the query on the database and then mapping the rows of the resulting table to objects according to the mapping file. In this example, we use a very simple query that says to get every row of the table use to define Supplier objects. However, this is not the only way to get a Supplier object. Consider the mapping for InventoryItem:

<class name="petstore.InventoryItem" table="inventory">
  <id name="id" column="ID">
    <generator class="native" />
  </id>
  <property name="description" column="Description" />
  <property name="quantity" />
  <property name="price" />
  <many-to-one name="supplier" class="petstore.Supplier" column="supplier" />
</class>

The last line of the mapping says that the supplier field is defined by the supplier column; no surprise there. However, in the database, that field value is an int, and we want the corresponding Supplier object. Here we are making use of the foreign key constraints that we implemented at the beginning of this tutorial. The existence of that constraint in the database is enough for Hibernate to know that the integer field value corresponds to the value of the id column of one of the rows in the suppliers table, and that the supplier field of this InventoryItem should be set by constructing the corresponding Supplier object from that row of the suppliers table. We are also saying that there is a many-to-one relation between InventoryItems and Suppliers; that is, many of the former may reference one of the latter.

It would be great if, once we have created an object via Hibernate that corresponds to a row in a database table, any changes to that object are automatically propagated to the database (e.g., changing the quantity of an InventoryItem automatically changing the corresponding field value of the corresponding row in the inventory table. However, things are not quite this straightforward. A bit of terminology might be helpful at this point. A persistent object is one that is associated to the database in some way, and is usually managed by Hibernate during some portion of its lifetime. It is usually a POJO (with a default constructor and getters/setters for Hibernate). A persistent object can be in one of three states: transient (has never been associated with the database---remember that a persistent object is just an object); persistent (currently associated with a session); and detached (was associated with a session, but is no longer, probably because the session has been closed). A session corresponds to a (typically short-lived) conversation between the application and the collection of persistent objects. A session indirectly is a conversation with the database, because Hibernate ensures that during a session, persistent identity (the row of the database that is mapped to an object) is the same as Java identity (in-memory location of the object).

So let's consider a concrete example. Suppose we want to change the quantity of a particular InventoryItem and ensure that the change is reflected in the database. The way we currently get our inventory is that we instantiate an Inventory object (bean), which in turn creates a List of InventoryItems by executing template.find("from InventoryItem"). This (Spring) method is just a cover of a Hibernate method; one of the convenient things that Spring does for us is to manage the opening and closing of Hibernate sessions. In particular, this method invocation causes a Hibernate session to be opened, the database to be queried and the InventoryItems to be created, and then the session to be closed. But because of this last step, the persistent InventoryItem objects are now detached. Trying to invoke setQuantity on them directly does change the in-memory object, but because the object is detached, no corresponding change to the database is made.

There are (at least) two ways to ensure that changes to the in-memory object are transferred to the database. One way is to make changes to the object during a session; here is how that would be implemented as an updateItemQuantity method in Inventory:

public void updateItemQuantity(final int itemID, final int quantity) {
    template.execute(new HibernateCallback() {
        public Object doInHibernate(Session session) {
            InventoryItem item =
                (InventoryItem)session.load(InventoryItem.class, itemID) ;
            item.setQuantity(quantity) ;
            return null ;

        }
    }) ;

    notifyObservers() ;
}

The execute method expects an implementation of HibernateCallback, which specificies the doInHibernate method. That method is provided a Session, and all tt in that method is executed in the context of that session. Thus we can load in the corresponding InventoryItem and then change the quantity with the appropriate setter method; Hibernate will "intercept" this instruction and ensure that the change is also propagated to the database. Note that it is not adequate to get the InventoryItem from items directly, as those objects are detached; we must ask Hibernate to give us the object. Also note that we have obtained a new persistent object; it is not the same one as the object in items with the same ID. This must be handled by re-loading the list from the database whenever is considered appropriate.

An alternative is to change the in-memory object and then instruct Hibernate to identify the persistent object and make the changes:

public void updateItemQuantity(final int itemID, final int quantity) {
    for (InventoryItem item : items) {
        if (item.getId() == itemID) {
            item.setQuantity(quantity) ;
            template.update(item) ;
        }
    }

    notifyObservers() ;
}

Here the update method re-attaches the persistent object and propagates the change back to the database.

Personal tools