I am having a problem with JPA auditing and for @Embedded
members. Consider the following example scenario:
I set up a test table in an Oracle DB:
CREATE TABLE AUDIT_TEST (
ID NUMBER(38) NOT NULL PRIMARY KEY,
CREATION_DATE TIMESTAMP(6) DEFAULT SYSTIMESTAMP NOT NULL
);
I define a JPA @Entity
with auditing:
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "AUDIT_TEST")
public class AuditTest {
private Long id;
private LocalDateTime creationDate;
@Id
@Column(name = "ID")
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@CreatedDate
@Column(name = "CREATION_DATE")
public LocalDateTime getCreationDate() { return creationDate; }
public void setCreationDate(LocalDateTime creationDate) {
this.creationDate = creationDate;
}
}
Finally, I enable JPA auditing in my @Configuration
:
@SpringBootApplication()
@EnableJpaAuditing()
public class AuditTestApplication {
}
So far so good; when I construct an AuditTest
instance, assign it an id and commit, the creationDate
column gets populated with the current timestamp as expected.
However, things stop working when I encapsulate the audit column in an @Embeddable
:
@Embeddable
public class AuditTestEmbeddable {
private LocalDateTime creationDate;
@CreatedDate
@Column(name = "CREATION_DATE")
public LocalDateTime getCreationDate() { return creationDate; }
public void setCreationDate(LocalDateTime creationDate) {
this.creationDate = creationDate;
}
}
Then I change my entity class to embed the creation date:
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "AUDIT_TEST")
public class AuditTest {
private Long id;
private AuditTestEmbeddable auditTestEmbeddable = new AuditTestEmbeddable();
@Id
@Column(name = "ID")
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@Embedded
public AuditTestEmbeddable getAuditTestEmbeddable() {
return auditTestEmbeddable;
}
public void setAuditTestEmbeddable(AuditTestEmbeddable auditTestEmbeddable) {
this.auditTestEmbeddable = auditTestEmbeddable;
}
}
And unfortunately, the auditing is no longer working.
Is anyone here aware of a way to save the auditing functionality while still using @Embedded
classes?
Auditing basically involves tracking and logging every change we make to our persisted data, which consists of tracking and storing every insert, update, and delete activity. Auditing aids in the preservation of history records, which can later be used to trace user activity.
Enable JpaAudit In order to enable JPA Auditing for this project will need to apply three annotations and a configuration class. Those annotations are; @EntityListener , @CreatedDate , and @LastModifiedDate . @EntityListener will be the one that is responsible to listen to any create or update activity.
Annotation Type Audited. When applied to a class, indicates that all of its properties should be audited. When applied to a field, indicates that this field should be audited.
Spring Data provides sophisticated support to transparently keep track of who created or changed an entity and the point in time this happened. To benefit from that functionality you have to equip your entity classes with auditing metadata that can be defined either using annotations or by implementing an interface.
Update: This functionality has been added to Spring Data 2.1 M2 (Lovelace). https://jira.spring.io/browse/DATACMNS-1274
Spring Data audit annotations in nested (embeddable) classes isn't supported yet. Here's the jira ticket requesting this feature.
However, we could use custom audit listener to set audit information in embeddable classes.
Here's the sample implementation taken from a blog: How to audit entity modifications using the JPA @EntityListeners, @Embedded, and @Embeddable annotations.
Embeddable Audit
@Embeddable
public class Audit {
@Column(name = "created_on")
private LocalDateTime createdOn;
@Column(name = "created_by")
private String createdBy;
@Column(name = "updated_on")
private LocalDateTime updatedOn;
@Column(name = "updated_by")
private String updatedBy;
//Getters and setters omitted for brevity
}
Audit Listener
public class AuditListener {
@PrePersist
public void setCreatedOn(Auditable auditable) {
Audit audit = auditable.getAudit();
if(audit == null) {
audit = new Audit();
auditable.setAudit(audit);
}
audit.setCreatedOn(LocalDateTime.now());
audit.setCreatedBy(LoggedUser.get());
}
@PreUpdate
public void setUpdadtedOn(Auditable auditable) {
Audit audit = auditable.getAudit();
audit.setUpdatedOn(LocalDateTime.now());
audit.setUpdatedBy(LoggedUser.get());
}
}
Auditable
public interface Auditable {
Audit getAudit();
void setAudit(Audit audit);
}
Sample Entity
@Entity
@EntityListeners(AuditListener.class)
public class Post implements Auditable {
@Id
private Long id;
@Embedded
private Audit audit;
private String title;
}
With spring-data 2.4.4 the AuditListener works fine with embedded objects, see documentation spring-data
The minimal version of spring-data is bundled in spring-boot version 2.4.3
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With