Forms and data models in Spring MVC

We were able to achieve what we wanted in the previous example, but the form handling techniques we used were very basic. Suppose we would like to submit form variables from the html page using a form object instead of submitting and handling the raw data parameters as we have just done. Rather than entering the greeting inside this basic html text box like this:

<input type="text" name="greetingText">

let’s use Spring’s form tags and the ModelAttribute and transform it to something like this:

<form:input path="greeting.greetingText">

1. Add a domain object called Greeting.java

We will start by creating a domain object which will eventually be persisted. This will contain our greeting text and other fields which we will add later. Now add Greeting.java to the src/main/java folder with the package name com.bitbybit.domain.

Greeting.java
package com.bitbybit.domain;
 
public class Greeting {
 
	private String greetingText;
 
	public String getGreetingText() {
		return greetingText;
	}
 
	public void setGreetingText(String greetingText) {
		this.greetingText = greetingText;
	}
 
}

2. Add a domain object called Color.java

Now we will add another domain object called Color. What if we would like to know the user’s favorite color? And then display that color in the background of the next pages. Something like this:

Add your greeting: [ ...... ]
Choose a color: [red, orange, yellow, green, blue, purple]
[Submit]

We might want to eventually persist the color data in the database, but don’t know that yet. Let’s start by creating a Color object with the name of the color and the html color code.

Color.java
package com.bitbybit.domain;
 
public class Color {
 
	private String colorName;
 
	private String colorCode;
 
	public Color(){}
 
	public Color(String colorName, String colorCode) {
		this.colorName = colorName;
		this.colorCode = colorCode;
	}
 
	public String getColorName() {
		return colorName;
	}
 
	public void setColorName(String colorName) {
		this.colorName = colorName;
	}
 
	public String getColorCode() {
		return colorCode;
	}
 
	public void setColorCode(String colorCode) {
		this.colorCode = colorCode;
	}
 
}

We have created two constructors here. Don’t forget to include the empty constructor! The spring container seems to need to have the empty constructor when it instantiates new objects, or it will throw an Exception, so keep the habit of doing this just in case.

3. Add a wrapper class called GreetingForm.java

Now here is a classic dilemma. We would like to use the Greeting object in the jsp pages and handler classes and we would like to use the Color object as well (when submitting the user’s greeting and color choice) but we can really only submit one model object from our html form. So what do we do? Here are some options:

a.) put the Color object inside the Greeting object and use the Greeting object as both the web and domain object
b.) create a new class called GreetingForm.java with fields for the web form but no reference to Color object or Greeting object and transfer the fields back and forth from persistence and web layers via DTOs
c.) create a new class called GreetingForm.java and include Color and Greeting objects

Option “a” is not a great option because persistent domain classes should be for persistence and not have other stuff mixed in.
Option “b” is a more viable option and is often used but still involves a lot of code duplication and transfer logic from persistent objects to the web form and vice versa. However it does achieve a clean separation of persistence and web data.

Option “c” is the one we will go for here. GreetingForm is really like a wrapper class. The only disadvantage is that we will have persistence data in the web layer, but that is something we can live with in most cases. However there should not be a clear rule here. Sometimes it makes sense to go with option “b.” If the Greeting object contained 100 fields for example, and we only needed to display one field, then option “b” would make better sense than option “c”.

GreetingForm.java
package com.bitbybit.web.form;
 
import com.bitbybit.domain.Color;
import com.bitbybit.domain.Greeting;
 
public class GreetingForm {
 
	//the domain model persistent data
	private Greeting greeting;
 
	//the other non persistent data ... used in user interface only
	private Color color;
 
	public Greeting getGreeting() {
		return greeting;
	}
 
	public void setGreeting(Greeting greeting) {
		this.greeting = greeting;
	}
 
	public Color getColor() {
		return color;
	}
 
	public void setColor(Color color) {
		this.color = color;
	}
 
}

4. Modify GreetingController.java to take a ModelAttribute parameter

Now GreetingForm is our model attribute, so we must make that an input parameter in our handler methods. Note that the name of the model attribute is “greetingform” and that is the name used in the jsp page! If things with the model in the jsp page do not correspond to the java code, then you will get an exception like this: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name ‘greetingform’ available as request attribute

GreetingController.java
package com.bitbybit.web.controller;
 
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
 
import org.apache.log4j.Logger;
import com.bitbybit.domain.Greeting;
 
import com.bitbybit.domain.Color;
import com.bitbybit.web.form.GreetingForm;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
@Controller
@RequestMapping("/home")
public class GreetingController {
 
