1. Quali tipi di colonne del database dovresti usare
La tua prima domanda è stata:
Quali tipi di dati useresti nel database (supponendo MySQL, possibilmente in un fuso orario diverso da quello della JVM)? I tipi di dati saranno consapevoli del fuso orario?
In MySQL, il TIMESTAMPtipo di colonna si sposta dal fuso orario locale del driver JDBC al fuso orario del database, ma può solo memorizzare i timestamp '2038-01-19 03:14:07.999999, quindi non è la scelta migliore per il futuro.
Quindi, meglio usare DATETIMEinvece, che non ha questo limite superiore. Tuttavia, DATETIMEnon è a conoscenza del fuso orario. Quindi, per questo motivo, è meglio usare UTC sul lato database e usare la hibernate.jdbc.time_zoneproprietà Hibernate.
Per maggiori dettagli hibernate.jdbc.time_zonesull'impostazione, consulta questo articolo .
2. Quale tipo di proprietà dell'entità dovresti usare
La tua seconda domanda era:
Quali tipi di dati useresti in Java (data, calendario, lungo, ...)?
Sul lato Java, è possibile utilizzare Java 8 LocalDateTime. Puoi anche usare l'eredità Date, ma i tipi di data / ora di Java 8 sono migliori poiché sono immutabili e non eseguono il trasferimento del fuso orario al fuso orario locale quando li registri.
Per maggiori dettagli sui tipi di data / ora Java 8 supportati da Hibernate, consulta questo articolo .
Ora possiamo anche rispondere a questa domanda:
Quali annotazioni useresti per la mappatura (ad es. @Temporal)?
Se si utilizza LocalDateTimeo java.sql.Timestampper mappare una proprietà di entità timestamp, non è necessario utilizzarla @Temporalpoiché HIbernate sa già che questa proprietà deve essere salvata come Timestamp JDBC.
Solo se stai utilizzando java.util.Date, devi specificare l' @Temporalannotazione, in questo modo:
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_on")
private Date createdOn;
Ma è molto meglio se lo mappa in questo modo:
@Column(name = "created_on")
private LocalDateTime createdOn;
Come generare i valori della colonna di controllo
La tua terza domanda era:
Di chi saresti responsabile per l'impostazione dei timestamp: il database, il framework ORM (Hibernate) o il programmatore dell'applicazione?
Quali annotazioni useresti per la mappatura (ad es. @Temporal)?
Esistono molti modi per raggiungere questo obiettivo. È possibile consentire al database di farlo.
Per la create_oncolonna, è possibile utilizzare un DEFAULTvincolo DDL, come:
ALTER TABLE post
ADD CONSTRAINT created_on_default
DEFAULT CURRENT_TIMESTAMP() FOR created_on;
Per la updated_oncolonna, è possibile utilizzare un trigger DB per impostare il valore della colonna CURRENT_TIMESTAMP()ogni volta che viene modificata una determinata riga.
Oppure, utilizzare JPA o Hibernate per impostarli.
Supponiamo che tu abbia le seguenti tabelle del database:

E ogni tabella ha colonne come:
created_by
created_on
updated_by
updated_on
Usando l'ibernazione @CreationTimestampe le @UpdateTimestampannotazioni
Hibernate offre le annotazioni @CreationTimestampe @UpdateTimestampche possono essere utilizzate per mappare le colonne created_one updated_on.
È possibile utilizzare @MappedSuperclassper definire una classe di base che verrà estesa da tutte le entità:
@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
}
E, tutte le entità estenderanno BaseEntity, in questo modo:
@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
}
Per maggiori dettagli sull'uso @MappedSuperclass, consulta questo articolo .
Tuttavia, anche se le createdOne updateOnproprietà sono impostate dalla Hibernate-specifico @CreationTimestampe @UpdateTimestampannotazioni, la createdBye updatedByrichiedono la registrazione di un callback domanda, come illustrato dalla seguente soluzione APP.
Utilizzando JPA @EntityListeners
È possibile incapsulare le proprietà di controllo in un 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
}
E crea un AuditListenerper impostare le proprietà di controllo:
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());
}
}
Per registrare il AuditListener, è possibile utilizzare l' @EntityListenersannotazione 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
}
Per ulteriori dettagli sull'implementazione delle proprietà di controllo con l'APP @EntityListener, consulta questo articolo .