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, TIMESTAMP
kiể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 DATETIME
thay vào đó, không có giới hạn ranh giới trên. Tuy nhiên, DATETIME
khô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_zone
Hibernate.
Để biết thêm chi tiết về hibernate.jdbc.time_zone
cà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 LocalDateTime
hoặ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 @Temporal
chú 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_on
cột, bạn có thể sử dụng DEFAULT
ràng buộc DDL, như:
ALTER TABLE post
ADD CONSTRAINT created_on_default
DEFAULT CURRENT_TIMESTAMP() FOR created_on;
Đối với updated_on
cộ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 @CreationTimestamp
và @UpdateTimestamp
chú thích
Hibernate cung cấp các chú thích @CreationTimestamp
và @UpdateTimestamp
chú thích có thể được sử dụng để ánh xạ các cột created_on
và updated_on
cộ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 createdOn
và updateOn
được đặt theo chú thích @CreationTimestamp
và @UpdateTimestamp
chú thích cụ thể của Hibernate , thì createdBy
và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 @EntityListeners
chú 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 .