	protected static Logger logger = Logger.getLogger("GreetingController");
 
        //note there is no actual greetings.html file!! 
	@RequestMapping(value = "/addgreeting.html", method = RequestMethod.GET)
        public String showAddGreetingPage(
                    @ModelAttribute("greetingform") GreetingForm greetingForm, 
                    Map<String, Object> model) {		
 
		logger.info("entering showAddGreetingPage()");
 
		//color list is hardcoded for now
		List<Color> colorList = new ArrayList<Color>();
		colorList.add(new Color("Indian Red", "F75D59"));
		colorList.add(new Color("Red", "FF0000"));
		colorList.add(new Color("Salmon", "F9966B"));
		colorList.add(new Color("Lemon Chiffon", "FFF8C6"));
		colorList.add(new Color("Olive Green", "BCE954"));
		colorList.add(new Color("Steel Blue", "C6DEFF"));
		colorList.add(new Color("Medium Purple", "9E7BFF"));
		model.put("colorlist", colorList);
 
    	//resolves to /WEB-INF/jsp/addgreeting.jsp
    	return "addgreeting";
	}	
 
	@RequestMapping(value = "/greetings.html", method = RequestMethod.POST)
	public String addGreetingAndShowAll(@ModelAttribute("greetingform") GreetingForm greetingForm,
			Map<String, Object> model) {		
 
		logger.info("entering addGreetingAndShowAll()");
 
		Greeting greeting = greetingForm.getGreeting();
		model.put("greeting", greeting);
 
	    	String selectedColorCode=greetingForm.getColor().getColorCode(); 
	    	if(selectedColorCode.equals("")) //if no color selected, then make default white
	    		selectedColorCode="FFFFFF";
	    	model.put("colorcode", selectedColorCode);
 
	    	// This will resolve to /WEB-INF/jsp/greetings.jsp
	    	return "greetings";
	} 
 
}

5. Modify addgreeting.jsp to use Spring form input tags

Now that the handler method is set up to handle the GreetingForm submission, we just have to modify the jsp to submit it correctly. Note that modelAttribute and commandName are pretty much the same thing in spring terminology and this <form:form action=”greetings.html” modelAttribute=”greetingform”>
would be pretty much the same thing as this
<form:form action=”greetings.html” commandName=”greetingform”>
… how confusing…

WEB-INF/jsp/addgreeting.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
 
<!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=UTF-8">
  	<title>Spring Greetings</title>
</head>
 
<body>
 
<h1>Spring Greetings</h1>
<form:form action="greetings.html" modelAttribute="greetingform"> 	
	<table>
		<tr>
			<td>Add your greeting:</td>
                        <td><form:input path="greeting.greetingText" size="100"/></td>
		</tr>
		<tr>
			<td>What's your favorite color?</td>
			<td> 
				<form:select path="color.colorCode">
            		<form:option value="" label="--Please Select"/>
            		<form:options items="${colorlist}" itemValue="colorCode" itemLabel="colorName"/>
        		</form:select>
			</td>
		</tr>		
		<tr>
			<td colspan="2" align="center">
				<input type="submit" value="Submit" />	
			</td>
		</tr>
	</table>
</form:form>
 
</body>
</html>

6. Modify greetings.jsp to output new results

All that is left is to output the results. We can use the GreetingForm object in this form if we like, or not. Here I decided to just set a few attributes in my model Map for the sake of doing it another way. I set the color code and the Greeting object in my model Map in the handler class, and have displayed them here.

WEB-INF/jsp/greetings.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
 
<!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=UTF-8">
  	<title>Spring Greetings</title>
</head>
 
<body bgcolor='#<c:out value="${colorcode}" />'>
 
<h1>Spring Greetings</h1>
 
Greeting by <b>Anonymous</b><br/>
on <c:out value="<%=new java.util.Date()%>" /><br/>
<c:out value="${greeting.greetingText}" /><br/>			
 
<p><a href="/springgreetings/home/addgreeting.html">Add greeting</a><br/>
<a href="/springgreetings/">Home</a>
 
</body>
</html>

Now redeploy the application
$ mvn tomcat:redeploy
and test it out here

http://localhost:8080/springgreetings3/home/addgreeting.html



Defining a ModelAttribute as a method return type…

