@ModelAttribute fields are null in Spring controller

I followed a tutorial to handle form submission in Spring. I wanted to populate my TaskEntity bean with the form and have this bean’s values available in my controller’s taskSubmit method. However, all of the properties of task in this method are null when I fill out the form and hit submit. The relevant code is as follows:

TaskEntity.java

public class TaskEntity {

    private String title;
    private String description;
    private Date dueDate;
    private TaskPriority priority;

    public void setTitle(String title) 
    {
        this.title = title;
    }

    public String getTitle() 
    {
        return this.title;
    }

    public void setDescription(String description) 
    {
        this.description = description;
    }


    public String getDescription() 
    {
        return this.description;
    }

    public void setDueDate(Date dueDate) 
    {
        this.dueDate = dueDate;
    }

    public Date getDueDate() 
    {
        return this.dueDate;
    }

    public void setPriority(String priority) 
    {
        this.priority = TaskPriority.valueOf(priority);
    }

    public TaskPriority getPriority() 
    {
        return this.priority;
    }
}

addTask.jsp

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>To Do List - Add Task</title>
    <link rel="stylesheet" type="text/css" target="_blank" rel="nofollow" href="${pageContext.request.contextPath}/css/stylesheet.css">
</head>
<body>
    <form action="tasks" th:action="@{/tasks}" th:object="${taskEntity}" method="post">
        <p>Title: <input type="text" th:field="*{title}" /></p>
        <p>Description: <input type="text" th:field="*{description}" /></p>
        <p>Due Date: <input type="text" th:field="*{dueDate}" /></p>
        <p>Priority: <input type="text" th:field="*{priority}" /></p>
        <p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
    </form>
</body>
</html>

TaskController.java

@Controller
public class TaskController {

    private static final String TASKS = "/tasks";
    private static final String ADD_TASK = "/addTask";

    @Autowired
    private TaskEntityDao taskEntityDao;

    ObjectMapper mapper = new ObjectMapper();

    @RequestMapping(value = TASKS, method = RequestMethod.GET)
    public ModelAndView readTasks() 
    {
        return new ModelAndView("tasks");
    }

    @RequestMapping(value = ADD_TASK, method = RequestMethod.GET)
    public String addTask(Model model)
    {
        model.addAttribute("taskEntity", new TaskEntity());
        return "addTask";
    }

    @RequestMapping(value = TASKS, method = RequestMethod.POST)
    public void taskSubmit(@ModelAttribute TaskEntity task)
    {
        // Fields for 'task' are all null!      
        taskEntityDao.createTask(task);

        readTasks();
    }
}

I expected the TaskEntity passed to the view with model.addAttribute(“taskEntity”, new TaskEntity()); was going to have the form values mapped to it’s fields, but I must have missed something.

Update:

I am adding the view resolver code from my Spring config:

<bean id="viewResolver"
    class="org.springframework.web.servlet.view.UrlBasedViewResolver">
    <property name="viewClass"
        value="org.springframework.web.servlet.view.JstlView" />
    <property name="prefix" value="/WEB-INF/jsp/" />
    <property name="suffix" value=".jsp" />
</bean>

With your edit you’ve confirmed that your view resolution is for JSPs.

You have a JSP page with <input> elements that look like

<input type="text" th:field="*{description}" />

Presumably th:field is a Thymeleaf element, but your page isn’t processed by Thymeleaf, it’s processed by the JSP Servlet. The JSP Servlet doesn’t care about th:field, it’s not special to it in any way. It just copies it literally. Furthermore, your input doesn’t have a name attribute. As such, when you submit the form, your browser doesn’t send a corresponding value. (Even if it did, it wouldn’t have a name that Spring MVC recognizes and can bind to your model attribute.)

Set up your MVC configuration to properly render Thymeleaf views instead of JSP views.

Or if you really want to use JSPs, use the corresponding technologies: EL, JSTL, and Spring’s tag library. There are tons of tutorials online, like this one.

You have a GET/POST for the same URL, in this case represented by TASKS = “/tasks”

Thus according with the following:

@RequestMapping(value = TASKS, method = RequestMethod.GET)
public ModelAndView readTasks() 
{
    return new ModelAndView("tasks");
}

Where was created the TaskEntity object?

You have model.addAttribute(“taskEntity”, new TaskEntity()); in the addTask method, but works with other URL, in this case ADD_TASK = “/addTask”.

You need understand that the web form is loaded according a model created through the GET event and then when the submit event happens (web form was filled) the POST event happens. In this scenario is normal that GET/POST (handled by @RequestMapping ) share the ‘same’ URL.

Observation: Thymeleaf does not work with a UrlBasedViewResolver, it has its own ViewResolver for Thymeleaf, furthermore, Thymeleaf works with a .html file, not with .jsp. Thus I am assuming the POST event is not working how should be expected for the Model.

Note: is a bad practice that a @Controller has a direct dependency with a @Repository, it should be for a @Service.

According with:

@RequestMapping(value = TASKS, method = RequestMethod.POST)
public void taskSubmit(@ModelAttribute TaskEntity task)
{
    // Fields for 'task' are all null!      
    taskEntityDao.createTask(task);

    readTasks();
}
  • For Fields for ‘task’ are all null!, how you are confirming that?
  • Consider in remove readTasks(). Is not very common that a @RequestMapping calls other @RequestMapping.