Spring MVC: Multiple Row Form Submit using List of Beans

Recently I had a requirement where using Spring MVC we had to take inputs multiple rows of data from user. The form had many rows which user can edit and submit. Spring MVC provides very simple yet elegant way of collecting data from multiple rows from HTML form and store them in List of Beans in Java.

Lets look at the requirement first. We have a screen where data for multiple Contacts is displayed. The Contact data is displayed in an HTML table. Each row in the table represents a single contact. Contact details consist of attributes such as Firstname, Lastname, Email and Phone number.

Related: Spring 3 MVC Tutorial Series (Must Read)

The Add Contact form would look like following:

Lets see the code behind this example.

Tools and Technologies used:

  1. Java 5 or above
  2. Eclipse 3.3 or above
  3. Spring MVC 3.0

Step 1: Create Project Structure

Open Eclipse and create a Dynamic Web Project.

Enter project name as SpringMVC_Multi_Row and press Finish.

Step 2: Copy Required JAR files

Once the Dynamic Web Project is created in Eclipse, copy the required JAR files under WEB-INF/lib folder. Following are the list of JAR files:

Step 3: Adding Spring MVC support

Once the basic project setup is done, we will add Spring 3 MVC support. For that first modify default web.xml and add springs DispatcherServlet.

File: /WebContent/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>Spring3MVC-Multi-Row</display-name> <servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> </web-app>
Code language: HTML, XML (xml)

Related: Tutorial: Learn Spring MVC Lifecycle

Now add spring-servlet.xml file under WEB-INF folder.

File: /WebContent/WEB-INF/spring-servlet.xml

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <context:annotation-config /> <context:component-scan base-package="net.viralpatel.spring3.controller" /> <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> </beans>
Code language: HTML, XML (xml)

Note that in above spring-servlet file, line 10, 11 defines context:annotation-config and component-scan tags. These tags let Spring MVC knows that the spring mvc annotations are used to map controllers and also the path from where the controller files needs to be loaded. All the files below package net.viralpatel.spring3.controller will be picked up and loaded by spring mvc.

Step 4: Add Spring Controller and Form classes

File: /src/net/viralpatel/spring3/form/Contact.java

