Pour une certaine entité Hibernate, nous avons besoin de stocker son heure de création et la dernière fois qu'elle a été mise à jour. Comment concevriez-vous cela?
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?
Quels types de données utiliseriez-vous en Java (
Date
,Calendar
,long
, ...)?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
)?
Je ne cherche pas seulement une solution de travail, mais une solution sûre et bien conçue.
Vous pouvez simplement utiliser
@CreationTimestamp
et@UpdateTimestamp
:la source
TemporalType.DATE
dans le premier cas etTemporalType.TIMESTAMP
dans le second.@CreationTimestamp
et@UpdateTimestamp
on en a besoin@Column(..., columnDefinition = "timestamp default current_timestamp")
, ou utilisez@PrePersist
et@PreUpdate
(ce dernier garantissant également que les clients ne peuvent pas définir une valeur différente).nullable=false
du@Column(name = "create_date" , nullable=false)
travailEn prenant les ressources de ce post ainsi que des informations prises à gauche et à droite de différentes sources, je suis venu avec cette solution élégante, créez la classe abstraite suivante
et demandez à toutes vos entités de l'étendre, par exemple:
la source
not-null property references a null or transient value: package.path.ClassName.created
@Column(name = "updated", nullable = false, insertable = false)
le faire fonctionner. Intéressant que cette réponse ait eu autant de votes positifs ..1. Quels types de colonnes de base de données vous devez utiliser
Votre première question était:
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 lahibernate.jdbc.time_zone
propriété Hibernate.2. Quel type de propriété d'entité utiliser
Votre deuxième question était:
Côté Java, vous pouvez utiliser Java 8
LocalDateTime
. Vous pouvez également utiliser l'héritageDate
, 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.Maintenant, nous pouvons également répondre à cette question:
Si vous utilisez
LocalDateTime
oujava.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:Mais, c'est beaucoup mieux si vous le mappez comme ceci:
Comment générer les valeurs de colonne d'audit
Votre troisième question était:
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 uneDEFAULT
contrainte DDL, comme: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
annotationsHibernate propose les annotations
@CreationTimestamp
et@UpdateTimestamp
qui peuvent être utilisées pour mapper les colonnescreated_on
etupdated_on
.Vous pouvez utiliser
@MappedSuperclass
pour définir une classe de base qui sera étendue par toutes les entités:Et, toutes les entités étendront le
BaseEntity
, comme ceci:Cependant, même si les
createdOn
etupdateOn
propriétés sont définies par la mise en veille prolongée spécifique@CreationTimestamp
et@UpdateTimestamp
annotations, lacreatedBy
etupdatedBy
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:
Et créez un
AuditListener
pour définir les propriétés d'audit:Pour enregistrer le
AuditListener
, vous pouvez utiliser l'@EntityListeners
annotation JPA:la source
datetime
plustimestamp
. Vous voulez que votre base de données connaisse le fuseau horaire de vos horodatages. Cela empêche les erreurs de conversion de fuseau horaire.timestsmp
type ne stocke pas les informations de fuseau horaire. Il fait juste une conversation de l'application TZ à DB TZ. En réalité, vous souhaitez stocker le client TZ séparément et faire la conversation dans l'application avant de rendre l'interface utilisateur.timestamp
est toujours en UTC. MySQL convertit lesTIMESTAMP
valeurs du fuseau horaire actuel en UTC pour le stockage, et en retour de l'UTC vers le fuseau horaire actuel pour la récupération. Documentation MySQL: Les types DATE, DATETIME et TIMESTAMPVous pouvez également utiliser un intercepteur pour définir les valeurs
Créez une interface appelée TimeStamped que vos entités implémentent
Définir l'intercepteur
Et enregistrez-le auprès de la fabrique de sessions
la source
Avec la solution d'Olivier, lors des déclarations de mise à jour, vous pouvez rencontrer:
Pour résoudre ce problème, ajoutez updatable = false à l'annotation @Column de l'attribut "créé":
la source
@Version
. Lorsqu'une entité est définie, deux appels sont effectués, l'un pour enregistrer et l'autre pour mettre à jour. J'étais confronté au même problème à cause de cela. Une fois que j'ai ajouté,@Column(updatable = false)
cela a résolu mon problème.Merci à tous ceux qui ont aidé. Après avoir fait moi-même des recherches (je suis le gars qui a posé la question), voici ce que j'ai trouvé le plus logique:
Type de colonne de base de données: le nombre de millisecondes indépendant du fuseau horaire depuis 1970 représenté comme étant
decimal(20)
parce que 2 ^ 64 a 20 chiffres et que l'espace disque est bon marché; soyons francs. De plus, je n'utiliseraiDEFAULT CURRENT_TIMESTAMP
ni déclencheurs. Je ne veux pas de magie dans la DB.Java type de champ:
long
. L'horodatage Unix est bien pris en charge sur diverses bibliothèques,long
n'a aucun problème Y2038, l'arithmétique d'horodatage est rapide et facile (principalement opérateur<
et opérateur+
, en supposant qu'aucun jour / mois / année ne soit impliqué dans les calculs). Et, plus important encore, leslong
s et lesjava.lang.Long
s primitifs sont immuables - effectivement transmis par la valeur - contrairement àjava.util.Date
s; Je serais vraiment énervé de trouver quelque chose commefoo.getLastUpdate().setTime(System.currentTimeMillis())
lors du débogage du code de quelqu'un d'autre.Le cadre ORM devrait être chargé de remplir automatiquement les données.
Je n'ai pas encore testé cela, mais en regardant uniquement les documents, je suppose que
@Temporal
cela fera l'affaire; ne sais pas si je pourrais utiliser@Version
à cette fin.@PrePersist
et@PreUpdate
sont de bonnes alternatives pour contrôler cela manuellement. Ajouter cela au super-type de couche (classe de base commune) pour toutes les entités, est une idée mignonne à condition que vous souhaitiez vraiment un horodatage pour toutes vos entités.la source
Si vous utilisez l'API de session, les rappels PrePersist et PreUpdate ne fonctionneront pas selon cette réponse .
J'utilise la méthode persist () de Hibernate Session dans mon code, donc la seule façon dont je pouvais faire ce travail était avec le code ci-dessous et après cet article de blog (également publié dans la réponse ).
la source
updated.clone()
autrement, d'autres composants peuvent manipuler l'état interne (date)Maintenant, il y a aussi des annotations @CreatedDate et @LastModifiedDate.
=> https://programmingmitra.blogspot.fr/2017/02/automatic-spring-data-jpa-auditing-saving-CreatedBy-createddate-lastmodifiedby-lastmodifieddate-automatically.html
(Framework Spring)
la source
Le code suivant a fonctionné pour moi.
la source
protected Integer id;
deprotected
la classe parent en général, parce que je ne pouvais pas l'utiliser dans mes cas de test comme.getId()
Une bonne approche consiste à avoir une classe de base commune pour toutes vos entités. Dans cette classe de base, vous pouvez avoir votre propriété id si elle est communément nommée dans toutes vos entités (une conception commune), vos propriétés de date de création et de dernière mise à jour.
Pour la date de création, vous conservez simplement une propriété java.util.Date . Assurez-vous de toujours l'initialiser avec new Date () .
Pour le dernier champ de mise à jour, vous pouvez utiliser une propriété Timestamp, vous devez la mapper avec @Version. Avec cette annotation, la propriété sera mise à jour automatiquement par Hibernate. Attention, Hibernate appliquera également un verrouillage optimiste (c'est une bonne chose).
la source
Juste pour renforcer: ce
java.util.Calender
n'est pas pour les horodatages .java.util.Date
est pour un moment dans le temps, agnostique des choses régionales comme les fuseaux horaires. La plupart des bases de données stockent les choses de cette façon (même si elles ne le semblent pas; il s'agit généralement d'un paramètre de fuseau horaire dans le logiciel client; les données sont bonnes)la source
En tant que type de données dans JAVA, je recommande fortement d'utiliser java.util.Date. J'ai rencontré des problèmes de fuseau horaire assez désagréables lors de l'utilisation de Calendar. Voir ce fil .
Pour définir les horodatages, je recommanderais d'utiliser une approche AOP ou vous pourriez simplement utiliser des déclencheurs sur la table (en fait, c'est la seule chose que je trouve jamais l'utilisation de déclencheurs acceptable).
la source
Vous pouvez envisager de stocker l'heure en tant que DateTime et en UTC. J'utilise généralement DateTime au lieu de Timestamp en raison du fait que MySql convertit les dates en UTC et en heure locale lors du stockage et de la récupération des données. Je préfère garder tout ce genre de logique en un seul endroit (couche métier). Je suis sûr qu'il existe d'autres situations où l'utilisation de Timestamp est préférable.
la source
Nous avons eu une situation similaire. Nous utilisions Mysql 5.7.
Cela a fonctionné pour nous.
la source
@PrePersist
et@PrePersist
ne couvre pas un tel cas.Si nous utilisons @Transactional dans nos méthodes, @CreationTimestamp et @UpdateTimestamp enregistreront la valeur dans DB mais retourneront null après avoir utilisé save (...).
Dans cette situation, l'utilisation de saveAndFlush (...) a fait l'affaire
la source
Je pense qu'il est plus simple de ne pas le faire dans le code Java, vous pouvez simplement définir la valeur par défaut de la colonne dans la définition de la table MySql.
la source