Skip to content

Developing Spring MVC application – Annotation based Controller and JPA

June 20, 2009

This application is based on the sample Spring MVC application described in the “Developing a Spring Framework MVC application step-by-step” document in the Spring Framework 2.5 download. The differences are

  • Annotation based Controller class
  • JPA based persistence using Hibernate
  • Maven based project configuration
  • Test classes are upgraded to use JUnit 4 annotation



I. Annotation based Controller class

Instead of either implementing the Controller interface or extending from SimpleFormController class. Here provided are annotation-based Controller class implementations.




InventoryController

package springapp.web; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import springapp.service.ProductManager; @Controller @RequestMapping("/*") public class InventoryController { private final Logger logger = Logger.getLogger(getClass()); @Autowired private ProductManager productManager; @RequestMapping public ModelAndView inventoryHandler() { if (logger.isDebugEnabled()) { logger.debug("inventoryHandler called"); } String now = (new Date()).toString();         Map<String, Object> myModel = new HashMap<String, Object>(); myModel.put("now", now); myModel.put("products", this.productManager.getProducts()); return new ModelAndView("hello", "model", myModel); } public void setProductManager(ProductManager productManager) { this.productManager = productManager; } }



PriceIncreaseFormController

package springapp.web; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.validation.BindingResult; import org.springframework.validation.Validator; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.support.SessionStatus; import springapp.service.PriceIncrease; import springapp.service.ProductManager; @Controller @RequestMapping("/priceincrease.htm") public class PriceIncreaseFormController { private final Logger logger = Logger.getLogger(getClass()); @Autowired     private ProductManager productManager; @Autowired private Validator priceValidator; @RequestMapping(method=RequestMethod.POST) public String processSubmit( @ModelAttribute("priceIncrease") PriceIncrease priceIncrease, BindingResult result, SessionStatus status) { priceValidator.validate(priceIncrease, result); if (result.hasErrors()) { return "priceincrease"; } else { int increase = priceIncrease.getPercentage(); logger.info("Increasing prices by " + increase + "%."); productManager.increasePrice(increase);             status.setComplete(); return "redirect:hello.htm"; } } @RequestMapping(method = RequestMethod.GET) public String setupForm( @RequestParam(required = false, value = "20") Integer percentage, ModelMap model) { PriceIncrease priceIncrease = new PriceIncrease(); priceIncrease.setPercentage(10); model.addAttribute("priceIncrease", priceIncrease); return "priceincrease"; } public void setProductManager(ProductManager productManager) { this.productManager = productManager; } public ProductManager getProductManager() { return productManager; } }



XML Context file change

In order to enable annotation based configuration, we will have to enable component scanning and register a DefaultAnnotationHandlerMapping bean and a AnnotationMethodHandlerAdapter bean.

<context:component-scan base-package="springapp" /> <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>



II. JPA based persistence using Hibernate

Differences from the original application in choosing and implementaing persistence layer are

  • Domain class Product is annotated with JPA annotation
  • DAO class is annotated with JPA annotation and Spring transaction annotation
  • Data Source is using C3P0
  • Hibernate is used as the JPA provider



Annotated domain class

Domain class Product is annotated so that Hibernate can take care of persistence work.

package springapp.domain; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name="products") public class Product implements Serializable { private Long id; private String description; private Double price; @Id @GeneratedValue public Long getId() { return id; } public void setId(Long id) { this.id = id; }     public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append("Description: " + description + ";"); buffer.append("Price: " + price); return buffer.toString(); } }



Annotated DAO class

Instead of extending from SimpleJdbcDaoSupport class and using JdbcTemplate to access the database, here we annotated the DAO class to inject a EntityManager to take care of database access for us. Transaction is provided by using Spring transaction annotation instead of using AOP to define advice and aspect of where to apply transaction.

package springapp.repository; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import springapp.domain.Product; @Repository("productDao") public class JdbcProductDao implements ProductDao { @PersistenceContext         private EntityManager entityManager; @Override @Transactional(readOnly=true) public List<Product> getProductList() { Query query = this.entityManager.createQuery("from Product"); return query.getResultList(); } @Override @Transactional public void saveProduct(Product product) { entityManager.merge(product);         } public void setEntityManager(EntityManager entityManager) { entityManager = entityManager; } }