package net.viralpatel.spring3.form; public class Contact { private String firstname; private String lastname; private String email; private String phone; public Contact() { } public Contact(String firstname, String lastname, String email, String phone) { this.firstname = firstname; this.lastname = lastname; this.email = email; this.phone = phone; } // Getter and Setter methods }
Code language: Java (java)

File: /src/net/viralpatel/spring3/form/ContactForm.java

package net.viralpatel.spring3.form; import java.util.List; public class ContactForm { private List<Contact> contacts; public List<Contact> getContacts() { return contacts; } public void setContacts(List<Contact> contacts) { this.contacts = contacts; } }
Code language: Java (java)

Note line 7 in above code how we have defined a List of bean Contact which will hold the multi-row data for each Contact.

File: /src/net/viralpatel/spring3/controller/ContactController.java

package net.viralpatel.spring3.controller; import java.util.ArrayList; import java.util.List; import net.viralpatel.spring3.form.Contact; import net.viralpatel.spring3.form.ContactForm; 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; import org.springframework.web.servlet.ModelAndView; @Controller public class ContactController { private static List<Contact> contacts = new ArrayList<Contact>(); static { contacts.add(new Contact("Barack", "Obama", "barack.o@whitehouse.com", "147-852-965")); contacts.add(new Contact("George", "Bush", "george.b@whitehouse.com", "785-985-652")); contacts.add(new Contact("Bill", "Clinton", "bill.c@whitehouse.com", "236-587-412")); contacts.add(new Contact("Ronald", "Reagan", "ronald.r@whitehouse.com", "369-852-452")); } @RequestMapping(value = "/get", method = RequestMethod.GET) public ModelAndView get() { ContactForm contactForm = new ContactForm(); contactForm.setContacts(contacts); return new ModelAndView("add_contact" , "contactForm", contactForm); } @RequestMapping(value = "/save", method = RequestMethod.POST) public ModelAndView save(@ModelAttribute("contactForm") ContactForm contactForm) { System.out.println(contactForm); System.out.println(contactForm.getContacts()); List<Contact> contacts = contactForm.getContacts(); if(null != contacts && contacts.size() > 0) { ContactController.contacts = contacts; for (Contact contact : contacts) { System.out.printf("%s \t %s \n", contact.getFirstname(), contact.getLastname()); } } return new ModelAndView("show_contact", "contactForm", contactForm); } }
Code language: Java (java)

In above ContactController class, we have defile two methods: get() and save().

get() method: This method is used to display Contact form with pre-populated values. Note we added a list of contacts (Contacts are initialize in static block) in ContactForm bean object and set this inside a ModelAndView object. The add_contact.jsp is displayed which in turns display all contacts in tabular form to edit.

save() method: This method is used to fetch contact data from the form submitted and save it in the static array. Also it renders show_contact.jsp file to display contacts in tabular form.

Step 5: Add JSP View files

Add following files under WebContent/WEB-INF/jsp/ directory.

File: /WebContent/WEB-INF/jsp/add_contact.jsp

<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%> <%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <html> <head> <title>Spring 3 MVC Multipe Row Submit - viralpatel.net</title> </head> <body> <h2>Spring MVC Multiple Row Form Submit example</h2> <form:form method="post" action="save.html" modelAttribute="contactForm"> <table> <tr> <th>No.</th> <th>Name</th> <th>Lastname</th> <th>Email</th> <th>Phone</th> </tr> <c:forEach items="${contactForm.contacts}" var="contact" varStatus="status"> <tr> <td align="center">${status.count}</td> <td><input name="contacts[${status.index}].firstname" value="${contact.firstname}"/></td> <td><input name="contacts[${status.index}].lastname" value="${contact.lastname}"/></td> <td><input name="contacts[${status.index}].email" value="${contact.email}"/></td> <td><input name="contacts[${status.index}].phone" value="${contact.phone}"/></td> </tr> </c:forEach> </table> <br/> <input type="submit" value="Save" /> </form:form> </body> </html>
Code language: HTML, XML (xml)

In above JSP file, we display contact details in a table. Also each attribute is displayed in a textbox. Note that modelAttribute=”contactForm” is defined in <form:form /> tag. This tag defines the modelAttribute name for Spring mapping. On form submission, Spring will parse the values from request and fill the ContactForm bean and pass it to the controller.

Also note how we defined textboxes name. It is in form contacts[i].a. Thus Spring knows that we want to display the List item with index i and its attribute a.

contacts[${status.index}].firstname will generate each rows as follows:

contacts[0].firstname // mapped to first item in contacts list
contacts[1].firstname // mapped to second item in contacts list
contacts[2].firstname // mapped to third item in contacts list

Spring 3 MVC and path attribute and square bracket

One thing here is worth noting that we haven’t used Spring’s tag to render textboxes. This is because Spring MVC 3 has a unique way of handling path attribute for tag. If we define the textbox as follows:

<form:input path="contacts[${status.index}].firstname" />
Code language: HTML, XML (xml)

Then instead of converting it to following HTML code:

<input name="contacts[0].firstname" /> <input name="contacts[1].firstname" /> <input name="contacts[2].firstname" />
Code language: HTML, XML (xml)

It converts it into following:

<input name="contacts0.firstname" /> <input name="contacts1.firstname" /> <input name="contacts2.firstname" />
Code language: HTML, XML (xml)

Note how it removed square brackets [ ] from name attribute. In previous versions of Spring (before 2.5) the square bracket were allowed in name attribute.

It seems w3c has later changed the HTML specification and removed [ ] from html input name.
Read the specification http://www.w3.org/TR/html4/types.html#type-name. It clearly says that:

ID and NAME tokens must begin with a letter ([A-Za-z]) and may be followed by any number of letters, digits ([0-9]), hyphens (“-“), underscores (“_”), colons (“:”), and periods (“.”).

Thus, square brackets aren’t allowed in name attribute! And thus Spring 3 onwards this was implemented.

So far I haven’t got any workaround to use springs <form:input /> tag instead of plain html <input /> to render and fetch data from multiple rows.

File: /WebContent/WEB-INF/jsp/show_contact.jsp

<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%> <%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <html> <head> <title>Spring 3 MVC Multipe Row Submit - viralpatel.net</title> </head> <body> <h2>Show Contacts</h2> <table width="50%"> <tr> <th>Name</th> <th>Lastname</th> <th>Email</th> <th>Phone</th> </tr> <c:forEach items="${contactForm.contacts}" var="contact" varStatus="status"> <tr> <td>${contact.firstname}</td> <td>${contact.lastname}</td> <td>${contact.email}</td> <td>${contact.phone}</td> </tr> </c:forEach> </table> <br/> <input type="button" value="Back" onclick="javascript:history.back()"/> </body> </html>
Code language: HTML, XML (xml)

File: /WebContent/index.jsp

<jsp:forward page="get.html"></jsp:forward>
Code language: HTML, XML (xml)

Final Project Structure

Once we have added all relevant source files and jar files, the project structure should look like following:

Step 6: Execute it

Execute the web application Right click on project > Run As > Run on Server.

Add Contact page

Show Contact page

Download Source Code

Spring-MVC-Multiple-Row-List-example.zip (2.9 MB)

View Comments

  • Viral,
    You have done a Good Job here.
    Can you do a tutorial on Apache Tiles to use with Spring MVC?
    Thanks for your help.

  • Hi Viral,

    The tutorial is very helpful but is there anything similar without annotations? We are using an older version of Spring Web MVC.

    Thanks
    Sree

  • The filename associated with the last code block should be show_contact.jsp, not add_contact.jsp (it is repeated from the block above)

  • Hi Vishal,

    Thanks for this great tutorial. I have similar kind of requirement but with one change.
    I need to allow use to add row using script. So what i did is i generate the input text boxes as u specified in the tutorial but its not working.

    Can you please guide me how should i achieve this?

    Thanks a lot,
    Ankur

  • Hi Viral.
    Thanks for this article. I am able to do al these thing with normal HTTP request. But my requirement is to do this with the help of DOJO(AJAX). Can you point me to some tutorial or can provide some help.

    Thanks

  • Hi Viral,
    Thanks for this awesome article. I have a question related to this. When I want to do a form validation, how can I specify a filed name in the validation class. For example, in this error.rejectValue ("fieldName", "errorMsgCode", "errorMessage") method, what is the right way to write the fieldName part? Thanks for your help in advance.

  • Hi Viral,
    Thanks very much for this article. It is well done and was quite helpful. I use a whitelist in my controllers to initialize the binder and found that I needed to use a wildcard to get the list to bind properly. As in "contacts*" in the following code:
    [code language="java"]
    @InitBinder public void initBinder(WebDataBinder binder) {
    // whitelist fields to bind
    log.debug("Defining whitelist fields");
    binder.setAllowedFields(new String[] {
    "firstName","lastName","email","phone","contacts*"
    });
    }
    [/code]

  • hi viral,
    thanks very much,this good article,detail explained for all concept and easily understand for all people include beginner.

  • hi i am only new to programing plus new to use a frame work i have choosen spring mvc as my first i have installed it sucessfully ...though i think so...can any one tell me where will i get those step 2 jar files from....? and in my server its showing the vmware one..... do i have to install apache or vmware one is ok....

Recent Posts

  • Java

Java URL Encoder/Decoder Example

Java URL Encoder/Decoder Example - In this tutorial we will see how to URL encode/decode…

4 years ago
  • General

How to Show Multiple Examples in OpenAPI Spec

Show Multiple Examples in OpenAPI - OpenAPI (aka Swagger) Specifications has become a defecto standard…

4 years ago
  • General

How to Run Local WordPress using Docker

Local WordPress using Docker - Running a local WordPress development environment is crucial for testing…

4 years ago
  • Java

Create and Validate JWT Token in Java using JJWT

1. JWT Token Overview JSON Web Token (JWT) is an open standard defines a compact…

4 years ago
  • Spring Boot

Spring Boot GraphQL Subscription Realtime API

GraphQL Subscription provides a great way of building real-time API. In this tutorial we will…

4 years ago
  • Spring Boot

Spring Boot DynamoDB Integration Test using Testcontainers

1. Overview Spring Boot Webflux DynamoDB Integration tests - In this tutorial we will see…

4 years ago