如何通过Spring Boot实现HTML表单日期时间输入并保存至MySQL数据库
问题分析
你遇到的核心问题是Spring MVC无法自动将表单提交的日期字符串绑定到java.sql.Date类型的字段,报错提示的"Bean property 'date' is not readable or has an invalid getter method"本质上是类型转换失败导致的——表单提交的是字符串,而Spring默认不知道怎么把它转成java.sql.Date。你的临时字符串转sql.Date方法虽然可行,但确实不够优雅,下面给你两个更符合Spring生态的解决方案:
方案一:使用@DateTimeFormat注解配合Java 8日期类型(推荐)
Spring支持直接绑定Java 8的java.time.LocalDate/LocalTime类型,只需要在实体类字段上加上@DateTimeFormat注解指定格式,就能自动完成表单字符串到日期类型的转换,不需要手动写转换方法。
1. 修改实体类Post.java
把java.sql.Date换成LocalDate,LocalTime保留,同时添加@DateTimeFormat注解,还可以通过@PrePersist自动合并日期和时间到数据库的TIMESTAMP字段:
@Entity public class Post { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; private String user; private String text; // 绑定日期字段,指定表单提交的格式 @DateTimeFormat(pattern = "yyyy-MM-dd") private LocalDate date; // 绑定时间字段,默认格式支持HH:mm,无需额外指定 private LocalTime time; // 数据库存储的合并时间戳 @Temporal(TemporalType.TIMESTAMP) private java.util.Date myDate; // 持久化前自动合并date和time到myDate @PrePersist public void mergeDateTime() { if (date != null && time != null) { LocalDateTime localDateTime = LocalDateTime.of(date, time); this.myDate = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); } } // 必须保证getter/setter正确生成,字段名和表单name一致 public LocalDate getDate() { return date; } public void setDate(LocalDate date) { this.date = date; } public LocalTime getTime() { return time; } public void setTime(LocalTime time) { this.time = time; } // 其他getter/setter... }
2. 保持表单createPost.html不变
表单中的date和time字段的th:field可以直接对应实体类的字段:
<form action="#" th:action="@{/createPost}" th:object="${post}" method="POST"> <p>Description: <input type="text" th:field="*{text}" /> </p> <p>date: <input type="date" th:field="*{date}" /></p> <p>time: <input type="time" th:field="*{time}" /></p> <p><input class="submitButton" type="submit" value="Submit" /> </p> </form>
3. 简化控制器PostController.java
不需要手动处理日期转换,Spring会自动完成绑定:
@RequestMapping(value="/createPost", method=RequestMethod.POST) public String createPost(@ModelAttribute Post post){ Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); String currentUser = authentication.getName(); post.setUser(currentUser); // @PrePersist会自动处理日期时间合并 postRepository.save(post); return "redirect:/"; }
方案二:全局配置日期时间转换器
如果你的应用中有很多地方需要处理日期绑定,可以配置一个全局的Converter,这样所有的String到日期类型的转换都会自动生效,不需要每个字段加注解。
1. 编写全局转换器配置类
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addFormatters(FormatterRegistry registry) { // 转换String到LocalDate registry.addConverter(new Converter<String, LocalDate>() { @Override public LocalDate convert(String source) { return LocalDate.parse(source, DateTimeFormatter.ISO_LOCAL_DATE); } }); // 如果仍需使用java.sql.Date,添加这个转换器 registry.addConverter(new Converter<String, java.sql.Date>() { @Override public java.sql.Date convert(String source) { LocalDate localDate = LocalDate.parse(source); return java.sql.Date.valueOf(localDate); } }); // 转换String到LocalTime registry.addConverter(new Converter<String, LocalTime>() { @Override public LocalTime convert(String source) { return LocalTime.parse(source, DateTimeFormatter.ISO_LOCAL_TIME); } }); } }
2. 实体类和控制器无需额外修改
实体类可以保留java.sql.Date或者用LocalDate,控制器直接用@ModelAttribute绑定即可,表单也保持原来的样子。
为什么临时方案不够优雅?
- 手动处理字符串转换需要写重复代码,容易出现格式错误(比如时区、格式不匹配)
- 直接接收字符串参数然后转换,破坏了
@ModelAttribute的封装性,代码不够整洁 - 没有利用Spring内置的类型转换机制,增加了维护成本
以上两个方案都是Spring官方推荐的方式,既能解决绑定问题,又能让代码更简洁易维护。
内容的提问来源于stack exchange,提问作者Rem

