1. Quels types de colonnes de base de données vous devez utiliser
Votre première question était:
Quels types de données utiliseriez-vous dans la base de données (en supposant MySQL, éventuellement dans un fuseau horaire différent de celui de la JVM)? Les types de données seront-ils sensibles au fuseau horaire?
Dans MySQL, le TIMESTAMPtype de colonne passe du fuseau horaire local du pilote JDBC au fuseau horaire de la base de données, mais il ne peut stocker que des horodatages jusqu'à '2038-01-19 03:14:07.999999, ce n'est donc pas le meilleur choix pour l'avenir.
Donc, mieux vaut utiliser à la DATETIMEplace, qui n'a pas cette limite supérieure. Cependant, DATETIMEn'est pas au courant du fuseau horaire. Donc, pour cette raison, il est préférable d'utiliser UTC côté base de données et d'utiliser la hibernate.jdbc.time_zonepropriété Hibernate.
Pour plus de détails sur le hibernate.jdbc.time_zoneparamètre, consultez cet article .
2. Quel type de propriété d'entité utiliser
Votre deuxième question était:
Quels types de données utiliseriez-vous en Java (date, calendrier, long, ...)?
Côté Java, vous pouvez utiliser Java 8 LocalDateTime. Vous pouvez également utiliser l'héritage Date, mais les types de date / heure Java 8 sont meilleurs car ils sont immuables et ne font pas passer un fuseau horaire au fuseau horaire local lors de leur journalisation.
Pour plus de détails sur les types de date / heure Java 8 pris en charge par Hibernate, consultez cet article .
Maintenant, nous pouvons également répondre à cette question:
Quelles annotations utiliseriez-vous pour le mappage (par exemple @Temporal)?
Si vous utilisez LocalDateTimeou java.sql.Timestamppour mapper une propriété d'entité d'horodatage, vous n'avez pas besoin de l'utiliser, @Temporalcar HIbernate sait déjà que cette propriété doit être enregistrée en tant qu'horodatage JDBC.
Seulement si vous utilisez java.util.Date, vous devez spécifier l' @Temporalannotation, comme ceci:
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_on")
private Date createdOn;
Mais, c'est beaucoup mieux si vous le mappez comme ceci:
@Column(name = "created_on")
private LocalDateTime createdOn;
Comment générer les valeurs de colonne d'audit
Votre troisième question était:
Qui feriez-vous pour définir les horodatages - la base de données, le cadre ORM (Hibernate) ou le programmeur d'application?
Quelles annotations utiliseriez-vous pour le mappage (par exemple @Temporal)?
Il existe de nombreuses façons d'atteindre cet objectif. Vous pouvez autoriser la base de données à le faire.
Pour la create_oncolonne, vous pouvez utiliser une DEFAULTcontrainte DDL, comme:
ALTER TABLE post
ADD CONSTRAINT created_on_default
DEFAULT CURRENT_TIMESTAMP() FOR created_on;
Pour la updated_oncolonne, vous pouvez utiliser un déclencheur DB pour définir la valeur de la colonne à CURRENT_TIMESTAMP()chaque fois qu'une ligne donnée est modifiée.
Ou, utilisez JPA ou Hibernate pour les définir.
Supposons que vous disposez des tables de base de données suivantes:

Et, chaque table a des colonnes comme:
created_by
created_on
updated_by
updated_on
Utilisation d'Hibernate @CreationTimestampet d' @UpdateTimestampannotations
Hibernate propose les annotations @CreationTimestampet @UpdateTimestampqui peuvent être utilisées pour mapper les colonnes created_onet updated_on.
Vous pouvez utiliser @MappedSuperclasspour définir une classe de base qui sera étendue par toutes les entités:
@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
}
Et, toutes les entités étendront le BaseEntity, comme ceci:
@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
}
Pour plus de détails sur l'utilisation @MappedSuperclass, consultez cet article .
Cependant, même si les createdOnet updateOnpropriétés sont définies par la mise en veille prolongée spécifique @CreationTimestampet @UpdateTimestampannotations, la createdByet updatedByexigent l' enregistrement d' un rappel d'application, comme illustré par la solution JPA suivante.
Utilisation de JPA @EntityListeners
Vous pouvez encapsuler les propriétés d'audit dans 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
}
Et créez un AuditListenerpour définir les propriétés d'audit:
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());
}
}
Pour enregistrer le AuditListener, vous pouvez utiliser l' @EntityListenersannotation 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
}
Pour plus de détails sur l'implémentation des propriétés d'audit avec le JPA @EntityListener, consultez cet article .