Note: An updated version of this tutorial is now accessible at Spring MVC 3.1, jqGrid, and Spring Data JPA Integration Guide
What is jqGrid
jqGrid is an Ajax-enabled JavaScript control that provides solutions for representing and manipulating tabular data on the web. Since the grid is a client-side solution loading data dynamically through Ajax callbacks, it can be integrated with any server-side technology, including PHP, ASP, Java Servlets, JSP, ColdFusion, and Perl.Here's a screenshot of how our jqGrid-powered application would look like:
jqGrid uses a jQuery Java Script Library and is written as plugin for that package.
Source: http://www.trirand.com/jqgridwiki/doku.php
In a nutshell jqGrid is a table for manipulating data. It's an AJAX application that's built on top of JQuery. It communicates via JSON.
What is MongoDB?
MongoDB (from "humongous") is a scalable, high-performance, open source, document-oriented database. Written in C++, MongoDB features:Here's our database schema:
- Document-oriented storage
- Full Index Support
- Replication & High Availability
- Scale horizontally without compromising functionality.
- Rich, document-based queries.
- Atomic modifiers for contention-free performance.
- Flexible aggregation and data processing.
- Store files of any size without complicating your stack.
- Enterprise class support, training, and consulting available.
Source: http://www.mongodb.org/
{ id:'', firstName:'', lastName:'', money:'' }In a nutshell MongoDB is a database that uses JSON instead of SQL There's no static schema to create. All schemas are dynamic, meaning you create them on-the-fly. You can try a real-time online shell for MongoDB at http://try.mongodb.org/. Visit the official MongoDB site for a through discussion.
In order to complete this tutorial, you will be required to install a copy of MongoDB. If you don't have a MongoDB yet, grabe one now by visiting this link http://www.mongodb.org/display/DOCS/Quickstart. The installation is really easy.
Let's begin by defining our MongoDBFactory.
MongoDBFactory
MongoDBFactory is simply a factory for retrieving a single instance of your database via getDB() and a single instance of your collection via getCollection(). This is a custom class we created to simplify the retrieval of these items. If you prefer to retrieve them manually instead of using this MongoDBFactory, you're free to do so. Here's an example on how you may retrieve them manually:
Remember our database and collections will be created on-the-fly. We will not create a domain object here because the fields of our jqGrid exactly matches our dynamic schema in MongoDB. This is intentional. What happens if we change the fields in jqGrid? Then we can simply create a new schema in MongoDB. What if we change the schema in MongoDB, then we can simply change the fields in jqGrid. This is what makes them dynamic.
Let's start.
First, we declare our service interface.
IUserService
Here's the service implementation:
UserService
This service defines our basic CRUD system. We have the following methods:
getAll() - for retrieving all persons edit() - for editing delete() - for deleting add() - for adding get() - for retrieving single person
The database is initialized once in the UserService' constructor through the init() method:
Notice we're creating a dynamic JSON schema here with the following format:
{ id:'', firstName:'', lastName:'' }
We have declared our service. Let's now declare a controller.
MediatorController
This controller declares a single mapping:
/main/usersThis loads a JSP page containing our jqGrid.
Here's the JSP page:
users.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <c:url value="spring-jqgrid-mongo" var="baseUrl"/> <head> <link rel="stylesheet" type="text/css" media="screen" href="/${baseUrl}/resources/css/jquery/ui-lightness/jquery-ui-1.8.6.custom.css" /> <link rel="stylesheet" type="text/css" media="screen" href="/${baseUrl}/resources/css/jqgrid/ui.jqgrid.css" /> <script type="text/javascript" src="/${baseUrl}/resources/js/jquery/jquery-1.4.4.min.js"></script> <script type="text/javascript"> var jq = jQuery.noConflict(); </script> <script type="text/javascript" src="/${baseUrl}/resources/js/jquery/jquery-ui-1.8.6.custom.min.js"></script> <script type="text/javascript" src="/${baseUrl}/resources/js/jqgrid/grid.locale-en.js" ></script> <script type="text/javascript" src="/${baseUrl}/resources/js/jqgrid/jquery.jqGrid.min.js"></script> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Spring MVC 3: jqGrid and MongoDB Integration Tutorial</title> </head> <body > <script type="text/javascript"> jq(function() { jq("#grid").jqGrid({ url:'/${baseUrl}/krams/crud', datatype: 'json', mtype: 'GET', colNames:['Id', 'First Name', 'Last Name'], colModel:[ {name:'id',index:'id', width:55,editable:false,editoptions:{readonly:true,size:10},hidden:true}, {name:'firstName',index:'lastName', width:100,editable:true, editrules:{required:true}, editoptions:{size:10}}, {name:'lastName',index:'firstName', width:100,editable:true, editrules:{required:true}, editoptions:{size:10}} ], postData: { }, rowNum:20, rowList:[20,40,60], height: 200, autowidth: true, rownumbers: true, pager: '#pager', sortname: 'id', viewrecords: true, sortorder: "asc", caption:"Users", emptyrecords: "Empty records", loadonce: false, loadComplete: function() { }, jsonReader : { root: "rows", page: "page", total: "total", records: "records", repeatitems: false, cell: "cell", id: "id" } }); jq("#grid").jqGrid('navGrid','#pager', {edit:false,add:false,del:false,search:true}, { }, { }, { }, { sopt:['eq', 'ne', 'lt', 'gt', 'cn', 'bw', 'ew'], closeOnEscape: true, multipleSearch: true, closeAfterSearch: true } ); jq("#grid").navButtonAdd('#pager', { caption:"Add", buttonicon:"ui-icon-plus", onClickButton: addRow, position: "last", title:"", cursor: "pointer" } ); jq("#grid").navButtonAdd('#pager', { caption:"Edit", buttonicon:"ui-icon-pencil", onClickButton: editRow, position: "last", title:"", cursor: "pointer" } ); jq("#grid").navButtonAdd('#pager', { caption:"Delete", buttonicon:"ui-icon-trash", onClickButton: deleteRow, position: "last", title:"", cursor: "pointer" } ); jq("#btnFilter").click(function(){ jq("#grid").jqGrid('searchGrid', {multipleSearch: false, sopt:['eq']} ); }); // Toolbar Search jq("#grid").jqGrid('filterToolbar',{stringResult: true,searchOnEnter : true, defaultSearch:"cn"}); }); </script> <script type="text/javascript"> function addRow() { // Get the currently selected row jq("#grid").jqGrid('editGridRow','new', { url: "/${baseUrl}/krams/crud/add", editData: { }, recreateForm: true, beforeShowForm: function(form) { }, closeAfterAdd: true, reloadAfterSubmit:false, afterSubmit : function(response, postdata) { var result = eval('(' + response.responseText + ')'); var errors = ""; if (result.success == false) { for (var i = 0; i < result.message.length; i++) { errors += result.message[i] + "<br/>"; } } else { jq("#dialog").text('Entry has been added successfully'); jq("#dialog").dialog( { title: 'Success', modal: true, buttons: {"Ok": function() { jq(this).dialog("close");} } }); } // only used for adding new records var new_id = null; return [result.success, errors, new_id]; } }); } function editRow() { // Get the currently selected row var row = jq("#grid").jqGrid('getGridParam','selrow'); if( row != null ) jq("#grid").jqGrid('editGridRow',row, { url: "/${baseUrl}/krams/crud/edit", editData: { }, recreateForm: true, beforeShowForm: function(form) { }, closeAfterEdit: true, reloadAfterSubmit:false, afterSubmit : function(response, postdata) { var result = eval('(' + response.responseText + ')'); var errors = ""; if (result.success == false) { for (var i = 0; i < result.message.length; i++) { errors += result.message[i] + "<br/>"; } } else { jq("#dialog").text('Entry has been edited successfully'); jq("#dialog").dialog( { title: 'Success', modal: true, buttons: {"Ok": function() { jq(this).dialog("close");} } }); } return [result.success, errors, null]; } }); else jq( "#dialogSelectRow" ).dialog(); } function deleteRow() { // Get the currently selected row var row = jq("#grid").jqGrid('getGridParam','selrow'); // A pop-up dialog will appear to confirm the selected action if( row != null ) jq("#grid").jqGrid( 'delGridRow', row, { url: '/${baseUrl}/krams/crud/delete', recreateForm: true, beforeShowForm: function(form) { //change title jq(".delmsg").replaceWith('<span style="white-space: pre;">' + 'Delete selected record?' + '</span>'); //hide arrows jq('#pData').hide(); jq('#nData').hide(); }, reloadAfterSubmit:false, closeAfterDelete: true, afterSubmit : function(response, postdata) { var result = eval('(' + response.responseText + ')'); var errors = ""; if (result.success == false) { for (var i = 0; i < result.message.length; i++) { errors += result.message[i] + "<br/>"; } } else { jq("#dialog").text('Entry has been deleted successfully'); jq("#dialog").dialog( { title: 'Success', modal: true, buttons: {"Ok": function() { jq(this).dialog("close");} } }); } // only used for adding new records var new_id = null; return [result.success, errors, new_id]; } }); else jq( "#dialogSelectRow" ).dialog(); } </script> <p>Spring MVC 3: jqGrid and MongoDB Integration Tutorial</p> <div id="jqgrid"> <table id="grid"></table> <div id="pager"></div> </div> <div id="dialog" title="Feature not supported" style="display:none"> <p>That feature is not supported.</p> </div> <div id="dialogSelectRow" title="Warning" style="display:none"> <p>Please select row</p> </div> </body> </html>
Notice for each of the major jqGrid functions, there's a corresponding URL:
/${baseUrl}/krams/crud - retrieves all users in JSON format /${baseUrl}/krams/crud/add" - adds a new user /${baseUrl}/krams/crud/edit - edits an existing user /${baseUrl}/krams/crud/delete - deletes an existing userThese are used by JQuery when performing AJAX calls. A separate controller handles the calls.
UserController
Each URLs are mapped to a specific handler method in the controller. When the controller receives the request, it delegates processing to the service. If the service returns a successful message, a success message is returned. Likewise, if the service returns a failure message, a failure message is returned.
CustomGenericResponse is a simple POJO where can assign our custom responses. When the object is returned, Spring will automatically convert it to a JSON object, which the jqGrid understands
CustomGenericResponse
However, when retrieving all users, we wrapped the response in a CustomUserResponse object instead
CustomUserResponse
Let's finalize our Spring MVC application by declaring the required XML configurations.
To enable Spring MVC we need to add it in the web.xml
web.xml
Take note of the URL pattern. When accessing any pages in our MVC application, the host name must be appended with
/kramsIn the web.xml we declared a servlet-name spring. By convention, we must declare a spring-servlet.xml as well.
spring-servlet.xml
By convention, we must declare an applicationContext.xml as well.
applicationContext.xml
That's it. We've managed to create a simple Spring MVC 3 application that uses MongoDB for its database and jqGrid for its presentation layer. We've seen how we can map our schema from MongoDB to jqGrid easily.
The best way to learn further is to try the actual application.
Download the project
You can access the project site at Google's Project Hosting at http://code.google.com/p/jqgrid-spring3mvc-integration-tutorial/
You can download the project as a Maven build. Look for the spring-jqgrid-mongo.zip in the Download sections.
You can run the project directly using an embedded server via Maven.
For Tomcat: mvn tomcat:run
For Jetty: mvn jetty:run
If you want to learn more about Spring MVC and integration with other technologies, feel free to read my other tutorials in the Tutorials section.
Share the joy:
|
Subscribe by reader Subscribe by email Share