Cześć,
od niedawna zacząłem programować w Java i spring boot.
Na razie jestem na etapie prostej stronki zużyciem Spring MVC i thymeleaf.
Podczas próby dodania do formularza pola z wyborem daty dostaje error:
2016-12-29 11:12:47.260 ERROR 9344 --- [nio-8080-exec-1] org.thymeleaf.TemplateEngine : [THYMELEAF][http-nio-8080-exec-1] Exception processing template "student_form": Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringInputGeneralFieldAttrProcessor' (student_form:19)
2016-12-29 11:12:47.270 ERROR 9344 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringInputGeneralFieldAttrProcessor' (student_form:19)] with root cause
org.springframework.beans.NotReadablePropertyException: Invalid property 'birthdate' of bean class [com.lama.Entity.Student]: Bean property 'birthdate' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
Formularz:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Add Student</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"></link>
</head>
<body>
<div th:include="components/header"></div>
<div class="container">
<h1>Form</h1>
<!-- -->
<form action="#" th:action="@{insert}" th:object="${student}" method="post">
<p>Id: <input type="text" th:field="*{id}" /><span style="color: red;" th:if="${#fields.hasErrors('id')}" th:errors="*{id}"></span></p>
<p>Name: <input type="text" th:field="*{name}" /><span style="color: red;" th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span></p>
<p>Course: <input type="text" th:field="*{course}" /><span style="color: red;" th:if="${#fields.hasErrors('course')}" th:errors="*{course}"></span></p>
<p>Email: <input type="text" th:field="*{email}" /><span style="color: red;" th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></span></p>
<p>Birthdate: <input type="text" th:field="*{{birthdate}}"/><span style="color: red;" th:if="${#fields.hasErrors('birthdate')}" th:errors="*{birthdate}"></span></p>
<p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
</form>
</div>
</body>
</html>
Kontroler:
@Controller
@RequestMapping("/students")
public class StudentController implements HandlerExceptionResolver {
@Autowired // no need to use new clause
private StudentService studentService;
@GetMapping( value={"/add","/insert"}) // students/add or /students/insert
public String getForm(Model model) {
model.addAttribute("student", new Student());
return "student_form";
}
@PostMapping( value={"/add","/insert"}) // students/add or /students/insert
public String submitForm(@Valid @ModelAttribute Student student, BindingResult results) {
if (results.hasErrors())
return "student_form";
this.studentService.insertStudent(student);
return "result";
}
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView mav = new ModelAndView("error");
mav.addObject("error", ex.getMessage());
mav.addObject("error2", ex.getStackTrace());
return mav;
}
}
DAO:
@Repository
@Qualifier("fakeData") //used to quick switch betwean databases. That in service you choose actual implementation of interface
public class FakeStudentDao implements StudentDao {
private static Map<Integer, Student> students;
static {
students = new HashMap<Integer, Student>(){
{
put(1, new Student(1,
"Roman Zioło",
"Kurs Gotowania",
"roman@wp.pl",
"14/01/1994"));
put(2, new Student(2,
"Zbigniew Kieł",
"Kurs 3",
"z.kiel@x.pl",
"22/05/1922"));
put(3, new Student(3,
"Tomasz Kowalewski",
"Kurs Y",
"none@none.com",
"30/11/2000"));
}
};
}
@Override
public Collection<Student> getAllStudents(){
return this.students.values();
}
@Override
public Student getStudentById(Integer id) {
return this.students.get(id);
}
@Override
public void removeStudentById(Integer id) {
this.students.remove(id);
}
@Override
public void updateStudent(Student s) {
Student student = students.get(s.getId());
student.setCourse(s.getCourse());
student.setName(s.getName());
this.students.put(student.getId().intValue(), student);
}
@Override
public void insertStudent(Student s) {
this.students.put(s.getId().intValue(), s);
}
}
Entity:
public class Student {
@NotNull
private Integer id;
@NotNull
@Size(min = 2, max = 30)
private String name;
@NotNull
@Size(min = 10, max = 255)
private String course;
@NotNull
@Email
private String email;
@DateTimeFormat(pattern = "dd/MM/yyyy")
@NotNull
@Past
private Date birthday;
public Student(Integer id, String name, String course, String email, String birthdate) {
this.id = id;
this.name = name;
this.course = course;
this.email = email;
setBirthdayFromStr(birthdate);
}
public Student(Integer id, String name, String course, String email, Date birthdate) {
this.id = id;
this.name = name;
this.course = course;
this.email = email;
this.birthday =birthdate;
}
public Student() {
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public String getCourse() {
return course;
}
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setCourse(String course) {
this.course = course;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public void setBirthdayFromStr(String birthday) {
SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
try {
this.birthday = formatter.parse(birthday);
} catch (ParseException e) {
e.printStackTrace();
}
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
Pomożecie w tej kwestii. Wszelkie próby googlowania póki co nie przyniosły efektu.