I am new to Spring Boot with Kotlin and I am trying to implement a simple REST API with a MySQL database in the background. I started by creating a "TestEntity" class with a "TestEntityRepository", "TestEntityResource" and "TestEntityService". Everything worked fine until this point. I was able to POST and GET the data via the REST API.
After that I created another Entity "User" for which I wanted to do the same as for my TestEntity. I created the class with some more fields followed by the Repository, Resource and Service classes. Now, when I try to POST a new entry to my UserResource REST endpoint I get a 400 Bad Request error with the following message:
Field error in object 'user' on field 'id': rejected value [null]; codes [typeMismatch.user.id,typeMismatch.id,typeMismatch.long,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.id,id]; arguments []; default message [id]]; default message [Failed to convert value of type 'null' to required type 'long'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [null] to type [@javax.persistence.Id @javax.persistence.GeneratedValue long] for value 'null'; nested exception is java.lang.IllegalArgumentException: A null value cannot be assigned to a primitive type]]
My User class looks like the following:
@Entity
class User(
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
val id: Long,
@Column(name = "login_id", unique = true, nullable = false)
val loginId: String,
@Column(name = "display_name")
val displayName: String,
@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_date")
val createdDate: Date,
@UpdateTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "modify_date")
val modifyDate: Date
)
The repository class looks like this:
@Repository
interface UserRepository: JpaRepository<User, Long>
The service class like this:
@Service
class UserService(val repo: UserRepository) {
fun findAll(): List<User> = repo.findAll()
fun findById(userId: Long): User = repo.findById(userId)
.orElseThrow { ResponseStatusException(HttpStatus.NOT_FOUND, "No user was found with this ID.") }
fun post(user: User) {
repo.save(user)
}
fun delete(user: User) {
repo.delete(user)
}
}
And finally my resource class (RestController) like this:
@RestController
class UserResource(val service: UserService) {
@GetMapping("/user")
fun get(): List<User> = service.findAll()
@GetMapping("/user/{id}")
fun get(@PathVariable(name = "id") id: Long): User = service.findById(id)
@PostMapping("/user")
fun post(user: User) {
service.post(user)
}
@DeleteMapping("/user")
fun delete(user: User) = service.delete(user)
}
Does anybody may have an idea what is going wrong? I am just sending a HTTP POST with a JSON object that contains the fields "loginId" and "displayName". The "id" field should be filled automatically but it does not work. Also when I add a field called "id" to my JSON the request will not be accepted.
Thanks!
CodePudding user response:
- Make sure
auto_increement
is set toid
column in your table. - Use Autogeneration Strategy as
GenerationType.IDENTITY
.
CodePudding user response:
Okay I found a way to fix it but I am not sure if its the best way to do.
In my entity class I had to declare the createdDate and modifyDate fields as nullable:
@Entity
class User(
@Id
@GeneratedValue
var id: Long,
@Column(name = "login_id", unique = true, nullable = false)
var loginId: String,
@GeneratedValue(generator = APITokenGenerator.GENERATOR_NAME)
@GenericGenerator(name = APITokenGenerator.GENERATOR_NAME, strategy = "my.package.name.APITokenGenerator")
@Column(name = "api_token")
var apiToken: String? = null,
@Column(name = "display_name")
var displayName: String,
@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_date")
var createdDate: Date? = null,
@UpdateTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "modify_date")
var modifyDate: Date? = null
)
But I think the most important thing was to add the @RequestBody annotation to my RestController class which handles the incoming requests:
@RestController
@RequestMapping("/api/user")
class UserResource(val service: UserService) {
@GetMapping()
fun get(): List<User> = service.findAll()
@GetMapping("/{id}")
fun get(@PathVariable(name = "id") id: Long): User = service.findById(id)
@PostMapping()
fun post(@RequestBody user: User) {
service.post(user)
}
@DeleteMapping("")
fun delete(@RequestBody user: User) = service.delete(user)
}
It would be nice if someone can answer me if this is the correct solution or if there is something not correct anyway :-)