Categories: springmvc

Spring 4 MVC Form Validation and Resource Handling (Annotations)

In this post we will learn about using Spring Form Tags, Form Validation using JSR-303 validation annotations, hibernate-validators, providing internationalization support using MessageSource and accessing static resources (e.g. CSS, javascript, images) in our views using ResourceHandlerRegistry, all using annotation-based configuration. Let’s get going.

we will create a simple application containing a student registration form, on form submission validating the user-input via JSR-303 validation annotations, overriding default messages using internationalized validation messages through properties files and also access the static resources (e.g. applying Bootstrap CSS to our pages).

Please note that JSR303 is a specification and hibernate-validator we are using in this post is an implementation, which also provides few of it’s own validation annotations not included in specification.


Following technologies being used:

  • Spring 4.0.6.RELEASE
  • validation-api 1.1.0.Final
  • hibernate-validator 5.1.2.Final
  • Bootstrap v3.1.0
  • Maven 3
  • JDK 1.6
  • Tomcat 7.0.54
  • Eclipse JUNO Service Release 2

Let’s begin.

Step 1: Create the directory structure

Following will be the final project structure:

Let’s now add the content mentioned in above structure explaining each in detail.

Step 2: Update pom.xml to include required dependencies

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
 xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

 <modelVersion>4.0.0</modelVersion>
 <groupId>com.websystique.springmvc</groupId>
 <artifactId>Spring4MVCFormValidationExample</artifactId>
 <packaging>war</packaging>
 <version>1.0.0</version>
 <name>Spring4MVCFormValidationExample</name>

 <properties>
  <springframework.version>4.0.6.RELEASE</springframework.version>
  <hibernate.validator.version>5.1.2.Final</hibernate.validator.version>
  <javax.validation.version>1.1.0.Final</javax.validation.version>
 </properties>

 <dependencies>
  <!-- Spring dependencies -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${springframework.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>${springframework.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>${springframework.version}</version>
  </dependency>

  <!-- jsr303 validation dependencies-->
  <dependency>
   <groupId>javax.validation</groupId>
   <artifactId>validation-api</artifactId>
   <version>${javax.validation.version}</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-validator</artifactId>
   <version>${hibernate.validator.version}</version>
  </dependency>

  <!-- Servlet dependencies -->
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>javax.servlet-api</artifactId>
   <version>3.1.0</version>
  </dependency>
  <dependency>
   <groupId>javax.servlet.jsp</groupId>
   <artifactId>javax.servlet.jsp-api</artifactId>
   <version>2.3.1</version>
  </dependency>
  <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
  </dependency>
 </dependencies>

 <build>
  <pluginManagement>
   <plugins>
    <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-compiler-plugin</artifactId>
     <version>3.2</version>
     <configuration>
      <source>1.6</source>
      <target>1.6</target>
     </configuration>
    </plugin>
    <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-war-plugin</artifactId>
     <version>2.4</version>
     <configuration>
      <warSourceDirectory>src/main/webapp</warSourceDirectory>
      <warName>Spring4MVCFormValidationExample</warName>
      <failOnMissingWebXml>false</failOnMissingWebXml>
     </configuration>
    </plugin>
   </plugins>
  </pluginManagement>
  <finalName>Spring4MVCFormValidationExample</finalName>
 </build>
</project>

First thing to notice here is the maven-war-plugin declaration. As we are using full annotation configuration, we don’t even include web.xml, so we will need to configure this plugin in order to avoid maven failure to build war package. On Validation part, validation-api represents the specification, while hibernate-validator is an implementation of this specification. hibernate-validator also provides few of it’s own annotations (@Email, @NotEmpty, etc..) which are not part of the specification.

Along with that, we have also included JSP/Servlet/Jstl dependencies which we will be needing as we are going to use servlet api’s and jstl view in our code. In general, containers might already contains these libraries, so we can set the scope as ‘provided’ for them in pom.xml.

In addition, I’ve also separately downloaded bootstrap.css just to demonstrate how to use resource handling in annotation based configuration.

Step 3: Create POJO / Domain Object

This domain object will acts as a backing bean to the form holding data user will provide via form submission. We will annotate the properties(with validation annotations) which we want to be validated.

com.websystique.springmvc.model.Student

package com.websystique.springmvc.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.format.annotation.DateTimeFormat;

public class Student implements Serializable {

 @Size(min=3, max=30)
 private String firstName;

 @Size(min=3, max=30)
 private String lastName;

 @NotEmpty
 private String sex;

 @DateTimeFormat(pattern="dd/MM/yyyy")
 @Past @NotNull
 private Date dob;

 @Email @NotEmpty
 private String email;

 @NotEmpty
 private String section;

 @NotEmpty
 private String country;

 private boolean firstAttempt;

 @NotEmpty
 private List<String> subjects = new ArrayList<String>();

 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 getSex() {
  return sex;
 }

 public void setSex(String sex) {
  this.sex = sex;
 }

 public Date getDob() {
  return dob;
 }

 public void setDob(Date dob) {
  this.dob = dob;
 }

 public String getEmail() {
  return email;
 }

 public void setEmail(String email) {
  this.email = email;
 }

 public String getSection() {
  return section;
 }

 public void setSection(String section) {
  this.section = section;
 }

 public String getCountry() {
  return country;
 }

 public void setCountry(String country) {
  this.country = country;
 }

 public boolean isFirstAttempt() {
  return firstAttempt;
 }

 public void setFirstAttempt(boolean firstAttempt) {
  this.firstAttempt = firstAttempt;
 }

 public List<String> getSubjects() {
  return subjects;
 }

 public void setSubjects(List<String> subjects) {
  this.subjects = subjects;
 }

 @Override
 public String toString() {
  return "Student [firstName=" + firstName + ", lastName=" + lastName
    + ", sex=" + sex + ", dob=" + dob + ", email=" + email
    + ", section=" + section + ", country=" + country
    + ", firstAttempt=" + firstAttempt + ", subjects=" + subjects
    + "]";
 }

}

In above code, @Size, @Past & @NotNull are standard annotations while @NotEmpty & @Email are not part of specification.

Step 4: Add controller

Add the controller which will serve the GET and POST request.

com.websystique.springmvc.controller.HelloWorldController

package com.websystique.springmvc.controller;

import java.util.ArrayList;
import java.util.List;

import javax.validation.Valid;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.websystique.springmvc.model.Student;

@Controller
@RequestMapping("/")
public class HelloWorldController {

 /*
  * This method will serve as default GET handler.
  *
  */ @RequestMapping(method = RequestMethod.GET)
 public String newRegistration(ModelMap model) {
  Student student = new Student();
  model.addAttribute("student", student);
  return "enroll";
 }

 /*
  * This method will be called on form submission, handling POST request
  * It also validates the user input
  */ @RequestMapping(method = RequestMethod.POST)
 public String saveRegistration(@Valid Student student, BindingResult result, ModelMap model){

  if(result.hasErrors()) {
          return "enroll";
  }

  model.addAttribute("success", "Dear "+ student.getFirstName()+" , your Registration completed successfully");
  return "success";
 }

 /*
  * Method used to populate the Section list in view.
  * Note that here you can call external systems to provide real data.
  */ @ModelAttribute("sections")
 public List<String> initializeSections() {

  List<String> sections = new ArrayList<String>();
  sections.add("Graduate");
  sections.add("Post Graduate");
  sections.add("Research");
   return sections;
 }

 /*
  * Method used to populate the country list in view.
  * Note that here you can call external systems to provide real data.
  */ @ModelAttribute("countries")
 public List<String> initializeCountries() {

  List<String> countries = new ArrayList<String>();
  countries.add("USA");
  countries.add("CANADA");
  countries.add("FRANCE");
  countries.add("GERMANY");
  countries.add("ITALY");
  countries.add("OTHER");
   return countries;
 }

 /*
  * Method used to populate the subjects list in view.
  * Note that here you can call external systems to provide real data.
  */ @ModelAttribute("subjects")
 public List<String> initializeSubjects() {

  List<String> subjects = new ArrayList<String>();
  subjects.add("Physics");
  subjects.add("Chemistry");
  subjects.add("Life Science");
  subjects.add("Political Science");
  subjects.add("Computer Science");
  subjects.add("Mathmatics");
   return subjects;
 }

}

@Controller indicates that this class is a controller handling the requests with pattern mapped by @RequestMapping.Here with ‘/’, it is serving as default controller. Method newRegistration is fairly simple, annotated with @RequestMethod.GET serving default GET requests, adding the model object to serve as data-holder of form , and presenting the page containing the blank form.

Method initializeSections, initializeCountries & initializeSubjects are simply creating request level objects whose values will be used in view/jsp.

Method saveRegistration is annotated with @RequestMethod.POST, and will handle the form-submission POST requests.Notice the parameters and their orders in this method. @Valid asks spring to validate the associated object(student). BindingResult contains the outcome of this validation and any error that might have occurred during this validation. Notice that BindingResult must come right after the validated object else spring won’t be able to validate and an exception been thrown.

Note that in case of validation failure, default/ generalized error messages are shown on screen which may not be desirable. Instead, you can override this behavior providing internationalized messages specific to each field. To do that, we need to configure MessageSource in application configuration class and provide properties files containing actual messages which we will do next.

Step 5: Add Configuration Class

com.websystique.springmvc.configuration.HelloWorldConfiguration

package com.websystique.springmvc.configuration;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.websystique.springmvc")
public class HelloWorldConfiguration extends WebMvcConfigurerAdapter {

 /*
  * Configure View Resolver
  */ @Bean
 public ViewResolver viewResolver() {
  InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
  viewResolver.setViewClass(JstlView.class);
  viewResolver.setPrefix("/WEB-INF/views/");
  viewResolver.setSuffix(".jsp");
  return viewResolver;
 }

 /*
  * Configure ResourceHandlers to serve static resources like CSS/ Javascript etc...
  *
  */ @Override
 public void addResourceHandlers(ResourceHandlerRegistry registry) {
     registry.addResourceHandler("/static/**").addResourceLocations("/static/");
 }

 /*
  * Configure MessageSource to provide internationalized messages
  *
  */
 @Bean
 public MessageSource messageSource() {
     ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
     messageSource.setBasename("messages");
     return messageSource;
 }

}

@Configuration indicates that this class contains one or more bean methods annotated with @Bean producing bean manageable by spring container.@EnableWebMvc is equivalent to mvc:annotation-driven in XML. It enables support for @Controller-annotated classes that use @RequestMapping to map incoming requests to specific method. @ComponentScan is equivalent to context:component-scan base-package="..." providing with where to look for spring managed beans/classes.

Method viewResolver configures a view resolver to identify the real view. Method addResourceHandlers configures the ResourceHandler for static resources. CSS, JavaScript, images etc are static resources your pages needs.Above configuration says that all resource requests starting with /static/ will be served from /static/ folder under webapp. In this example, we will put all the css files under /static/css inside webapp directory. Note that this method is defined in WebMvcConfigurerAdapter so we needed to extend this class to override this method in order to register our static resources.

Method messageSource configures a Message bundle to support [internationalized] messages from properties file. Notice the parameter provided (messages) to baseName method. Spring will search for a file named messages.properties in application class path. Let’s add that file:

src/main/resources/messages.properties

Size.student.firstName=First Name must be between {2} and {1} characters long
Size.student.lastName=Last Name must be between {2} and {1} characters long
NotEmpty.student.sex=Please specify your gender
NotNull.student.dob=Date of birth can not be blank
Past.student.dob=Date of birth must be in the past
Email.student.email=Please provide a valid Email address
NotEmpty.student.email=Email can not be blank
NotEmpty.student.country=Please select your country
NotEmpty.student.section=Please select your section
NotEmpty.student.subjects=Please select at least one subject
typeMismatch=Invalid format

Notice that above message follows a specific pattern

{ValidationAnnotationClass}.{modelObject}.{fieldName}

Additionally, based on specific annotation (e.g. @Size) you can also pass the arguments to these messages using {0},{1},..{i}

Above configuration in XML format will be

<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"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
 
    <context:component-scan base-package="com.websystique.springmvc" />
    <mvc:annotation-driven/>

    <mvc:resources mapping="/static/**" location="/static/" />
    <mvc:default-servlet-handler />


    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename">
            <value>messages</value>
        </property>
    </bean>


    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix">
            <value>/WEB-INF/views/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>
 
</beans>

Step 6: Add Views (Simple JSP Pages)

We will add two simple jsp pages. First one will contain a Form to receive input from user, and second one will show the success message to user once form input is validated successfully.

Below is the peace of code used to include static resources(bootstrap.css in our case)

<link href="<c:url value='/static/css/bootstrap.css' />" rel="stylesheet"></link>

Notice the path to static resource. Since we have already configured ResourceHandlers in previous Step with /static/**, css file will be searched inside /static/ folder.

Complete JSP Files are shown below :

WEB-INF/views/enroll.jsp

&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=ISO-8859-1&quot;
    pageEncoding=&quot;ISO-8859-1&quot;%&gt;
&lt;%@ taglib prefix=&quot;form&quot; uri=&quot;http://www.springframework.org/tags/form&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;

&lt;html&gt;

&lt;head&gt;
 &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=ISO-8859-1&quot;&gt;
 &lt;title&gt;Student Enrollment Form&lt;/title&gt;
 &lt;link href=&quot;&lt;c:url value='/static/css/bootstrap.css' /&gt;&quot; rel=&quot;stylesheet&quot;&gt;&lt;/link&gt;
 &lt;link href=&quot;&lt;c:url value='/static/css/custom.css' /&gt;&quot; rel=&quot;stylesheet&quot;&gt;&lt;/link&gt;
&lt;/head&gt;

&lt;body&gt;

  &lt;div class=&quot;form-container&quot;&gt;
  
  &lt;h1&gt;Enrollment Form&lt;/h1&gt;
  
 &lt;form:form method=&quot;POST&quot; modelAttribute=&quot;student&quot; class=&quot;form-horizontal&quot;&gt;

  &lt;div class=&quot;row&quot;&gt;
   &lt;div class=&quot;form-group col-md-12&quot;&gt;
    &lt;label class=&quot;col-md-3 control-lable&quot; >WEB-INF/views/success.jsp</code>

&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=ISO-8859-1&quot;
    pageEncoding=&quot;ISO-8859-1&quot;%&gt;
&lt;%@ taglib prefix=&quot;c&quot; uri=&quot;http://java.sun.com/jsp/jstl/core&quot; %&gt;
&lt;html&gt;
&lt;head&gt;
 &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=ISO-8859-1&quot;&gt;
 &lt;title&gt;Student Enrollment Detail Confirmation&lt;/title&gt;
 &lt;link href=&quot;&lt;c:url value='/static/css/custom.css' /&gt;&quot; rel=&quot;stylesheet&quot;&gt;&lt;/link&gt;
&lt;/head&gt;
&lt;body&gt;
 &lt;div class=&quot;success&quot;&gt;
  Confirmation message : ${success}
  &lt;br&gt;
  We have also sent you a confirmation mail to your email address : ${student.email}.
 &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;

Step 7: Add Initializer class

com.websystique.springmvc.configuration.HelloWorldInitializer

package com.websystique.springmvc.configuration;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class HelloWorldInitializer implements WebApplicationInitializer {

 public void onStartup(ServletContext container) throws ServletException {

  AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
  ctx.register(HelloWorldConfiguration.class);
  ctx.setServletContext(container);

  ServletRegistration.Dynamic servlet = container.addServlet(
    "dispatcher", new DispatcherServlet(ctx));

  servlet.setLoadOnStartup(1);
  servlet.addMapping("/");
 }

}

The content above resembles the content of web.xml from previous tutorials as we are using the front-controller DispatherServler, assigning the mapping (url-pattern in xml) and instead of providing the path to spring configuration file(spring-servlet.xml) , here we are registering the Configuration Class.

UPDATE: Note that above class can be written even more concisely [and it’s the preferred way], by extending AbstractAnnotationConfigDispatcherServletInitializer base class, as shown below:

package com.websystique.springmvc.configuration;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class HelloWorldInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

 @Override
 protected Class<?>[] getRootConfigClasses() {
  return new Class[] { HelloWorldConfiguration.class };
 }
 
 @Override
 protected Class<?>[] getServletConfigClasses() {
  return null;
 }
 
 @Override
 protected String[] getServletMappings() {
  return new String[] { "/" };
 }

}

Step 8: Build and Deploy the application

One thing to keep in mind that the Spring java based configuration api’s like WebApplicationInitializer depends on Servlet 3.0 containers.So make sure you don’t have any web.xml with servlet declaration less than 3.0. For our case, we have removed web.xml file from our application.

Now build the war (either by eclipse as was mentioned in last tutorial) or via maven command line( mvn clean install). Deploy the war to a Servlet 3.0 container . Since here i am using Tomcat, i will simply put this war file into tomcat webapps folder and click on start.bat inside tomcat bin directory.

Run the application

You get the initial page like below :

Now if you try to submit, you will get validation errors (with our user-defined message in message.properties)

Now Provide your sample inputs

Submit the form now:

That’s it.

Download Source Code

References

View Comments

  • Hi Dear,

    I have written below code but i am not getting properties file message.Please guide me.
    ---------AppConfig.java-------

    @EnableWebMvc

    @ComponentScan(basePackages ={"com.shiva.FormValidator"})

    @Configuration
    public class AppConfig {

    @Bean
    public InternalResourceViewResolver getInternalResourceViewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/WEB-INF/");
    resolver.setSuffix(".jsp");
    return resolver;
    }
    @Bean
    public MessageSource messageSource() {

    ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    messageSource.setBasename("/MVCFormValidation1/WebContent/WEB-INF/messages");

    return messageSource;

    }
    }

    --------StudentController.java--------

    @Controller
    public class StudentFormController {
    @InitBinder
    public void customizeBinding (WebDataBinder binder) {
    SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
    dateFormatter.setLenient(false);
    binder.setDisallowedFields(new String[]{"roll"});
    binder.registerCustomEditor(Date.class, "dob",new CustomDateEditor(dateFormatter, true));
    binder.registerCustomEditor(String.class, "name",new StudentPropertyEditor());
    }

    @RequestMapping("/student")
    public ModelAndView handleStudentForm()
    {
    ModelAndView mv=new ModelAndView("Student");
    return mv;
    }
    @RequestMapping("MVCFormValidation1/studentDetails")
    public ModelAndView handleDtudentDetails(@Valid@ModelAttribute("student1") Student student1,BindingResult result)
    {

    if(result.hasErrors())
    {
    ModelAndView mv=new ModelAndView("Student");
    return mv;
    }
    ModelAndView mv=new ModelAndView("StudentDetails");
    return mv;

    }

    }

    ------messages.properties--------

    Size.student1.name=Name length must be in 2 to 10 charecter

    Thanks,
    Prabhat

  • Hi,
    Can you please do an example using Spring MVC and angularJS when the form fields are being populated through a database? Thanks!

  • Hi There,
    I downloaded the source code, tried running it using Tomcat. I am getting: HTTP Status 404 - /Spring4MVCFormValidationExample/

    type Status report

    message /Spring4MVCFormValidationExample/

    description The requested resource is not available.

    --------------------------------------------------------------------------------

    Apache Tomcat/9.0.0.M17

    The source code doesn't cme with web.xml and spring-servlet.xml as well. Do we create it?

    • Hi, Saurabh, are you sure you are running the example from this post[ assuming you changed the name of your app]? There is nowhere a mapping returning 'Hello World'.

        • Hi Saurabh,
          If you haven't yet been able to figure out the issue, then please check your server's web.xml file. You probably have the welcome page set to index.jsp. Try commenting out this setting.

          Shiji

  • I just downloaded this app and trying to deploy in tomcat but received an error.

    86:
    87: Section
    88:
    89:
    90:
    91:
    92:

    Stacktrace:] with root cause
    java.lang.IllegalArgumentException: Attribute 'items' must be an array, a Collection or a Map

    Hope you can let me know what the issue is.

  • Hi websys
    Is a good a approach to use the 'Student' class that you are using in the form, to store in the database like an entity? If no, what is the best approach?

    • Hi Guilherme, it is certainly not preferable to expose your entities to view. Common approach to avoid this issue is to create the view objects [StudentView pojo, resembling your entities] which you can annotate with Spring's built-in validators for instance), use it on view, and then on save/update, copy the values from this view object into a real entity object [Student] and then call your DAO methods. It does create an extra layer but it also saves you from putting entities into UI. You can create set of mappers for your projects using Guava Function Interface, comes handy while copying property values of one class into another.

      Hope this helps.

      • I code the Guava Function inside the Student pojo to don't expose this function to the view layer, or I code this on StudentView? Which is the best approach?

        • Hi Guilherme, Guava Function interface i mentioned above is just a helper to copy values from one class properties into another. You may create bunch of helper classes for your project. Something like this.

          package com.yourpacage.ui.converter;

          import com.google.common.base.Function;

          public abstract class AbstractConverterFunction implements Function{

          }

          package com.yourpacage.ui.converter;

          import com.yourpacage.model.Student;

          import com.yourpacage.ui.to.StudentView;

          public class ToStudentViewFunction extends AbstractConverterFunction{

          @Override

          public StudentView apply(Student entity) {

          if(entity == null){

          return null;

          }

          StudentView view = new StudentView();

          view.setId(entity.getId());

          view.setName(entity.getName());

          return view;

          }

          }

          Now, this ToStudentViewFunction class should be used in your service class. To keep things clean, you can create an extra service layer which takes care of this translation.

          package com.yourpackage.ui.toservice;

          import java.util.Arrays;

          import java.util.List;

          import static com.google.common.collect.Lists.newArrayList;

          import com.google.common.collect.Lists;

          @Service("studentViewService")

          @Transactional

          public class StudentViewServiceImpl implements StudentViewService{

          @Autowired

          StudentService studentService;

          private ToStudentViewFunction viewFunction = new ToStudentViewFunction();

          @Override

          public StudentView findById(Integer id) {

          return viewFunction.apply(studentService.findById(id));

          }

          @Override

          public void create(StudentView studentView) {

          if(studentView!= null){

          Student student = new Student();

          student.setName(studentView.getName());

          studentService.persist(student);

          studentView.setId(student.getId());

          }

          }

          @Override

          public void update(StudentView studentView) {

          if(studentView!= null){

          Student student = studentService.findById(studentView.getId());

          student.setName(studentView.getName());

          }

          }

          }

          Then in controller, instead of injecting StudentService class, you should be injecting StudentViewService.

          Hope it clears your doubts.

          • Thanks for the reply websys. I will try this now and will let you know!
            One question that I have, its best approach to validate the forms and data on front-end before send to server, validate on server, or both ?

          • Hi Guilherme, Recommended approach is to use both client-side and server side validation.You can't simply rely only on client side validation.

  • Nice article. I am trying to validation inside the modal window using annotation, window always closed before validation . Do you have any suggestion ?

    • Hi Kalpana,

      This example don't have the same setup like your particular example but i suppose you may intercept the form submission and skip the default behavior (which closes the form). And then in your error handler (which will be the case as there were server side validation error), display message to client, while in success handler, just simply close the modal window. A good link doing something similar can be found here.

      If it does not help, you may want to share some runable code so that we can try to fix the issue together.

Share
Published by

Recent Posts

Spring Boot + AngularJS + Spring Data + JPA CRUD App Example

In this post we will be developing a full-blown CRUD application using Spring Boot, AngularJS, Spring Data, JPA/Hibernate and MySQL,…

7 years ago

Spring Boot Rest API Example

Spring Boot complements Spring REST support by providing default dependencies/converters out of the box. Writing RESTful services in Spring Boot…

7 years ago

Spring Boot WAR deployment example

Being able to start the application as standalone jar is great, but sometimes it might not be possible to run…

7 years ago

Spring Boot Introduction + hello world example

Spring framework has taken the software development industry by storm. Dependency Injection, rock solid MVC framework, Transaction management, messaging support,…

7 years ago

Secure Spring REST API using OAuth2

Let's secure our Spring REST API using OAuth2 this time, a simple guide showing what is required to secure a…

8 years ago

AngularJS+Spring Security using Basic Authentication

This post shows how an AngularJS application can consume a REST API which is secured with Basic authentication using Spring…

8 years ago