1. Loại cột cơ sở dữ liệu nào bạn nên sử dụng
Câu hỏi đầu tiên của bạn là:
Những loại dữ liệu nào bạn sẽ sử dụng trong cơ sở dữ liệu (giả sử MySQL, có thể ở múi giờ khác là JVM)? Các loại dữ liệu sẽ được nhận biết múi giờ?
Trong MySQL, TIMESTAMPkiểu cột thực hiện chuyển từ múi giờ cục bộ của trình điều khiển JDBC sang múi giờ của cơ sở dữ liệu, nhưng nó chỉ có thể lưu trữ dấu thời gian lên đến '2038-01-19 03:14:07.999999, vì vậy nó không phải là lựa chọn tốt nhất cho tương lai.
Vì vậy, sử dụng tốt hơn DATETIMEthay vào đó, không có giới hạn ranh giới trên. Tuy nhiên, DATETIMEkhông nhận thức được múi giờ. Vì vậy, vì lý do này, tốt nhất nên sử dụng UTC ở phía cơ sở dữ liệu và sử dụng thuộc tính hibernate.jdbc.time_zoneHibernate.
Để biết thêm chi tiết về hibernate.jdbc.time_zonecài đặt, hãy xem bài viết này .
2. Loại thuộc tính thực thể nào bạn nên sử dụng
Câu hỏi thứ hai của bạn là:
Những loại dữ liệu nào bạn sẽ sử dụng trong Java (Ngày, Lịch, dài, ...)?
Về phía Java, bạn có thể sử dụng Java 8 LocalDateTime. Bạn cũng có thể sử dụng di sản Date, nhưng các loại Ngày / Giờ của Java 8 tốt hơn vì chúng không thay đổi và không thực hiện chuyển múi giờ sang múi giờ địa phương khi đăng nhập chúng.
Để biết thêm chi tiết về các loại Ngày / Giờ Java 8 được Hibernate hỗ trợ, hãy xem bài viết này .
Bây giờ, chúng ta cũng có thể trả lời câu hỏi này:
Những chú thích nào bạn sẽ sử dụng cho ánh xạ (ví dụ @Temporal)?
Nếu bạn đang sử dụng LocalDateTimehoặc java.sql.Timestampđể ánh xạ một thuộc tính thực thể dấu thời gian, thì bạn không cần phải sử dụng@Temporal vì HIbernate đã biết rằng thuộc tính này sẽ được lưu dưới dạng Dấu thời gian JDBC.
Chỉ khi bạn đang sử dụng java.util.Date, bạn cần chỉ định @Temporalchú thích, như thế này:
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_on")
private Date createdOn;
Nhưng, sẽ tốt hơn nhiều nếu bạn lập bản đồ như thế này:
@Column(name = "created_on")
private LocalDateTime createdOn;
Cách tạo các giá trị cột kiểm toán
Câu hỏi thứ ba của bạn là:
Ai sẽ chịu trách nhiệm thiết lập dấu thời gian cho cơ sở dữ liệu, khung ORM (Hibernate) hoặc lập trình viên ứng dụng?
Những chú thích nào bạn sẽ sử dụng cho ánh xạ (ví dụ @Temporal)?
Có nhiều cách bạn có thể đạt được mục tiêu này. Bạn có thể cho phép cơ sở dữ liệu để làm điều đó ..
Đối với create_oncột, bạn có thể sử dụng DEFAULTràng buộc DDL, như:
ALTER TABLE post
ADD CONSTRAINT created_on_default
DEFAULT CURRENT_TIMESTAMP() FOR created_on;
Đối với updated_oncột, bạn có thể sử dụng trình kích hoạt DB để đặt giá trị cột vớiCURRENT_TIMESTAMP() mỗi lần sửa đổi một hàng nhất định.
Hoặc, sử dụng JPA hoặc Hibernate để đặt chúng.
Giả sử bạn có các bảng cơ sở dữ liệu sau:

Và, mỗi bảng có các cột như:
created_by
created_on
updated_by
updated_on
Sử dụng Hibernate @CreationTimestampvà @UpdateTimestampchú thích
Hibernate cung cấp các chú thích @CreationTimestampvà @UpdateTimestampchú thích có thể được sử dụng để ánh xạ các cột created_onvà updated_oncột.
Bạn có thể sử dụng @MappedSuperclassđể định nghĩa một lớp cơ sở sẽ được mở rộng bởi tất cả các thực thể:
@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
}
Và, tất cả các thực thể sẽ mở rộng BaseEntity, như thế này:
@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
}
Để biết thêm chi tiết về việc sử dụng @MappedSuperclass, hãy xem bài viết này .
Tuy nhiên, ngay cả khi các thuộc tính createdOnvà updateOnđược đặt theo chú thích @CreationTimestampvà @UpdateTimestampchú thích cụ thể của Hibernate , thì createdByvàupdatedBy yêu cầu đăng ký một cuộc gọi lại ứng dụng, như được minh họa bởi giải pháp JPA sau đây.
Sử dụng JPA @EntityListeners
Bạn có thể gói các thuộc tính kiểm toán trong một nhúng:
@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
}
Và, tạo một AuditListenerđể thiết lập các thuộc tính kiểm toán:
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());
}
}
Để đăng ký AuditListener, bạn có thể sử dụng @EntityListenerschú thích 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
}
Để biết thêm chi tiết về việc triển khai các thuộc tính kiểm toán với JPA @EntityListener, hãy xem bài viết này .