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 TIMESTAMP
tipo 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 DATETIME
invece, che non ha questo limite superiore. Tuttavia, DATETIME
non è a conoscenza del fuso orario. Quindi, per questo motivo, è meglio usare UTC sul lato database e usare la hibernate.jdbc.time_zone
proprietà Hibernate.
Per maggiori dettagli hibernate.jdbc.time_zone
sull'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 LocalDateTime
o java.sql.Timestamp
per mappare una proprietà di entità timestamp, non è necessario utilizzarla @Temporal
poiché HIbernate sa già che questa proprietà deve essere salvata come Timestamp JDBC.
Solo se stai utilizzando java.util.Date
, devi specificare l' @Temporal
annotazione, 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_on
colonna, è possibile utilizzare un DEFAULT
vincolo DDL, come:
ALTER TABLE post
ADD CONSTRAINT created_on_default
DEFAULT CURRENT_TIMESTAMP() FOR created_on;
Per la updated_on
colonna, è 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 @CreationTimestamp
e le @UpdateTimestamp
annotazioni
Hibernate offre le annotazioni @CreationTimestamp
e @UpdateTimestamp
che possono essere utilizzate per mappare le colonne created_on
e updated_on
.
È possibile utilizzare @MappedSuperclass
per 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 createdOn
e updateOn
proprietà sono impostate dalla Hibernate-specifico @CreationTimestamp
e @UpdateTimestamp
annotazioni, la createdBy
e updatedBy
richiedono 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 AuditListener
per 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' @EntityListeners
annotazione 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 .