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 TIMESTAMP
type 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 DATETIME
place, qui n'a pas cette limite supérieure. Cependant, DATETIME
n'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_zone
propriété Hibernate.
Pour plus de détails sur le hibernate.jdbc.time_zone
paramè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 LocalDateTime
ou java.sql.Timestamp
pour mapper une propriété d'entité d'horodatage, vous n'avez pas besoin de l'utiliser, @Temporal
car 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' @Temporal
annotation, 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_on
colonne, vous pouvez utiliser une DEFAULT
contrainte DDL, comme:
ALTER TABLE post
ADD CONSTRAINT created_on_default
DEFAULT CURRENT_TIMESTAMP() FOR created_on;
Pour la updated_on
colonne, 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 @CreationTimestamp
et d' @UpdateTimestamp
annotations
Hibernate propose les annotations @CreationTimestamp
et @UpdateTimestamp
qui peuvent être utilisées pour mapper les colonnes created_on
et updated_on
.
Vous pouvez utiliser @MappedSuperclass
pour 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 createdOn
et updateOn
propriétés sont définies par la mise en veille prolongée spécifique @CreationTimestamp
et @UpdateTimestamp
annotations, la createdBy
et updatedBy
exigent 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 AuditListener
pour 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' @EntityListeners
annotation 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 .