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 }