Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Auditing and @Embedded in Spring Data JPA

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?

like image 521
jpvee Avatar asked Jul 07 '16 11:07

jpvee


People also ask

What is auditing in JPA?

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.

How do I enable JPA auditing?

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.

What is the use of @audited annotation?

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.

What is spring auditing?

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.


2 Answers

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;

  }
like image 199
TheKojuEffect Avatar answered Sep 18 '22 08:09

TheKojuEffect


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

like image 20
Gradnok Avatar answered Sep 18 '22 08:09

Gradnok