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.
