1. Jenis kolom basis data apa yang harus Anda gunakan
Pertanyaan pertama Anda adalah:
Tipe data apa yang akan Anda gunakan dalam database (dengan asumsi MySQL, mungkin dalam zona waktu yang berbeda dari JVM)? Apakah tipe data akan sadar zona waktu?
Di MySQL, TIMESTAMP
tipe kolom bergeser dari zona waktu lokal driver JDBC ke zona waktu database, tetapi hanya dapat menyimpan cap waktu hingga '2038-01-19 03:14:07.999999
, jadi itu bukan pilihan terbaik untuk masa depan.
Jadi, lebih baik gunakan DATETIME
, yang tidak memiliki batasan batas atas ini. Namun, DATETIME
tidak sadar zona waktu. Jadi, untuk alasan ini, lebih baik menggunakan UTC di sisi basis data dan menggunakan hibernate.jdbc.time_zone
properti Hibernate.
Untuk detail lebih lanjut tentang hibernate.jdbc.time_zone
pengaturan, periksa artikel ini .
2. Jenis properti entitas apa yang harus Anda gunakan
Pertanyaan kedua Anda adalah:
Jenis data apa yang akan Anda gunakan di Jawa (Tanggal, Kalender, panjang, ...)?
Di sisi Java, Anda dapat menggunakan Java 8 LocalDateTime
. Anda juga dapat menggunakan warisan Date
, tetapi tipe Tanggal / Waktu Java 8 lebih baik karena tidak dapat diubah, dan jangan melakukan zona waktu bergeser ke zona waktu lokal saat mencatatnya.
Untuk detail lebih lanjut tentang tipe Tanggal / Waktu Java 8 yang didukung oleh Hibernate, lihat artikel ini .
Sekarang, kita juga dapat menjawab pertanyaan ini:
Anotasi apa yang akan Anda gunakan untuk pemetaan (misalnya @Temporal
)?
Jika Anda menggunakan LocalDateTime
atau java.sql.Timestamp
untuk memetakan properti entitas timestamp, maka Anda tidak perlu menggunakan @Temporal
karena HIbernate sudah tahu bahwa properti ini harus disimpan sebagai JDBC Timestamp.
Hanya jika Anda menggunakan java.util.Date
, Anda perlu menentukan @Temporal
anotasi, seperti ini:
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_on")
private Date createdOn;
Tapi, jauh lebih baik jika Anda memetakannya seperti ini:
@Column(name = "created_on")
private LocalDateTime createdOn;
Cara menghasilkan nilai kolom audit
Pertanyaan ketiga Anda adalah:
Dengan siapa Anda bertanggung jawab untuk mengatur stempel waktu — basis data, kerangka kerja ORM (Hibernate), atau pemrogram aplikasi?
Anotasi apa yang akan Anda gunakan untuk pemetaan (misalnya @ temporal)?
Ada banyak cara untuk mencapai tujuan ini. Anda dapat mengizinkan database untuk melakukan itu ..
Untuk create_on
kolom, Anda bisa menggunakan DEFAULT
batasan DDL, seperti:
ALTER TABLE post
ADD CONSTRAINT created_on_default
DEFAULT CURRENT_TIMESTAMP() FOR created_on;
Untuk updated_on
kolom, Anda bisa menggunakan pemicu DB untuk mengatur nilai kolom dengan CURRENT_TIMESTAMP()
setiap kali baris yang diberikan dimodifikasi.
Atau, gunakan JPA atau Hibernate untuk mengaturnya.
Mari kita asumsikan Anda memiliki tabel database berikut:
Dan, setiap tabel memiliki kolom seperti:
created_by
created_on
updated_by
updated_on
Menggunakan Hibernate @CreationTimestamp
dan @UpdateTimestamp
anotasi
Hibernate menawarkan @CreationTimestamp
dan @UpdateTimestamp
anotasi yang dapat digunakan untuk memetakan created_on
dan updated_on
kolom.
Anda bisa menggunakan @MappedSuperclass
untuk mendefinisikan kelas dasar yang akan diperluas oleh semua entitas:
@MappedSuperclass
public class BaseEntity {
@Id
@GeneratedValue
private Long id;
@Column(name = "created_on")
@CreationTimestamp
private LocalDateTime createdOn;
@Column(name = "created_by")
private String createdBy;
@Column(name = "updated_on")
@UpdateTimestamp
private LocalDateTime updatedOn;
@Column(name = "updated_by")
private String updatedBy;
//Getters and setters omitted for brevity
}
Dan, semua entitas akan memperpanjang BaseEntity
, seperti ini:
@Entity(name = "Post")
@Table(name = "post")
public class Post extend BaseEntity {
private String title;
@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
@OneToOne(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY
)
private PostDetails details;
@ManyToMany
@JoinTable(
name = "post_tag",
joinColumns = @JoinColumn(
name = "post_id"
),
inverseJoinColumns = @JoinColumn(
name = "tag_id"
)
)
private List<Tag> tags = new ArrayList<>();
//Getters and setters omitted for brevity
}
Untuk detail lebih lanjut tentang penggunaan @MappedSuperclass
, lihat artikel ini .
Namun, bahkan jika createdOn
dan updateOn
properti diatur oleh khusus Hibernate @CreationTimestamp
dan @UpdateTimestamp
anotasi, createdBy
dan updatedBy
mengharuskan mendaftarkan panggilan balik aplikasi, seperti yang diilustrasikan oleh solusi JPA berikut.
Menggunakan JPA @EntityListeners
Anda dapat merangkum properti audit dalam Embeddable:
@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
}
Dan, buat AuditListener
untuk mengatur properti audit:
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 setUpdatedOn(Auditable auditable) {
Audit audit = auditable.getAudit();
audit.setUpdatedOn(LocalDateTime.now());
audit.setUpdatedBy(LoggedUser.get());
}
}
Untuk mendaftar AuditListener
, Anda dapat menggunakan @EntityListeners
anotasi JPA:
@Entity(name = "Post")
@Table(name = "post")
@EntityListeners(AuditListener.class)
public class Post implements Auditable {
@Id
private Long id;
@Embedded
private Audit audit;
private String title;
@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
@OneToOne(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY
)
private PostDetails details;
@ManyToMany
@JoinTable(
name = "post_tag",
joinColumns = @JoinColumn(
name = "post_id"
),
inverseJoinColumns = @JoinColumn(
name = "tag_id"
)
)
private List<Tag> tags = new ArrayList<>();
//Getters and setters omitted for brevity
}
Untuk detail lebih lanjut tentang menerapkan properti audit dengan JPA @EntityListener
, lihat artikel ini .