Wednesday, July 15, 2009

Shopping Cart Web Application - Part 8 - Spring

Introduction
The Spring Framework provides solutions to many technical challenges faced by Java developers and organizations wanting to create applications based on the Java platform. Because of the size and complexity of the functionality offered, it can be hard to distinguish the major building blocks from which the framework is composed.

The Spring Framework can be considered as a collection of smaller frameworks. Most of these frameworks are designed to work independently of each other yet provide better functionalities when used together. These frameworks are divided along the building blocks of typical complex applications:

  • Inversion of Control container - configuration of application components and lifecycle management of Java objects.
  • Aspect-oriented programming framework - working with functionalities which cannot be implemented with Java's object-oriented programming capabilities without making sacrifices.
  • Data access framework - working with relational database management systems on the Java platform using JDBC and Object-relational mapping tools providing solutions to technical challenges which are reusable in a multitude of Java-based environments.
  • Transaction management framework - harmonization of various transaction management API's and configurative transaction management orchestration for Java objects.
  • Model-view-controller framework - HTTP and Servlet based framework providing many hooks for extension and customization.
  • Remote Access framework - configurative RPC-style export and import of Java objects over computer networks supporting RMI, CORBA and HTTP-based protocols including web services (SOAP).
  • Authentication and authorization framework - configurative orchestration of authentication and authorization processes supporting many popular and industry-standard standards, protocols, tools and practices via the Spring Security sub-project (formerly Acegi).
  • Remote Management framework - configurative exposure and management of Java objects for local or remote configuration via JMX.
  • Messaging framework - configurative registration of message listener objects for transparent message consumption from message queues via JMS, improvement of message sending over standard JMS API's.
  • Testing framework - support classes for writing unit tests and integration tests.

What you need before we get started

What we will cover
  • Spring and Hibernate
  • Shopping Cart application Spring configuration file
  • Shopping Cart application Data Access Objects
  • Shopping Cart application Web Spring bean configuration file

Level
  • Beginner

The Spring Framework covers a lot of ground and we are only going to scratch the surface. We will add Spring to our Shopping Cart Application and I will show how Spring can be used to make our lives a lot easier. The main goal for this tutorial is to show you how to integrate Spring with Hibernate.

Spring and Hibernate
As learnt in the previous Hibernate tutorial Hibernate is a powerful ORM tool that lies between the Application and the Database. It enables applications to access data from any database in a platform-independent manner. There is no need for the application to depend on the low-level JDBC details like managing connection, dealing with statements and result sets. All the necessary details for accessing a particular data source is easily configurable in xml files.

One of the problems with using Hibernate is that the client application that accesses the database using Hibernate framework has to depend on the Hibernate APIs like Configuration, SessionFactory and Session. These objects will continue to get scattered across the code throughout the application. Moreover, the application code has to manually maintain and manage these objects. In the case of Spring, the business objects can be highly configurable with the help of IOC Container. It is possible to use the Hibernate objects as Spring Beans and they can enjoy all the facilities that Spring provides.

Shopping Cart application Spring configuration file
In Spring, all the business objects are configured in xml files and the configured business objects are called Spring Beans. These Spring Beans are maintained by the IOC which is given to the client application upon request.

1. Hibernate Session Example
In the Hibernate tutorial we created a hibernate.cfg.xml file. In this tutorial we are going to replace this file with a Spring bean configuration file. The Hibernate configuration will be configured in the Spring configuration class as opposed to the hibernate configuration file. The details of the Spring bean configuration file are pasted below:

shoppingcart-database.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
  <bean id="shoppingCartDataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
    <property name="url" value="jdbc:hsqldb:shoppingcart" />
    <property name="username" value="sa" />
    <property name="password" value="" />
  </bean>
  <bean id="shoppingCartSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="shoppingCartDataSource" />
    <property name="hibernateProperties">
      <props>
        <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
        <prop key="show_sql">true</prop>
        <prop key="format_sql">true</prop>
        <prop key="current_session_context_class">thread</prop>
      </props>
    </property>
    <property name="annotatedClasses">
      <list>
        <value>com.mydomain.shoppingcart.bo.Item</value>
        <value>com.mydomain.shoppingcart.bo.Basket</value>
        <value>com.mydomain.shoppingcart.bo.BasketItem</value>
      </list>
    </property>
  </bean>
  <bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory">
      <ref local="shoppingCartSessionFactory" />
    </property>
  </bean>
  <bean id="basketDao" class="com.mydomain.shoppingcart.dao.impl.BasketDaoImpl">
    <property name="sessionFactory">
      <ref local="shoppingCartSessionFactory" />
    </property>
  </bean>
  <bean id="itemDao" class="com.mydomain.shoppingcart.dao.impl.ItemDaoImpl">
    <property name="sessionFactory">
      <ref local="shoppingCartSessionFactory" />
    </property>
  </bean>
</beans>

1.1. shoppingCartDataSource
The shoppingCartDataSource bean defines a data-source of type 'org.apache.commons.dbcp.BasicDataSource'. More importantly, it defines the various connection properties that are needed for accessing the database. Since we are using HSQL as our database I have specified the hsqldb driver.

1.2. shoppingCartSessionFactory
The shoppingCartSessionFactory bean is responsible for creating Session objects through which Transaction and Data accessing is done. It replaces the session factory configuration we made in the Hibernate tutorial’s hibernate.cfg.xml file. We can also delete the HibernateUtil class we created in the Hibernate tutorial to get a handle on the session factory as Spring is going to do all of that for us.

1.3. transactionManager
The transactionManager bean binds a Hibernate Session from the factory to a thread to support transactions.

1.4. basketDao and itemDao
These last two beans are references to our very own data access objects. I have defined them here to tell Spring to manage the creation of these objects for me and to initialise them with the sessionFactory class.

2. JPA EntityManager Example
In the Hibernate tutorial for the JPA example we created a persistence.xml file. This file stays exactly the same. In this tutorial we are going to add a Spring bean configuration file and tell it that we are using JPA Entity Manager. The details of the Spring bean configuration file are pasted below:

shoppingcart-database.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
      http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
      http://www.springframework.org/schema/tx 
      http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
 
 <bean id="entityManagerFactory"
  class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
  <property name="persistenceUnitName" value="shopping-cart" />
 </bean>
 
 <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  <property name="entityManagerFactory">
   <ref local="entityManagerFactory" />
  </property>
 </bean>
 <bean
  class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
 
 <bean id="basketDao" class="com.mydomain.shoppingcart.dao.impl.BasketDaoImpl">
  <property name="entityManagerFactory" ref="entityManagerFactory" />
 </bean>
 
 <bean id="itemDao" class="com.mydomain.shoppingcart.dao.impl.ItemDaoImpl">
  <property name="entityManagerFactory" ref="entityManagerFactory" />
 </bean>
 
 <tx:annotation-driven />
</beans>

2.1. entityManagerFactory
The entityManagerFactory is a FactoryBean that creates a JPA EntityManagerFactory according to JPA's standard standalone bootstrap contract.

2.2. transactionManager
The transactionManager bean is a PlatformTransactionManager implementation for a single JPA EntityManagerFactory used to support transactions.


2.3. basketDao and itemDao
Like in the Hibernate example I have defined the DAO classes. They both extend the Spring JJpaDaoSupport helper class.

Shopping Cart application Data Access Objects

1. Hibernate Session Example
As you know we have two DAO implementations, BasketDaoImpl and ItemDaoImpl. We configured these classes already in our Spring bean configuration file so we know that Spring is going to manage the creation and initialisation of these classes for us. I have also made sure these classes both extend Springs HibernateDaoSupport class. The HibernateDaoSupport class is a convenient super class for Hibernate-based data access objects. It requires a SessionFactory to be set, providing a HibernateTemplate based on it to subclasses through the getHibernateTemplate() method.

BasketDaoImpl.java
@Transactional
public class BasketDaoImpl extends HibernateDaoSupport implements BasketDao {