To enable declarative transaction management, we need to use the annotation-driven element. To automatically inject Entity Manager into instance whose property is annotated with @PersistentCotext, we need to use the annotation-config element. Finally, we register a Transaction Manager and a Entity Manager Factory in our bean definition file.

<tx:annotation-driven /> <context:annotation-config /> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitName" value="product" /> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean> <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" /> <property name="showSql" value="true" /> <property name="generateDdl" value="true" /> </bean> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean>



DataSource

Instead of using Apache DBCP, here we use C3P0 as our implementation choice.

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="user" value="${hibernate.connection.username}" /> <property name="password" value="${hibernate.connection.password}" /> <property name="driverClass" value="${hibernate.connection.driver_class}" /> <property name="jdbcUrl" value="${hibernate.connection.url}" /> <property name="initialPoolSize" value="1" /> <property name="maxPoolSize" value="2" /> <property name="minPoolSize" value="1" /> <property name="acquireIncrement" value="1" /> <property name="acquireRetryAttempts" value="0" /> </bean>



III. Download

To download the Eclipse project for this application, please click here.

Before deploying the application, we need to create database table and insert few rows into it first. Please run the table.sql script first. This script is created by assuming MySQL is the database. You will also need to change the hibernate.properties file to provide your own settings such as username, password, database, etc.

Entry point to the application is http://127.0.0.1:8080/springapp/

Product page

spring-mvc-hello




Increase Price page

spring-mvc-increase-price




About these ads
9 Comments leave one →
  1. Michal Cevela permalink
    September 8, 2009 11:48 am

    Great job, nice tutorial! Just a question: Have you tried this stuff on OSGi platform in conjuction with Spring DM? I can recommend it… that makes this stuff even more elegant .-)

  2. cchweblog permalink*
    September 9, 2009 6:58 pm

    Nope, I have not tried to use OSGi nor Spring DM before.

  3. kruthika permalink
    October 30, 2009 7:36 am

    Hi, Nice tutorial! Could u pls tell with how Unit Test class looks for testing Controller??

  4. khadeer permalink
    November 23, 2009 2:16 pm

    Nice Tutorial

    can you please elaborate this example with spring security

  5. February 15, 2010 9:43 am

    How to execute maven project

  6. rajasekhar permalink
    November 23, 2010 10:52 am

    Hi,

    really its a very nice tutorial..but its throwing the following error in tomcat..can you please check it whats going wrong there.

    [ERROR] 16:47:12 org.springframework.web.context.ContextLoader – Context initialization failed
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor#0′ defined in ServletContext resource [/WEB-INF/springapp-data.xml]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘entityManagerFactory’ defined in ServletContext resource [/WEB-INF/springapp-data.xml]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: org.hibernate.HibernateException: The chosen transaction strategy requires access to the JTA TransactionManager
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:480)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:221)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:881)
    at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:597)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:366)
    at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:255)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:199)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:45)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4135)
    at org.apache.catalina.core.StandardContext.start(StandardContext.java:4630)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:791)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:771)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:546)
    at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:905)
    at org.apache.catalina.startup.HostConfig.deployWARs(HostConfig.java:740)
    at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:500)
    at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1277)
    at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:321)
    at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:119)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053)
    at org.apache.catalina.core.StandardHost.start(StandardHost.java:785)
    at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
    at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:445)
    at org.apache.catalina.core.StandardService.start(StandardService.java:519)
    at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:581)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.intellij.uiDesigner.snapShooter.SnapShooter.main(SnapShooter.java:47)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:90)

    Thanks in Advance,

    Regards,
    Raja.

  7. Lisa permalink
    July 18, 2011 6:25 pm

    I have the same error creating the ‘PersistenceExceptionTranslationPostProcessor’ – has anyone found a way to resolve this? Our code works perfectly on 2 of 3 of our development environments so it shouldn’t be a configuration issue. We’re using the same versions of STS, JDK 1.6_0.23 – and the pom manages all of the dependencies. No idea what could be different!

  8. December 25, 2011 9:00 pm

    Your code contains this:

    public void setEntityManager(EntityManager entityManager) {
    entityManager = entityManager;
    }

    That’s not good.

  9. March 11, 2012 2:49 pm

    Where are the pages ?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: