Saturday, January 28, 2012

Spring MVC 3.1, jqGrid, and Spring Data JPA Integration Guide (Part 1)

In this tutorial, we will create a simple CRUD application using Spring MVC 3.1, jqGrid, and Spring Data JPA. We will use jqGrid, a jQuery plugin, to present tabular data as an interactive grid, and Spring Data JPA to simplify the creation of JPA repositories (where Hibernate and MySQL are our JPA vendor and database respectively).


Dependencies

  • Spring core 3.1.0.RELEASE
  • Spring Data JPA 1.1.0 RC1
  • jQuery 1.6.4
  • jqGrid 4.3.1
  • See pom.xml for details

Github

To access the source code, please visit the project's Github repository (click here)

Functional Specs

Before we start, let's define our application's specs as follows:
  • A CRUD page for managing users
  • Use AJAX to avoid page refresh
  • Display reports in an interactive table
  • Users have roles. They are either admin or regular (default)
  • Everyone can create new users and edit existing ones
  • When editing, users can only edit first name, last name, and role fields
  • A username is assumed to be unique

Here's our Use Case diagram:
[User]-(View)
[User]-(Add) 
[User]-(Edit) 
[User]-(Delete) 

Database

Our database contains two tables: user and role tables.


user and role table design

User table

The user table contains personal information of each user. Notice the password values are hashed using Md5.

user table

Role table

The role table contains role values of each user. We define a role value of 1 as an admin, while a role value of 2 as a regular user.

role table

Screenshots

Before we start with the actual development, let's preview how our application should look like by providing screenshots. This is also a good way to clarify further the application's specs.

Entry page
The entry page is the primary page that users will see. It contains an interactive table where users can view, add, edit, and delete records on the same page.
Entry page (page 1)

Entry page (page 2)

Entry page (showing all records at once)

Create new record form
This form is used for adding new records. It is displayed when the user clicks on the Add button.
Create new record

Edit existing record form
This form is used for editing existing records. It is displayed when the user clicks on the Edit button. When editing, the form is pre-populated with the selected record's data.
Edit existing record

Alerts
You must select a record first alert is displayed whenever a user tries to edit or delete an existing record without selecting first that record.
Failure alert

Entry has been edited successfully alert is displayed whenever a successful action has been done, for example editing of an existing record.
Success alert

Filtered records
Records can be filtered by typing keywords on the menu toolbar.
Filtered records

Next

In the next section, we will discuss the project's structure and write the Java classes. Click here to proceed.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring MVC 3.1, jqGrid, and Spring Data JPA Integration Guide (Part 1) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