    public void delete(final Basket basket) throws Exception {
        HibernateCallback callback = new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                Object basketObj = session.load(Basket.class, basket.getId());
                session.delete(basketObj);
                return null;
            }
        };
        getHibernateTemplate().execute(callback);
    }

    public void saveOrUpdateBasket(final Basket basket) throws Exception {
        HibernateCallback callback = new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                session.saveOrUpdate(basket);
                return null;
            }
        };
        getHibernateTemplate().execute(callback);
    }

    public Basket removeItemFromBasket(final BasketItem basketItem) throws Exception {
        HibernateCallback callback = new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException, SQLException {

                Basket basket = (Basket) session.load(Basket.class, basketItem.getBasket().getId());
                for (Iterator<BasketItem> it = basket.getBasketItems().iterator(); it.hasNext();) {
                    BasketItem existingBasketItem = (BasketItem) it.next();
                    if (existingBasketItem.getId().equals(basketItem.getId())) {
                        if (existingBasketItem.getQuantity() > 1) {
                            existingBasketItem.decreaseQuantity();
                        } else {
                            it.remove();
                        }
                    }
                }
                session.saveOrUpdate(basket);
                return basket;
            }
        };
        return (Basket) getHibernateTemplate().execute(callback);
    }
}

I will step through the saveOrUpdateBasket method only as the others are very similar. The first thing you will notice is the instantiation of an anonymous class from a Spring interface called HibernateCallback.

There is one method inside this class called doInHibernate. This method will be called by the HibernateTemplate’s execute method. An active Hibernate session is passed to this method. As you can see we don’t care about activating or closing the Session, or handling transactions. This is all taken care of by Spring.

2. JPA Entity Manager Example
The BasketDaoImpl class in the JPA sample application extends Springs helper class (JpaDaoSupport):

BasketDaoImpl.java
@Transactional
public class BasketDaoImpl extends JpaDaoSupport implements BasketDao {

    public void delete(Basket basket) throws Exception {
        basket = getJpaTemplate().find(Basket.class, basket.getId());
        getJpaTemplate().remove(basket);
    }

    public Basket update(Basket basket) throws Exception {
        return getJpaTemplate().merge(basket);
    }
    
    public Basket removeItemFromBasket(BasketItem basketItem) throws Exception {
        Basket basket = getJpaTemplate().find(Basket.class, basketItem.getBasket().getId());
        for (Iterator<BasketItem> it = basket.getBasketItems().iterator(); it.hasNext(); ) {
            BasketItem existingBasketItem = (BasketItem) it.next();
            if (existingBasketItem.getId().equals(basketItem.getId())) {
                if (existingBasketItem.getQuantity() > 1) {
                    existingBasketItem.decreaseQuantity();
                } else {
                    it.remove();
                }
            }
        };
        return getJpaTemplate().merge(basket);
    }
}

Shopping Cart application Web Spring bean configuration file
I created a Spring bean configuration file for the web project as well. The main reason for this was so that I would let Spring manage the creation and retrieval of our ShoppingManager service. I created a file called applicationContext.xml and saved it in the WebContent/WEB-INF directory.

applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
      http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
 <bean id="shoppingService" class="com.mydomain.shoppingcart.service.impl.ShoppingManager">
  <property name="itemDao">
   <ref bean="itemDao" />
  </property>
  <property name="basketDao">
   <ref bean="basketDao" />
  </property>
 </bean>
 <import resource="classpath:shoppingcart-database.xml" />
</beans>

9 comments:

Anonymous said...

Hi Ross!
Great tutorial! I don't know if you are still "supporting" this one but I get an exception when starting the JPA-one:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'shoppingService' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Cannot resolve reference to bean 'itemDao' while setting bean property 'itemDao'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'itemDao' is defined

Any help is appreciated!

Unknown said...

Thank you and yes I am still supporting it and appreciate all the feedback.

It looks as if you may have the different versions of the application mixed up. Please check the following for me:

