1. Jakich typów kolumn bazy danych należy użyć
Twoje pierwsze pytanie brzmiało:
Jakich typów danych używałbyś w bazie danych (zakładając MySQL, być może w innej strefie czasowej niż JVM)? Czy typy danych będą uwzględniać strefę czasową?
W MySQL TIMESTAMP
typ kolumny przesuwa się z lokalnej strefy czasowej sterownika JDBC do strefy czasowej bazy danych, ale może przechowywać tylko znaczniki czasu do '2038-01-19 03:14:07.999999
, więc nie jest to najlepszy wybór na przyszłość.
Lepiej więc użyj DATETIME
zamiast tego, który nie ma tego górnego ograniczenia. Jednak DATETIME
nie jest świadomy strefy czasowej. Z tego powodu najlepiej używać UTC po stronie bazy danych i używać hibernate.jdbc.time_zone
właściwości Hibernacja.
Aby uzyskać więcej informacji na temat hibernate.jdbc.time_zone
ustawienia, sprawdź ten artykuł .
2. Jakiego typu właściwości encji należy użyć
Twoje drugie pytanie brzmiało:
Jakich typów danych używałbyś w Javie (data, kalendarz, długie, ...)?
Po stronie Java możesz używać Java 8 LocalDateTime
. Możesz także użyć starszej wersji Date
, ale typy daty / godziny Java 8 są lepsze, ponieważ są niezmienne i nie rejestrują strefy czasowej w lokalnej strefie czasowej podczas ich rejestrowania.
Więcej informacji o typach daty / godziny Java 8 obsługiwanych przez Hibernację można znaleźć w tym artykule .
Teraz możemy również odpowiedzieć na to pytanie:
Jakich adnotacji użyłbyś do mapowania (np. @Temporal
)?
Jeśli używasz LocalDateTime
lub java.sql.Timestamp
do mapowania właściwości encji znacznika czasu, nie musisz jej używać, @Temporal
ponieważ HIbernate już wie, że ta właściwość ma zostać zapisana jako sygnatura czasowa JDBC.
Tylko jeśli używasz java.util.Date
, musisz określić @Temporal
adnotację:
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_on")
private Date createdOn;
Ale jest znacznie lepiej, jeśli zamapujesz to w następujący sposób:
@Column(name = "created_on")
private LocalDateTime createdOn;
Jak wygenerować wartości kolumny kontroli
Twoje trzecie pytanie brzmiało:
Kogo udzieliłbyś odpowiedzialności za ustawienie znaczników czasu - bazy danych, frameworka ORM (Hibernacja) lub programisty aplikacji?
Jakich adnotacji użyłbyś do mapowania (np. @Temporal)?
Istnieje wiele sposobów osiągnięcia tego celu. Możesz na to pozwolić bazie danych…
W przypadku create_on
kolumny można użyć DEFAULT
ograniczenia DDL, takiego jak:
ALTER TABLE post
ADD CONSTRAINT created_on_default
DEFAULT CURRENT_TIMESTAMP() FOR created_on;
W przypadku updated_on
kolumny można użyć wyzwalacza DB, aby ustawić wartość kolumny przy CURRENT_TIMESTAMP()
każdej modyfikacji danego wiersza.
Lub użyj JPA lub Hibernacji, aby je ustawić.
Załóżmy, że masz następujące tabele bazy danych:
I każda tabela ma kolumny takie jak:
created_by
created_on
updated_by
updated_on
Korzystanie z hibernacji @CreationTimestamp
i @UpdateTimestamp
adnotacji
Hibernacja oferuje @CreationTimestamp
i @UpdateTimestamp
adnotacje, których można użyć do zmapowania kolumn created_on
i updated_on
.
Możesz użyć @MappedSuperclass
do zdefiniowania klasy bazowej, która zostanie rozszerzona przez wszystkie jednostki:
@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
}
I wszystkie podmioty rozszerzą BaseEntity
, w ten sposób:
@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
}
Więcej informacji na temat korzystania znajduje @MappedSuperclass
się w tym artykule .
Jednak nawet jeśli właściwości createdOn
i updateOn
są ustawione przez Hibernację @CreationTimestamp
i @UpdateTimestamp
adnotacje, createdBy
i updatedBy
wymagają rejestracji wywołania zwrotnego aplikacji, jak pokazano w poniższym rozwiązaniu JPA.
Korzystanie z JPA @EntityListeners
Możesz zawrzeć właściwości kontroli w 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
}
I utwórz wartość, AuditListener
aby ustawić właściwości kontroli:
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());
}
}
Aby zarejestrować AuditListener
, możesz użyć @EntityListeners
adnotacji 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
}
Aby uzyskać więcej informacji na temat wdrażania właściwości inspekcji za pomocą JPA @EntityListener
, sprawdź ten artykuł .