I have tried to create a REST APIs in Kotlin and Spring Boot and my Country
entity has auditing fields created_at
and updated_at
which indicate the record creation and last modified date and time respectively. The Spring Boot application is simple and I have not added any configuration files. Here is the implementation of the application:
/model/Country.kt
(EDIT: I have also tried to add @EntityListeners(AuditingEntityListener::class)
but this also didn't work.)
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: Long,
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/CountryCountroller.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);
}
}
ExampleApplication.kt
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)
}
Schema of the Country table:
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 DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (country_id)
The request body is a json and the request header has include the correct Content-Type information:
{
"code": "FRA",
"displayName": "France"
}
After calling the API, the following record is inserted into the table:
{
"id": 1,
"code": "FRA",
"displayName": "France",
"createdAt": null,
"updatedAt": null
}
From my understanding, since my auditing does not include auditing the created and update user details, i.e. @CreatedBy
and @LastModifiedBy
, I do not have to create a new Auditable class and implement functions for the value of CreatedBy
and LastModifiedBy
fields. Also, I also expect that the annotations @CreatedDate
and @LastModifiedDate
would help me insert the record creation datetime and last updated datetime right before the record is being inserted into the table. I wonder if I have missed anything in my code which results in having null on both created_at
and updated_at
fields.
Also worth discussing about, I have tried to use @CreationTimestamp
and @UpdateTimestamp
annotations from Hibernate to replace @CreatedDate
and @LastModifiedDate
. This actually works with current datetime being populated in created_at
and updated_at
fields. However, I would like to use @CreatedDate
and @LastModifiedDate
instead, as @CreatedBy
and @LastModifedBy
annotations are also available which I might be using in the future. Even if I decide to go with the Hibernate annotations in the future, I would still like to understand and know why I am not able to use the JPA annotations.
Not sure if this is helpful but I do have
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL8Dialect
in application.properties
file.
CodePudding user response:
Have you tried this annotation? @EntityListeners(AuditingEntityListener.class)
@Entity
@Table(name = "country")
@EntityListeners(AuditingEntityListener::class)
class Country (
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "country_id")
val id: Long,
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
)
CodePudding user response:
I have found the answer to my question.
To use @CreatedDate
and @LastModifiedDate
, I have to use var
instead of val
to define the variables created_at
and updated_at
. Since I am new to Kotlin coming from Java, I did not realise the importance of choosing var
and val
. As stated in Kotlin Docs,
Read-only local variables are defined using the keyword val. They can be assigned a value only once.
Variables that can be reassigned use the var keyword.
https://kotlinlang.org/docs/basic-syntax.html#variables
Since I have used val
to define the two variables, they are both read-only and therefore JPA Auditing is not able to populate the time into the two fields, thus I am getting NULL on the two columns created_at
and updated_at
in the table.