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
, ознакомьтесь с этой статьей .