In
this tutorial we will develop a simple Web application with Spring MVC as J2EE
framework and Dojo as javascript framework. We will use same Northwind Database
as discussed in previous article. Following will be functionality of
application.
User
will be able to search for employees based on employee id, first name or last
name. Once list is displayed user can select single record and update the same.
Once the record is updated flow will go back to search screen.
From
learning point of view we will learn following in Dojo
Modules
like dojo/dom, dojo/query, dojo/on, dojo/domReady also we will see how define
and require functions of dojo differ in nature. So lets start
First
we will configure two config files in our Web.xml (deployment descriptor file)
root-context.xml: This file will have all information
that will be shared across all servlet contexts (or modules or say throughout
application)
mvcapp-servlet-context: This file will have information
about beans for specific module.
So
following are our files
root-context.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<context:annotation-config />
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/northwind"
/>
<property name="username"
value="root" />
<property name="password"
value="" />
</bean>
</beans>
|
Here
we have defined common beans that we will need for all modules like datasource
which will have information about the DB that we want to connect to.
mvcapp-servlet-context.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<context:component-scan base-package="com.northwindsh">
<context:exclude-filter type="annotation"
expression="com.northwindsh.form.Contact"
/>
</context:component-scan>
<context:annotation-config />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"
/>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<property name="annotatedClasses">
<list>
<value>com.northwindsh.bean.Customer</value>
</list>
</property>
</bean>
</beans>
|
In
this configuration file we have information about,
sessionFactory (this too can be moved to module
specific configuration file but for sack of brevity I am keeping it here) which
will have information about hibernate configuration and classes that will be
annoted by @Entity annotation and mapped to one or more tables by @Table
annotation.
viewResolver: What suffix and prefix will be used
to derive view object.
Package
name that we need to scan for annotated classes for auto wiring beans.
Now
in our web.xml we will provide entry for these two files and there will be a
tweak for js and css files that interested reader can notice (there should be a
cleaner way of doing the same but for now I am keeping it simple)
web.xml
<?xml version="1.0"
encoding="UTF-8"?>
<web-app
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID"
version="3.0">
<display-name>Northwind Spring</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>mvcapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/mvcapp-servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvcapp</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/dojo/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/nwdojo/*</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/root-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
|
So first we will create our VO class
Now we write our first controller that will route us to search page. So following will be our controller with single method that will simply move us to search page.
package
com.northwindsh.form;
public class Contact {
private String firstname;
private String lastname;
private String email;
private String telephone;
public String getFirstname() {
return firstname;
}
public void
setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String
lastname) {
this.lastname = lastname;
}
public String getEmail() {
return email;
}
public void setEmail(String
email) {
this.email = email;
}
public String getTelephone() {
return telephone;
}
public void
setTelephone(String telephone) {
this.telephone = telephone;
}
}
|
Now we write our first controller that will route us to search page. So following will be our controller with single method that will simply move us to search page.
package
com.northwindsh.controller;
import
org.springframework.stereotype.Controller;
import
org.springframework.web.bind.annotation.RequestMapping;
import
org.springframework.web.bind.annotation.SessionAttributes;
import
org.springframework.web.servlet.ModelAndView;
import
com.northwindsh.form.Contact;
@Controller
@SessionAttributes
@RequestMapping(value = "/customer")
public class CustomerController
{
@RequestMapping("/search")
public ModelAndView search() {
return new ModelAndView("select-customer", "command", new Contact());
}
}
|
And
we will create a jsp file in WEB-INF/pages/select-customer.jsp as follows
<%@ page
language="java" contentType="text/html;
charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html
PUBLIC "-//W3C//DTD
HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=ISO-8859-1">
<title>Customer</title>
<script type="text/javascript"
src="../dojo/dojo.js" data-dojo-config="async: true"></script>
</head>
<body>
<form id='custSearchFrm'>
<table>
<tr>
<td>Customer Id</td>
<td><input id='custId'
name='custId'></td>
</tr>
<tr>
<td>Company Name</td>
<td><input id='compName'
name='compName'></td>
</tr>
<tr>
<td>Customer Last Name</td>
<td><input id='contactName'
name='contactName'></td>
</tr>
</table>
<br/>
<input type="button"
id='custSearchBtn' value='Search Customers'>
</form>
<div id="searchResults">
Results will come here
</div>
<script type="text/javascript">
require(["nwdojo/selectcustomer"],function(selCustObj){
});
</script>
</body>
</html>
|
Ensure
that you have downloaded dojo library and it is present inside your WebContent
folder in eclipse. Also notice that we are using custom module called selectcustomer by using dojo function require. When
we pass module name to this function dojo will check for selectcustomer.js in folder nwdojo
of root of web application. So we will create following file WebContent/nwdojo/selectcustomer.js
define(["dojo/request/xhr","dojo/dom","dojo/on","dojo/domReady!"],
function(xhr,dom,on){
function updateCustomer(){
dom.byId("custForm").submit();
}
function callAjaxListing(){
alert("Will send
ajax call");
xhr("customer/list",{method:"post",data:{'custId':'1'},preventCache:true}).then(
function(data){
dom.byId("searchResults").innerHTML=data;
on(dom.byId("custEditBtn"),"click",updateCustomer);
},
function(err){
alert("error"+err);
},
function(evt){
}
);
}
on(dom.byId("custSearchBtn"),"click",callAjaxListing);
}
);
|
In
this file note following
We
are passing following modules as an element of first argument array of define
function. With respect to each module (except last part “dojo/domReady!”) we
have respective object (in exact sequence) as function variable which is second
argument of function define. i.e. xhr for “dojo/request/xhr”, dom
for “dojo/dom” and on for ” dojo/on”.
We
are creating two functions
Ø updateCustomer() which will use module dom to get the
html form object identified by Id custForm and submit it.
Ø callAjaxListing() which will use xhr module to get data
from server (in turn we will finally get data from DB) by making AJAX call. On successful
retrieval this data will be pasted in div identified by id searchResults and also onClick event of button identified by id
custEditBtn will be attached to javascript function updateCustomer. Also note that we have attached this
function to button on successful return of AJAX response
Be
aware that we are yet to write server side component of both these actions i.e.
listing data and submitting form. So when you hit url http://localhost:8080/NorthWindSpringHibernate/customer/search following is what you
will see
Now
we will add functions (and appropriate mapping) in controller class to get the
listing, show the selected record(s) and update selected customer. So our
controller class will have following functions.
/**
* Will show list of customers from table
'customers'
* @param cust
* @param uiModel
* @return
*/
@RequestMapping("list")
public String
listCustomers(@ModelAttribute Customer cust, Model uiModel) {
System.out.println("Listing
customers:" + cust);
List<Customer> custList = custSvc.getCustomerList();
uiModel.addAttribute("list", custList);
return "customerlist";
}
/**
* Will show selected customers
* @param selectedIdList
* @param uiModel
* @return
*/
@RequestMapping("showselected")
public String
showSelectedCustomers(
@RequestParam(value = "selectedCustId")
List<String> selectedIdList,
Model uiModel) {
System.out.println("Listing
selected:" + selectedIdList);
Customer cust = custSvc.getCustomerDetails(selectedIdList.get(0));
uiModel.addAttribute("cust", cust);
return "customerdetails";
}
/**
* Will update customer record with value
coming from frontend in argument 'model'
* @param model
* @param uiModel
* @return
*/
@RequestMapping("updateCust")
public String
updateCustomer(@ModelAttribute Customer model, Model uiModel) {
System.out.println("Customer
will be updated" + model);
custSvc.updateCustomer(model);
List<Customer> custList = custSvc.getCustomerList();
uiModel.addAttribute("list", custList);
return "customerlist";
}
|
Interested
user can implement Service class and DAO class to list, display and update
selected functionalities. Also we will create one jsp for listing search
results and one for displaying details screen.
customerlist.jsp
<%@ page
language="java" contentType="text/html;
charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib
prefix="c" uri="http://java.sun.com/jstl/core_rt"
%>
<!DOCTYPE html
PUBLIC "-//W3C//DTD
HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=ISO-8859-1">
<script src='<c:url value="/dojo/dojo.js"/>' data-dojo-config="async: true"></script>
<title>Customer List</title>
</head>
<body>
<form action="showselected"
method="post" id="custForm">
<table>
<thead>
<tr>
<th></th>
<th>Customer ID</th>
<th>Company Name</th>
<th>Contact Name</th>
</tr>
</thead>
<tbody>
<c:forEach items="${list}"
var="cust" varStatus="loop">
<tr id="record_${loop.index % 2}">
<td><input type="checkbox"
value="${cust.custId}" name='selectedCustId'
id='selectedCustId'></td>
<td><a href='show/${cust.custId}'>${cust.custId}</a></td>
<td style="color:red">${cust.compName}</td>
<td>${cust.contactName}</td>
</tr>
</c:forEach>
<tr>
<td>
<input value="Edit"
id="custEditBtn" type="button">
</td>
<td>
<input value="Delete"
id="custDelBtn" type="button">
</td>
</tr>
</tbody>
</table>
</form>
<script type="text/javascript">
require([
"nwdojo/customerlist"
], function(counterObj){
});
</script>
</body>
</html>
|
And
<%@ page
language="java" contentType="text/html;
charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib
prefix="c" uri="http://java.sun.com/jstl/core_rt"%>
<!DOCTYPE html
PUBLIC "-//W3C//DTD
HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=ISO-8859-1">
<script src='<c:url value="/dojo/dojo.js"/>'
data-dojo-config="async:
true"></script>
<title>Customer Details</title>
</head>
<body>
<form action="updateCust"
method="post" id='updateCust'>
<table>
<tr>
<td>Customer Id</td>
<td><input value='${cust.custId}'
readonly="readonly" name='custId'></td>
</tr>
<tr>
<td>Company Name</td>
<td><input value='${cust.compName}'
name='compName'></td>
</tr>
<tr>
<td>Contact Name</td>
<td><input value='${cust.contactName}'
name='contactName'></td>
</tr>
<tr>
<td colspan='2'><input type="button" id='custUpdateBtn'
value="Update"></td>
</tr>
</table>
</form>
<script type="text/javascript">
require([
"nwdojo/customerdetails"
], function(counterObj){
});
</script>
</body>
</html>
|
Here
again we will have new js file that will represent out customerdetails module.
define(["dojo/dom","dojo/on","dojo/domReady!"],
function(dom,on){
function updateCustomer(){
dom.byId("updateCust").submit();
}
on(dom.byId("custUpdateBtn"),"click",updateCustomer);
}
);
|
When
we click on Search button we will get list of customers as follows (note that
here we have restricted size to 10)
When
you will select one record and click on Edit button it will take us to next
screen to edit the data.
We
can update this value and next time one screen we can see updated value in
Search Results
Listing after update.
So
are done with small application. Interested reader can apply following
Ensure
only one record is selected for edit…etc