I have set up an auditing feature using Spring Data JPA for a project. It is working fine, I am getting back the CreatedBy
, CreatedDate
, LastModifiedBy
, and LastModifiedDate
properties with no issue.
The thing is CreatedBy
and LastModifiedBy
are returning the username
property value from the logged in user, which was what I intended in the first place, but now I need to return a foreign key for that user instead of their username.
I honestly have no idea how to implement this, please bear in mind this is my first semi-professional (irrelevant long story there) project. I've searched everywhere and couldn't find an answer. I don't know if I am using StackOverflow correctly, this is my first post, so if my question is silly or not that relevant, keep in mind I'm new here, please be helpful and help me find my way around.
For this project I am using the Spring Framework and Kotlin.
I've got the following files:
import org.springframework.data.jpa.domain.support.AuditingEntityListener
import java.math.BigDecimal
import java.util.*
import javax.persistence.*
@Entity
@EntityListeners(AuditingEntityListener::class)
@Table(name = "SERVICE")
class ServiceModel(): AuditModel() {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
val id: Long? = null
@Column(nullable = false)
lateinit var description: String
@Column(nullable = false)
lateinit var duration: String
@Column(nullable = false)
lateinit var price: BigDecimal
@Column(length = 255, nullable = false)
lateinit var title: String
@Column(length = 255)
val image: String? = null
@Column(length = 255)
lateinit var comments: String
}
import org.springframework.data.jpa.domain.support.AuditingEntityListener
import javax.persistence.*
@Entity
@EntityListeners(AuditingEntityListener::class)
@Table(name = "EMPLOYEE")
class EmployeeModel(): AuditModel() {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
val id: Long? = null
@Column(nullable = false, length = 255)
lateinit var fullName: String
@Column(nullable = false, unique = true, length = 14)
lateinit var document: String
@Column(nullable = false, length = 16)
lateinit var phoneNumber: String
@Column(length = 255, nullable = false)
lateinit var email: String
@Column(nullable = false, length = 10)
lateinit var zipCode: String
@Column(nullable = false, length = 255)
lateinit var address: String
@Column(nullable = false, length = 255)
lateinit var country: String
@Column(nullable = false, length = 255)
lateinit var city: String
@Column(nullable = false, length = 255)
lateinit var province: String
@ManyToOne(optional = true)
@JoinColumn(name = "fk_company", nullable = true)
val company : CompanyModel? = null
}
import org.springframework.data.annotation.CreatedBy
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.LastModifiedBy
import org.springframework.data.annotation.LastModifiedDate
import java.util.*
import javax.persistence.Column
abstract class AuditModel {
@CreatedBy
@Column(name = "created_by")
lateinit var createdBy: String
@CreatedDate
@Column(name = "created_at")
lateinit var createdAt: Date
@LastModifiedBy
@Column(name = "updated_by")
lateinit var updatedBy: String
@LastModifiedDate
@Column(name = "updated_at")
lateinit var updatedAt: Date
}
import org.springframework.data.domain.AuditorAware
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.stereotype.Component
import java.util.*
@Component
class AuditorAwareImpl : AuditorAware<String> {
override fun getCurrentAuditor(): Optional<String> {
var loggedInUser: String = SecurityContextHolder.getContext().authentication.name
return Optional.of(loggedInUser)
}
}
I have tried to get the foreign key property instead of the username in the AuditorAwareImpl.kt
file from the SecurityContextHolder
class, but to no avail.
I hope I've provided enough for someone to be able to help, otherwise let me know and I'll further contribute. Thank you very much in advance.
I'm sorry I'm not able to answer you in Kotlin but maybe you can work with my Java answer as well. You could declare your own EntityListener
and set the audited fields manually. The problem is that you can't override the property which the @CreatedBy
respectively the @LastModifiedBy
accesses.
A custom entity listener could look like this:
@Component
@RequiredArgsConstructor
public class EmployeeModelListener {
@PrePersist
public void prePersist(EmployeeModel employee) {
employee.setCreatedBy(); // access the foreign key
employee.setCreatedAt(ZonedDateTime.now());
employee.setUpdatedBy(employee.getCreatedBy());
employee.setUpdatedAt(employee.getCreatedAt());
}
@PreUpdate
public void preUpdate(EmployeeModel employee) {
employee.setUpdatedBy(); // access the foreign key
employee.setUpdatedAt(ZonedDateTime.now());
}
}
Note that this is a component, therefore allowing the injection of beans if necessary (I don't know how you obtain said foreign key but if you needed a repository for example, you could autowire it in here and use it to retrieve the desired data).
You then need to remove the auditing annotations like @CreatedBy
and @CreatedDate
from your AuditModel
and change the @EntityListeners(AuditingEntityListener::class)
to @EntityListeners(EmployeeModelListener::class)
in your EmployeeModel
.
You can also try if a hybrid model works where you leave the @CreatedDate
and @LastModifiedDate
annotations on the AuditModel
and don't set it manually in the EmployeeModelListener
.