Now we have seen how Model attributes can be used as a method input parameter. Model attributes can also be defined as a method return type. This is handy for populating lists with info that doesn’t necessarily have anything to do with the logic in the handler methods – for example, countries, cities, and in this case, color lists. We might need these lists on every page but we don’t necessarily want to put the retrieval logic in every method. So you see, by putting annotation @ModelAttribute(“colorlist”) over the method populateColorList(), the spring container automatically invokes the method for us and assigns it to “colorlist.” Then it’s possible to use this variable directly in the jsp page without having to set the attribute in the model.

GreetingController.java
package com.bitbybit.web.controller;
 
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
 
import org.apache.log4j.Logger;
import com.bitbybit.domain.Greeting;
 
import com.bitbybit.domain.Color;
import com.bitbybit.web.form.GreetingForm;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
@Controller
@RequestMapping("/home")
public class GreetingController {
 
	protected static Logger logger = Logger.getLogger("GreetingController");
 
        //note there is no actual greetings.html file!! 
	@RequestMapping(value = "/addgreeting.html", method = RequestMethod.GET)
        public String showAddGreetingPage(@ModelAttribute("greetingform") GreetingForm greetingForm) {
 
		logger.info("entering showAddGreetingPage()");
 
		//no need to add colorlist to the model anymore since it is defined at method level below
		//no need to have the model object in this method either
 
    	        //resolves to /WEB-INF/jsp/addgreeting.jsp
    	        return "addgreeting";
	}	
 
	@ModelAttribute("colorlist")
	public List<Color> populateColorList() {
		//color list is hardcoded for now...
		List<Color> colorList = new ArrayList<Color>();
		colorList.add(new Color("Indian Red", "F75D59"));
		colorList.add(new Color("Red", "FF0000"));
		colorList.add(new Color("Salmon", "F9966B"));
		colorList.add(new Color("Lemon Chiffon", "FFF8C6"));
		colorList.add(new Color("Olive Green", "BCE954"));
		colorList.add(new Color("Steel Blue", "C6DEFF"));
		colorList.add(new Color("Medium Purple", "9E7BFF"));
		return colorList;
	}	
 
	@RequestMapping(value = "/greetings.html", method = RequestMethod.POST)
	public String addGreetingAndShowAll(@ModelAttribute("greetingform") GreetingForm greetingForm,
			Map<String, Object> model) {		
 
		logger.info("entering addGreetingAndShowAll()");
 
		Greeting greeting = greetingForm.getGreeting();
		model.put("greeting", greeting);
 
	    	String selectedColorCode=greetingForm.getColor().getColorCode(); 
	    	if(selectedColorCode.equals("")) //if no color selected, then make default white
	    	selectedColorCode="FFFFFF";
	    	model.put("colorcode", selectedColorCode);
 
	    	// This will resolve to /WEB-INF/jsp/greetings.jsp
	    	return "greetings";
	} 
 
}

Improving the home page links…

Now let’s start to make the application a little more user friendly and navigable. Modify index.jsp to link to the new pages we have created.

index.jsp
<html>
<body>
<h2>Welcome to Spring Greetings!</h2>
 
<p><a href="/springgreetings/home/addgreeting.html">Add greeting</a><br/>
<a href="/springgreetings/home/greetings.html">Show all greetings</a>
 
</body>
</html>

Now at http://localhost:8080/springgreetings/ we can see an improvement in our home page. But, only one link works. The other gives an error:

To fix this, we just have to add a method to GreetingController.java. It will still display a page with a blank greeting entered by “Anonymous” which isn’t too interesting, but we will soon add persistence and the possibility to retrieve all the greetings from the database. So this method will be useful in the next section.

	//define the same url with GET so users can skip to the greetings page
	@RequestMapping(value = "/greetings.html", method = RequestMethod.GET)
	public String showAllGreetings(Map<String, Object> model) {		
 
	        logger.info("entering showAllGreetings");
 
		model.put("colorcode", "FFFFFF");
 
	    	// This will resolve to /WEB-INF/jsp/greetings.jsp
	    	return "greetings";
	}

This is what your directory should look like:

Here is the source code.



5 Comments
Hitesh Jain wrote on 2013-06-28 at 08:54:29:
Thanks for this post, its a really very good MVC spring post


Japheth wrote on 2013-03-13 at 15:32:24:
Thanks, the best spring mvc tutorial I have come across!


Anonymous wrote on 2013-02-23 at 15:38:25:
This is very helpful. Thank you for such a nice post.


Clement wrote on 2012-10-17 at 14:51:44:
Making things simple to understand is an art. The author, probably is a great J2EE Artist. ;-).


Baylee wrote on 2012-03-28 at 23:51:26:
This is cool!


Post a Comment

(required):