1. Какие типы столбцов базы данных вы должны использовать
Ваш первый вопрос был:
Какие типы данных вы бы использовали в базе данных (предполагая MySQL, возможно, в другом часовом поясе, чем JVM)? Будут ли типы данных учитывать часовой пояс?
В MySQL TIMESTAMPтип столбца переключается с локального часового пояса драйвера JDBC на часовой пояс базы данных, но он может хранить только временные метки до'2038-01-19 03:14:07.999999 , поэтому это не лучший выбор в будущем.
Таким образом, лучше использовать DATETIMEвместо этого, который не имеет этого ограничения верхней границы. Тем DATETIMEне менее, часовой пояс не знает. Поэтому по этой причине лучше всего использовать UTC на стороне базы данных и использовать hibernate.jdbc.time_zoneсвойство Hibernate.
Для более подробной информации о hibernate.jdbc.time_zoneнастройке, ознакомьтесь с этой статьей .
2. Какой тип свойства объекта следует использовать
Ваш второй вопрос был:
Какие типы данных вы бы использовали в Java (Date, Calendar, long, ...)?
На стороне Java вы можете использовать Java 8 LocalDateTime. Вы также можете использовать устаревшие версии Date, но типы даты / времени в Java 8 лучше, поскольку они неизменны и не выполняют смещение часового пояса к локальному часовому поясу при их регистрации.
Чтобы узнать больше о типах даты и времени Java 8, поддерживаемых Hibernate, ознакомьтесь с этой статьей .
Теперь мы также можем ответить на этот вопрос:
Какие аннотации вы бы использовали для отображения (например @Temporal)?
Если вы используете LocalDateTimeили java.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столбца вы можете использовать триггер БД для установки значения столбца при 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
}
Для более подробной информации об использовании @MappedSuperclass, ознакомьтесь с этой статьей .
Тем не менее, даже если 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
}
Чтобы узнать больше о реализации свойств аудита с помощью JPA @EntityListener, ознакомьтесь с этой статьей .