1 package pk.lucidxpo.ynami.persistence.model; 2 3 import jakarta.persistence.Column; 4 import jakarta.persistence.EntityListeners; 5 import jakarta.persistence.Id; 6 import jakarta.persistence.MappedSuperclass; 7 import lombok.Data; 8 import org.hibernate.annotations.CreationTimestamp; 9 import org.hibernate.annotations.UpdateTimestamp; 10 import org.springframework.data.annotation.CreatedBy; 11 import org.springframework.data.annotation.LastModifiedBy; 12 import org.springframework.data.jpa.domain.support.AuditingEntityListener; 13 14 import java.time.Instant; 15 16 import static org.hibernate.annotations.SourceType.DB; 17 import static pk.lucidxpo.ynami.utils.Identity.randomID; 18 19 @Data 20 @MappedSuperclass 21 @EntityListeners({AuditingEntityListener.class}) 22 public abstract class Auditable<U> implements Identifiable { 23 // TODO: consider replacing this class with org.springframework.data.jpa.domain.AbstractAuditable 24 @Id 25 @Column(nullable = false, updatable = false) 26 private String id = randomID(); 27 28 // The @CreatedDate and @CreationTimestamp are convenient annotation which sets the field value to the current 29 // timestamp when the entity is first saved. @CreatedDate is a Spring annotation. It is applicable to all stores 30 // covered by Spring Data: JPA, JDBC, R2DBC, MongoDb, Cassandra and so on, whereas @CreationTimestamp is a Hibernate 31 // annotation. It is applicable to Hibernate only. By default, both of these annotations use the current date of 32 // the Java Virtual Machine when setting property values. Starting from Hibernate 6.0.0, we can optionally specify 33 // the database as the source of the date. However, when using @CreationTimestamp and @UpdateTimestamp, we have to 34 // keep in mind that Hibernate generates new timestamps on a per-field basis. This leads to multiple timestamps 35 // being different, even though they were set by the same INSERT or UPDATE statement. Main reason to use the 36 // @CreationTimestamp instead of @CreatedDate is, Spring Data Jpa sets the creation timestamp with nanoseconds 37 // (9 digits) precision on java object but saves the object with microseconds (6 digits) precision in the database 38 // on linux machines (this issue doesn't happen on MAC) and hence the assertions in the integration tests fail 39 // due to difference in precisions. So, setting 'source = DB' on the @CreationTimestamp annotation helps resolve 40 // this issue and tests work on both Mac and Linux. 41 // Insertable => Whether the column is included in SQL INSERT statements generated by the persistence provider. 42 // Updatable => Whether the column is included in SQL UPDATE statements generated by the persistence provider. 43 @CreationTimestamp(source = DB) 44 @Column(nullable = false, insertable = false, updatable = false) 45 private Instant createdDate; 46 47 // The @LastModifiedDate and @UpdateTimestamp are convenient annotation which automatically sets the field value to 48 // the current timestamp on each entity update. @LastModifiedDate is a Spring annotation. It is applicable to all 49 // stores covered by Spring Data: JPA, JDBC, R2DBC, MongoDb, Cassandra and so on, whereas @UpdateTimestamp is a 50 // Hibernate annotation. It is applicable to Hibernate only.By default, both of these annotations use the current 51 // date of the Java Virtual Machine when setting property values. Starting from Hibernate 6.0.0, we can optionally 52 // specify the database as the source of the date. Main reason to use the @UpdateTimestamp instead of 53 // @LastModifiedDate is, Spring Data Jpa sets the updation timestamp with nanoseconds (9 digits) precision on java 54 // object but saves the object with microseconds (6 digits) precision in the database on linux machines (this issue 55 // doesn't happen on MAC) and hence the assertions in the integration tests fail due to difference in precisions. 56 // So, setting 'source = DB' on the @UpdateTimestamp annotation helps resolve this issue and tests work on both Mac 57 // and Linux. 58 // Insertable => Whether the column is included in SQL INSERT statements generated by the persistence provider. 59 // Updatable => Whether the column is included in SQL UPDATE statements generated by the persistence provider. 60 @UpdateTimestamp(source = DB) 61 @Column(insertable = false, updatable = false) 62 private Instant lastModifiedDate; 63 64 // @ManyToOne 65 @CreatedBy 66 @Column(nullable = false, updatable = false) 67 private U createdBy; 68 69 @Column(nullable = false) 70 // @ManyToOne 71 @LastModifiedBy 72 private U lastModifiedBy; 73 }