- 您应该使用哪些数据库列类型
你的第一个问题是:
您将在数据库中使用哪些数据类型(假设 MySQL,可能与 JVM 在不同的时区)?数据类型会感知时区吗?
在 MySQL 中,TIMESTAMP
column 类型会从 JDBC 驱动本地时区转移到数据库时区,但它最多只能存储时间戳2038-01-19 03:14:07.999999
,因此它不是未来的最佳选择。
所以,最好DATETIME
改用它,它没有这个上限限制。但是,DATETIME
不知道时区。因此,出于这个原因,最好在数据库端使用 UTC 并使用hibernate.jdbc.time_zone
Hibernate 属性。
- 您应该使用什么实体属性类型
你的第二个问题是:
你会在 Java 中使用哪些数据类型(日期、日历、长......)?
在 Java 方面,您可以使用 Java 8 LocalDateTime
。您也可以使用 legacy Date
,但 Java 8 Date/Time 类型更好,因为它们是不可变的,并且在记录它们时不要将时区转换为本地时区。
现在,我们也可以回答这个问题:
您会为映射使用哪些注释(例如@Temporal
)?
如果您使用LocalDateTime
orjava.sql.Timestamp
来映射时间戳实体属性,那么您不需要使用@Temporal
,因为 HIbernate 已经知道该属性将被保存为 JDBC 时间戳。
只有在使用java.util.Date
时,才需要指定@Temporal
注解,如下所示:
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_on")
private Date createdOn;
但是,如果你像这样映射它会更好:
@Column(name = "created_on")
private LocalDateTime createdOn;
如何生成审计列值
你的第三个问题是:
你会让谁负责设置时间戳——数据库、ORM 框架(Hibernate)还是应用程序程序员?
您会为映射使用哪些注释(例如@Temporal)?
有很多方法可以实现这一目标。您可以允许数据库执行此操作..
对于该create_on
列,您可以使用DEFAULT
DDL 约束,例如:
ALTER TABLE post
ADD CONSTRAINT created_on_default
DEFAULT CURRENT_TIMESTAMP() FOR created_on;
对于updated_on
列,您可以使用 DB 触发器在CURRENT_TIMESTAMP()
每次修改给定行时设置列值。
或者,使用 JPA 或 Hibernate 来设置这些。
假设您有以下数据库表:
而且,每个表都有如下列:
created_by
created_on
updated_by
updated_on
使用 Hibernate@CreationTimestamp
和@UpdateTimestamp
注解
Hibernate 提供了@CreationTimestamp
和@UpdateTimestamp
注释,可用于映射created_on
和updated_on
列。
您可以使用它@MappedSuperclass
来定义将由所有实体扩展的基类:
@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
}
而且,所有实体都将扩展BaseEntity
,如下所示:
@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
}
但是,即使createdOn
和updateOn
属性是由特定于 Hibernate 的@CreationTimestamp
和@UpdateTimestamp
注释设置的,createdBy
和 也updatedBy
需要注册应用程序回调,如下面的 JPA 解决方案所示。
使用 JPA@EntityListeners
您可以将审计属性封装在 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
}
并且,创建一个AuditListener
来设置审计属性:
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());
}
}
要注册AuditListener
,您可以使用@EntityListeners
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
}