Spring Boot+Kotlin接口报错:未传createdAt致JSON解析失败
近期学习用Kotlin和Spring Boot构建REST API,创建领域模型与控制器接口时遇到问题,相关代码与表结构如下:
模型代码(/model/Country.kt)
package com.example.example.model import jakarta.persistence.Column import jakarta.persistence.Entity import jakarta.persistence.EntityListeners import jakarta.persistence.GeneratedValue import jakarta.persistence.GenerationType import jakarta.persistence.Id import jakarta.persistence.Table import org.springframework.data.annotation.CreatedDate import org.springframework.data.annotation.LastModifiedDate import org.springframework.data.jpa.domain.support.AuditingEntityListener import java.sql.Timestamp @Entity @Table(name = "country") class Country ( @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "country_id") val id: Int, val code: String, val displayName: String, @CreatedDate @Column(name = "created_at", nullable = false, updatable = false) val createdAt: Timestamp, @LastModifiedDate @Column(name = "updated_at", nullable = false) val updatedAt: Timestamp )
控制器代码(/controller/CountryController.kt)
package com.example.example.controller import com.example.example.model.Country import com.example.example.repository.CountryRepository import jakarta.validation.Valid import org.springframework.beans.factory.annotation.Autowired import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RestController import org.springframework.http.MediaType import org.springframework.web.bind.annotation.PostMapping @RestController class CountryController { @Autowired lateinit var countryRepository: CountryRepository @PostMapping("/countries", consumes = arrayOf(MediaType.APPLICATION_JSON_VALUE)) fun createCountry(@Valid @RequestBody country: Country): Country { return countryRepository.save(country); } }
Country表结构
country_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, code VARCHAR(3) NOT NULL, displayName VARCHAR(40) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (country_id)
错误情况
运行Spring Boot应用后,用Postman调用POST /countries接口时收到400 Bad Request错误,日志提示:
2023-01-12T14:09:15.423Z WARN 90037 --- [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Instantiation of [simple type, class com.example.example.model.Country] value failed for JSON property createdAt due to missing (therefore NULL) value for creator parameter createdAt which is a non-nullable type]
请求体仅包含{"code": "FRA", "displayName": "France"},请求头Content-Type设置正确。曾尝试给createdAt和updatedAt添加@JsonIgnore注解,仍返回相同错误。需求是让这两个字段由Spring Boot调用仓库方法或MySQL自动填充,无需请求体传入。
1. 调整实体类的构造器参数配置
Kotlin主构造器参数默认不可空,Jackson反序列化时会要求JSON包含所有参数值,但id、createdAt、updatedAt属于框架/数据库自动生成字段,无需请求传入。需将这些参数改为可空类型并设置默认值,同时通过Jackson注解控制序列化规则:
修改后的Country类:
package com.example.example.model import com.fasterxml.jackson.annotation.JsonIgnoreProperties import jakarta.persistence.Column import jakarta.persistence.Entity import jakarta.persistence.EntityListeners import jakarta.persistence.GeneratedValue import jakarta.persistence.GenerationType import jakarta.persistence.Id import jakarta.persistence.Table import org.springframework.data.annotation.CreatedDate import org.springframework.data.annotation.LastModifiedDate import org.springframework.data.jpa.domain.support.AuditingEntityListener import java.sql.Timestamp @Entity @Table(name = "country") @JsonIgnoreProperties(value = ["createdAt", "updatedAt"], allowGetters = true) class Country ( @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "country_id") val id: Int? = null, val code: String, val displayName: String, @CreatedDate @Column(name = "created_at", nullable = false, updatable = false) val createdAt: Timestamp? = null, @LastModifiedDate @Column(name = "updated_at", nullable = false) val updatedAt: Timestamp? = null )
@JsonIgnoreProperties(value = ["createdAt", "updatedAt"], allowGetters = true):告知Jackson反序列化时忽略这两个字段(不要求JSON传入),但序列化返回响应时保留它们。- 给
id、createdAt、updatedAt设置可空类型和默认值null,让Jackson能通过Kotlin自动生成的可选参数构造器实例化对象。
2. 启用Spring Data JPA审计功能
要让@CreatedDate和@LastModifiedDate自动生效,需在Spring Boot启动类上添加@EnableJpaAuditing注解:
package com.example.example import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.data.jpa.repository.config.EnableJpaAuditing @SpringBootApplication @EnableJpaAuditing class ExampleApplication fun main(args: Array<String>) { runApplication<ExampleApplication>(*args) }
该注解会激活审计机制,自动填充标注的时间字段。
3. 可选:依赖MySQL默认值填充(替代方案)
如果希望完全由数据库处理时间字段,可以调整实体类配置,让JPA不干预这些字段的生成:
- 移除
@CreatedDate和@LastModifiedDate注解 - 设置字段的
insertable = false和updatable = false,告知JPA插入/更新时不处理这些字段,交由数据库默认值处理
修改后的时间字段部分:
@Column(name = "created_at", nullable = false, updatable = false, insertable = false) val createdAt: Timestamp? = null, @Column(name = "updated_at", nullable = false, insertable = false, updatable = false) val updatedAt: Timestamp? = null
这种方式无需启用@EnableJpaAuditing,完全依赖数据库的默认值和更新触发器。
4. 验证效果
重新启动应用后,用Postman发送POST /countries请求,请求体为{"code": "FRA", "displayName": "France"},接口应返回200 OK,响应体中会包含自动填充的id、createdAt和updatedAt字段。
内容的提问来源于stack exchange,提问作者Dexter Fung

