skip to Main Content

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.

2

Answers


  1. Chosen as BEST ANSWER

    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.


  2. 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
    )
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search