29 comments:

  1. Hi Krams,

    Thanks for the good presence of this article.It really helps me a lot.I have doubt why do we need to use "DTO" objects instead of using straight domain objects.Please reply me.

    - Arun Rajesh

    ReplyDelete
    Replies
    1. @ Arun, the only reason I can think of is encapsulation. As "User" and "Role" are different objects, and the information in both of them needs to go up as one, so all the information that needs to go up is placed in one object and sent up.

      Delete
  2. Hi Krams, please update your Spring Data JPA to 1.1.0.RELEASE

    ReplyDelete
  3. Hi Krams, How to display multiple table records in grid and update records spanning in multiple tables.Thanks

    ReplyDelete
  4. This blog has helped me to learn and use jquery. Now i am stuck with using jqgrid dropdown i.e. select as i have to populate dropdown list in jqgrid from database i.e mysql. Moreover lists are dependent e.g. state depends on country.

    Please help on this.

    ReplyDelete
  5. Olá, Krams seus posts são execelentes...

    galack do brasil ,obrigado!

    ReplyDelete
  6. @ Arun, the only reason I can think of is encapsulation. As "User" and "Role" are different objects, and the information in both of them needs to go up as one, so all the information that needs to go up is placed in one object and sent up.

    ReplyDelete
  7. Hi Krams,

    I followed your tutorial in my code, I am getting the json data in this format. I dont see curly braces or key-value pair.. What could be the issue?

    {"total":"10","page":"1","rows":[[43,1354522733624,"ValidFile02.csv","test demo -07"],[42,1354522600580,"ValidFile01.csv","Test for demo - 06"],[41,1354522480789,"ValidFile02.csv","Test for demo - 05"]],"records":"3"}

    ReplyDelete
    Replies
    1. Did you modify anything in the code? Or did you retrofit the sample code in your own custom application? Did you do any custom mapping from your domain to dto?

      Delete
  8. Hi dear krams, thank you for your great tutorials but,
    I can't run this project
    I get this error in the console:

    Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.krams.repository.UserRepository org.krams.controller.UserController.repository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository': FactoryBean threw exception on object creation; nested exception is java.lang.NoSuchMethodError: org.springframework.data.repository.core.RepositoryMetadata.getDomainClass()Ljava/lang/Class;

    what this means

    also Netbeans say this:
    name clash: save(java.lang.Iterable) in org.springframework.data.jpa.repository.JpaRepository and <'S'>save(java.lang.Iterable<'S'>) in org.springframework.data.repository.CrudRepository have the same erasure, yet neither overrides the other

    note : i add apostrophe around S and changed it to 'S' because the online editor don't allow me to add S tag.

    ReplyDelete
    Replies
    1. Thank you Frank Lloyd Teh
      I update Spring Data JPA to 1.1.0.RELEASE and every thing is OK now.

      Delete
  9. Hi Chaps!!
    That was a great tutorial to start with. However, I am not able to run the application and I keep getting the following error during the tomcat server startup: org.springframework.data.mapping.PropertyReferenceException: No property customer found for type com.adaptris.dashboard.customer.Customer
    Where Customer is my domain Class with @Entity(name = "customer").

    I've also created the table 'customer in MySQL DB'. Please help me out in this.

    ReplyDelete
  10. Hi Krams,

    Thanks for such a wonderful tutorials.

    I trying to run your Spring jqgrid jpa tutoria on tomcat but getting below mention error.

    please help me to resolve this asap.

    I was unable to keep all the stack trace.

    [ERROR] [main 09:58:17] (ContextLoader.java:initWebApplicationContext:312) Context initialization failed
    java.lang.NoClassDefFoundError: org/springframework/beans/factory/NoUniqueBeanDefinitionException
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2427)
    at java.lang.Class.getDeclaredMethods(Class.java:1791)
    at org.springframework.core.type.StandardAnnotationMetadata.hasAnnotatedMethods(StandardAnnotationMetadata.java:136)
    at org.springframework.context.annotation.ConfigurationClassUtils.isLiteConfigurationCandidate(ConfigurationClassUtils.java:105)
    at org.springframework.context.annotation.ConfigurationClassUtils.checkConfigurationClassCandidate(ConfigurationClassUtils.java:86)
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:216)
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:1

    ReplyDelete
    Replies
    1. Have you tried doing a clean build with Maven? Make sure the dependencies are loaded.

      Delete
  11. Yes, I am joining horde of flamers!

    The tutorial is supposed to be used by novice developers! This is who I am in java world. And I have no idea how to run your tutorial!

    I downloaded latest SpringToolsSuite an dimported your latest project from github, no way to make it work!

    Shame!

    ReplyDelete
    Replies
    1. Well, you know projects. There's no guarantee that it will work out-of-the-box for everyone.

      Delete
  12. Hi I have imported this specific project into my eclipse and build is successful. but when deployed in tomcat I am getting the following error.
    Mar 25, 2013 11:19:12 AM org.apache.catalina.core.ApplicationContext log
    INFO: Initializing Spring root WebApplicationContext
    Mar 25, 2013 11:19:14 AM org.apache.catalina.core.StandardContext listenerStart
    SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
    java.lang.NoClassDefFoundError: org/springframework/beans/factory/NoUniqueBeanDefinitionException
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2427)
    at java.lang.Class.getDeclaredMethods(Class.java:1791)
    at org.springframework.core.type.StandardAnnotationMetadata.hasAnnotatedMethods(StandardAnnotationMetadata.java:136)
    at org.springframework.context.annotation.ConfigurationClassUtils.isLiteConfigurationCandidate(ConfigurationClassUtils.java:105)
    at org.springframework.context.annotation.ConfigurationClassUtils.checkConfigurationClassCandidate(ConfigurationClassUtils.java:86)
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:216)
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:178)
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:617)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:446)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:384)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:283)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4779)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5273)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:895)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:871)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:615)
    at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:962)
    at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1603)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
    Caused by: java.lang.ClassNotFoundException: org.springframework.beans.factory.NoUniqueBeanDefinitionException
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1711)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1556)
    ... 27 more
    Mar 25, 2013 11:19:15 AM org.apache.catalina.core.ApplicationContext log
    INFO: Closing Spring root WebApplicationContext
    Mar 25, 2013 11:19:16 AM org.apache.catalina.core.ApplicationContext log
    INFO: ContextListener: contextInitialized()
    Mar 25, 2013 11:19:16 AM org.apache.catalina.core.ApplicationContext log
    INFO: SessionListener: contextInitialized()

    ReplyDelete
  13. Help would be appreciated on this.


    Mar 25, 2013 11:19:12 AM org.apache.catalina.core.ApplicationContext log
    INFO: Initializing Spring root WebApplicationContext
    Mar 25, 2013 11:19:14 AM org.apache.catalina.core.StandardContext listenerStart
    SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
    java.lang.NoClassDefFoundError: org/springframework/beans/factory/NoUniqueBeanDefinitionException
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2427)
    at java.lang.Class.getDeclaredMethods(Class.java:1791)
    at org.springframework.core.type.StandardAnnotationMetadata.hasAnnotatedMethods(StandardAnnotationMetadata.java:136)
    at org.springframework.context.annotation.ConfigurationClassUtils.isLiteConfigurationCandidate(ConfigurationClassUtils.java:105)
    at org.springframework.context.annotation.ConfigurationClassUtils.checkConfigurationClassCandidate(ConfigurationClassUtils.java:86)
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:216)
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:178)
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:617)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:446)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:384)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:283)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4779)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5273)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:895)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:871)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:615)
    at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:962)
    at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1603)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
    Caused by: java.lang.ClassNotFoundException: org.springframework.beans.factory.NoUniqueBeanDefinitionException
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1711)
    at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1556)
    ... 27 more
    Mar 25, 2013 11:19:15 AM org.apache.catalina.core.ApplicationContext log
    INFO: Closing Spring root WebApplicationContext
    Mar 25, 2013 11:19:16 AM org.apache.catalina.core.ApplicationContext log
    INFO: ContextListener: contextInitialized()
    Mar 25, 2013 11:19:16 AM org.apache.catalina.core.ApplicationContext log
    INFO: SessionListener: contextInitialized()

    ReplyDelete
    Replies
    1. You're missing a class. Are you sure that you have all the required jars and dependencies?

      Delete
    2. Solved for me. I added this to pom.xml and rebuilt it in eclipse. Thanks for the beautiful tutorial.

      This
      3.1.0.RELEASE

      To this
      3.2.2.RELEASE



      Delete
  14. You are great like your tutorials.
    now i am using this tutorial in my company (in Iran , Asia).
    God Bless You

    ReplyDelete
    Replies
    1. Thanks. If you discover mistakes or bugs (I'm sure there is), don't teach those to your students :-)

      Delete
  15. What is the purpose of including querydsl in this example? It does not appear to be used.

    ReplyDelete
  16. Best Spring blog. Great tutorial dude, thanks for you time.

    ReplyDelete
  17. Hi Krams,
    Is there a way you can write a tutorial about how to integrate an event calendar with a back end database using Spring MVC.
    Here is the calendar plugin I am talking about:
    http://www.webappers.com/2010/06/08/wdcalendar-jquery-based-google-calendar-clone/

    Thanks,
    Emmanuel

    ReplyDelete
  18. This is one of the best blogs on Spring/jQuery.

    ReplyDelete
  19. Hi Mark

    For some reason spring is not injecting the parameters as per @RequestParam. The controllers work without any request parameters just fine such as a blind search. The same issue with edit/update. Looks like some kind of binding issue. I am using Spring 3.1.0.M2 . If not there could be some issues around the jqgrid ajax calls . Please help.

    java.lang.IllegalArgumentException: Name for argument type [java.lang.Integer] not available, and parameter name information not found in class file either.
    at org.springframework.util.Assert.notNull(Assert.java:112)
    at org.springframework.web.method.annotation.support.AbstractNamedValueMethodArgumentResolver.updateNamedValueInfo(AbstractNamedValueMethodArgumentResolver.java:133)
    at org.springframework.web.method.annotation.support.AbstractNamedValueMethodArgumentResolver.getNamedValueInfo(AbstractNamedValueMethodArgumentResolver.java:111)
    at org.springframework.web.method.annotation.support.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:80)
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:65)
    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:153)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:117)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:100)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:502)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:465)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:863)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:792)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:851)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:767)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:563)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:602)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
    at java.lang.Thread.run(Thread.java:662)


    ReplyDelete
  20. Hi Krams

    I like this tutorial.

    I want to use the source code in my project, but I couldn’t find any clear information on your license policy.

    Could you inform me about that in detail ?

    can i get it for free?

    ReplyDelete
  21. A great article. Helped me learn integrate jqGrid and Spring. Thank you.

    ReplyDelete