0

I read through the similar question and unfortunately didn't find a solution.
I am sending a Student entity as @ModelAttribute to my JSP, allowing user to edit it. When the Student is POST'ed back to the controller (StudentController) the fields rates and id are null. Null value for rates is OK, I pass student's rates as a separate @RequestParam. Other fields are OK. But id is unexpectedly null.
I am using Java 8, Spring 4.1, Tomcat 7.0.55, JSTL 1.2.

Here is a relevant part of my StudentController:

@Controller
@RequestMapping(value = "/student")
public class StudentController {

    @Autowired
    private StudentService studentService;
    @Autowired
    private FacultyService facultyService;

    @RequestMapping(value = "/edit/{id:.+}", method = RequestMethod.GET)
    public ModelAndView editStudentPage(@PathVariable Long id) {
        ModelAndView mav = new ModelAndView("student-edit");

        Student student = (id > 0)? studentService.findById(id) : new Student();
        mav.addObject("student", student);
        // or
        //mav.getModelMap().addAttribute(student);

        /* other objects omitted */

        return mav;
    }

    @RequestMapping(value = "/edit/{id:.+}", method = RequestMethod.POST)
    public ModelAndView editStudent(
            @ModelAttribute Student student,
            @RequestParam(value="rates", required = false) String ratesString,
            @PathVariable Long id,
            final RedirectAttributes redirectAttributes) throws StudentNotFoundException {
        ModelAndView mav = new ModelAndView("redirect:/student/list");

        student = studentService.update(student);
        student.updateRates(ratesString);

        String msg = "Student was successfully updated";
        redirectAttributes.addFlashAttribute("message", msg);
        return mav;
    }

    /* ... */
}

Here is a student-edit JSP file:

<jsp:useBean id="student" scope="request" class="by.naxa.demo.model.Student" />
<!-- I've tried to comment out the above line in order to avoid collision with `modelAttribute` (in hope that this was a culprit) -->
<%@page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c"    uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<c:url var="formAction" value="/student/edit/${student.id}" /> <!-- simplified -->

<!-- <head> omitted -->
<body>
<form:form action="${formAction}" method="POST" modelAttribute="student" >
    <form:hidden path="id" /> <!-- I've tried to specify `value="${student.id}"` here -->

    <div>
        <form:label path="name">Student's name:</form:label>
        <form:input path="name" required="true"/><br />
        <!-- other fields omitted -->

        <input type="submit" value="Save" />
        <!-- `Cancel` omitted -->
    </div>
</form:form>
</body>

I see that inputs on a generated page are populated with right values:

<input id="id" name="id" value="1" type="hidden" />
<input id="name" name="name" required="true" type="text" value="John Snow"/>

web.xml:

<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- Processes application requests -->
<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/app/servlet-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet>
    <servlet-name>jsp</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>/WEB-INF/views/*</url-pattern>
</servlet-mapping>

<welcome-file-list>
    <welcome-file />
</welcome-file-list>

ViewResolver in Spring servlet-context.xml:

<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
    <property name="prefix" value="/WEB-INF/views/" />
    <property name="suffix" value=".jsp" />
</bean>

Class Student extends AbstractNamedPersistable. Here they are (annotated with Lombok and JPA):

@Entity
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true, includeFieldNames = false)
@NamedQuery(name = "Student.findByTheStudentsName", query = "select s from Student s where s.name = ?1")
public @Data class Student extends AbstractNamedPersistable<Long> {
    /* all fields and `updateRates(String)` omitted */
}

@MappedSuperclass
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
@AllArgsConstructor
public abstract @Data class AbstractNamedPersistable<PK extends Serializable> extends AbstractPersistable<PK> {

    @Column(name = "Name", nullable = false, unique = true)
    private String name;

    @Override
    public String toString() {
        return getName();
    }
}

What could be the problem?

Community
  • 1
  • 1
naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
  • 1
    Please see http://stackoverflow.com/questions/29039116/how-to-partially-update-record-with-spring-mvc-and-use-post-redirect-get-patte – Neil McGuigan Mar 13 '15 at 18:15
  • @NeilMcGuigan, thank you, i've used a [`@SessionAttributes`](http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/SessionAttributes.html) to store `Student` in a session context, and [Converter](http://docs.spring.io/autorepo/docs/spring/current/javadoc-api/org/springframework/core/convert/converter/package-summary.html)s to convert `rates` between `List` (field) and `String` (input). – naXa stands with Ukraine Mar 18 '15 at 09:32

1 Answers1

0

This is an issue(bug) with Lombok I guess, when we use @NoArgsConstructor with a request which is attributed as @ModelAttribute, it overrides the request params with a default constructor as it is attributed with @ModelAttribute and not @RequestBody, so the JSON Body overrides the fields passed in query string . The way to avoid this is to remove @NoArgsConstructor annotation for request pojos annotated with @ModelAttribute.