ShoppingCartSpringPart8 - should have 2 eclipse projects (shopping-cart-core-spring-part8 and shopping-cart-web-spring-part8).

ShoppingCartSpringJpaPart8 - should have 2 eclipse projects (shopping-cart-core-spring-jpa-part8 and shopping-cart-web-spring-jpa-part8).


It looks to me based on the exception you are getting that you are using the shopping-cart-web-spring-part8 with the shopping-cart-web-spring-part8. The reason I say that is because the shopping-cart-web-spring-part8 project should have the spring bean definitions you mentioned but the shopping-cart-web-spring-jpa-part8 shouldn't

Anonymous said...

Ok. I've imported the shopping-cart-core-spring-jpa-part8 and shopping-cart-web-spring-jpa-part8 into one Eclipse-project. The result was the exception I mentioned above.

Now, I've additionally copied the missing DaoClasses from shopping-cart-core-spring-part8 into this Eclipse-project and it works. But I'm not sure if this is the way it meant to be...

Greetings

Unknown said...

That's very strange.

You mustn't copy DAO's over.

Do you have all 4 projects in your workspace?

Please do this for me: Right click on your shopping-cart-web-spring-jpa-part8 project and go to Java EE Module Dependencies. Make sure shopping-cart-web-spring-jpa-part8 is checked and NOT shopping-cart-web-spring-part8.

Your applicationContext.xml file in the shopping-cart-web-spring-jpa-part8 project should have the shoppingService bean definition looking like:

<bean id="shoppingService" class="com.mydomain.shoppingcart.service.impl.ShoppingManager" />

Anonymous said...

Hi Ross,

applicationContext.xml has the bean definition inside and there are no Java EE Module Dependencies.

Just to be sure, how should I import the project into Eclipse?

What I did:

- created new dyanmaic web project
- imported shopping-cart-core-spring-jpa-part8
-imported shopping-cart-web-spring-jpa-part8 into the same project

Unknown said...

Hi,

You shouldn't create a dynamic project. Within your Eclipse workspace you should do the following:

1. Click on File -> Import -> General -> existing projects into workspace
2. Select the 'Select archive file' radio button
3. Click browse button and navigate to the ShoppingCartSpringJpaPart8.zip
4. Click the Finish button

The project expects a project called LIBS to be in your workspace with all the libraries. If not already created you can do so by:
1. File -> Project -> General -> Project
2. Project name: LIBS
3. Finish

Since this is the 8th part to the tutorial I suggest stepping through each one (starting with JUnit Part 3.

The reason I did it like that was because of the different versions of the application I had I didn't want to duplicate libraries all over the place and if I included all the libraries in one download it would exceed the size limit set on my box.net account.

After all the libraries have been copied to the LIBS project you will probably need to configure your web project's build path as I have it pointing to Tomcat server. Therefore once you have configured your Tomcat server in Eclipse right click on the shopping-cart-web-spring-jpa-part8 project -> Libraries -> You will notice Server Library [Apache Tomcat] Highlight that and click the edit button --> if you configured Eclipse properly it should have your Tomcat configuration in a list to choose from.

If there is anything else please send me your email and I will do my best to help you.

Anonymous said...

Thank you very much for your patience!

My email:
boxchampion(at)gmx.de

Cheers!

Kamal said...

Hi,

I did this :

Please choose one archive and import both projects into your Eclipse environment:ShoppingCartSpringPart8 - Hibernate session sample application

It's very confusing, two project imported in eclipse and no one has WEB-INF or a library.

Please can you help us.
thanks

rmahony said...

hi there, did you download the ShoppingCartSpringPart8.zip file? If you extract it you can see 2 folders, in the shopping-cart-web-spring-part8 folder the WEB-INF folder exists in the WebContent folder. 

As for the libraries I didn't include them in the attachment as it would have made the download to big. Instead I configured the project to look for the libraries in a separate project called LIBS and then provide each library as a separate download. You can get all the libraries I used by stepping through each part of the tutorial starting with test cases - part 3