Erstellungszeitstempel und letzter Aktualisierungszeitstempel mit Hibernate und MySQL

Für eine bestimmte Hibernate-Entität müssen wir ihre Erstellungszeit und die letzte Aktualisierung speichern. Wie würdest du das gestalten?

Ich suche nicht nur nach einer funktionierenden Lösung, sondern nach einer sicheren und durchdachten Lösung.

Wenn Sie die JPA-Anmerkungen verwenden, können Sie die Ereignis-Hooks @PrePersist und @PreUpdate verwenden:

 @Entity @Table(name = "entities") public class Entity { ... private Date created; private Date updated; @PrePersist protected void onCreate() { created = new Date(); } @PreUpdate protected void onUpdate() { updated = new Date(); } } 

oder Sie können die Annotation @EntityListener für die class verwenden und den Ereigniscode in eine externe class einfügen.

Ich nahm die Ressourcen in diesem Beitrag zusammen mit Informationen, die von verschiedenen Quellen stammen, und kam mit dieser eleganten Lösung, um die folgende abstrakte class zu erstellen

 import java.util.Date; import javax.persistence.Column; import javax.persistence.MappedSuperclass; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import javax.persistence.Temporal; import javax.persistence.TemporalType; @MappedSuperclass public abstract class AbstractTimestampEntity { @Temporal(TemporalType.TIMESTAMP) @Column(name = "created", nullable = false) private Date created; @Temporal(TemporalType.TIMESTAMP) @Column(name = "updated", nullable = false) private Date updated; @PrePersist protected void onCreate() { updated = created = new Date(); } @PreUpdate protected void onUpdate() { updated = new Date(); } } 

und lassen Sie alle Ihre Entitäten erweitern, zum Beispiel:

 @Entity @Table(name = "campaign") public class Campaign extends AbstractTimestampEntity implements Serializable { ... } 

Sie können einfach @CreationTimestamp und @UpdateTimestamp :

 @CreationTimestamp @Temporal(TemporalType.TIMESTAMP) @Column(name = "create_date") private Date createDate; @UpdateTimestamp @Temporal(TemporalType.TIMESTAMP) @Column(name = "modify_date") private Date modifyDate; 

Sie können auch einen Interzeptor verwenden, um die Werte festzulegen

Erstellen Sie eine Schnittstelle namens TimeStamped, die von Ihren Entitäten implementiert wird

 public interface TimeStamped { public Date getCreatedDate(); public void setCreatedDate(Date createdDate); public Date getLastUpdated(); public void setLastUpdated(Date lastUpdatedDate); } 

Definieren Sie den Abfangjäger

 public class TimeStampInterceptor extends EmptyInterceptor { public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) { if (entity instanceof TimeStamped) { int indexOf = ArrayUtils.indexOf(propertyNames, "lastUpdated"); currentState[indexOf] = new Date(); return true; } return false; } public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { if (entity instanceof TimeStamped) { int indexOf = ArrayUtils.indexOf(propertyNames, "createdDate"); state[indexOf] = new Date(); return true; } return false; } } 

Und registriere es in der Sitzungsfabrik

Danke an alle, die geholfen haben. Nachdem ich selbst etwas recherchiert habe (ich bin der Typ, der die Frage gestellt hat), hier ist, was ich am meisten sinnvoll fand:

  • Database column type: Die zeitzonenunabhängige Anzahl von Millisekunden seit 1970 wird als decimal(20) weil 2 ^ 64 20 Ziffern hat und der Speicherplatz billig ist; lass uns einfach sein. Außerdem werde ich weder DEFAULT CURRENT_TIMESTAMP noch Trigger verwenden. Ich will keine Magie in der DB.

  • Java-Feldtyp: long . Der Unix-Zeitstempel wird über verschiedene Bibliotheken gut unterstützt, hat long keine Y2038-Probleme, Zeitstempel-Arithmetik ist schnell und einfach (hauptsächlich Operator < und Operator + , vorausgesetzt, dass keine Tage / Monate / Jahre in die Berechnungen involviert sind). Und, am wichtigsten, sind sowohl primitive long s als auch java.lang.Long s unveränderbar - effektiv vom Wert übergeben - im Gegensatz zu java.util.Date s; Ich wäre wirklich sauer, wenn ich etwas wie foo.getLastUpdate().setTime(System.currentTimeMillis()) beim Debuggen von jemand anderem finden würde.

  • Das ORM-Framework sollte für das automatische Ausfüllen der Daten verantwortlich sein.

  • Ich habe das noch nicht getestet, aber ich @Temporal mir nur die Dokumente an. Ich @Temporal davon aus, dass @Temporal die Aufgabe übernehmen wird; nicht sicher, ob ich @Version für diesen Zweck verwenden könnte. @PrePersist und @PreUpdate sind gute Alternativen, um das manuell zu steuern. Das Hinzufügen zum Layer-Supertyp (allgemeine Basisklasse) für alle Entitäten ist eine nette Idee, vorausgesetzt, Sie möchten für alle Ihre Entitäten einen Timestamping durchführen.

Mit Oliviers Lösung können Sie bei Update-statementen auf Folgendes stoßen:

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Die Spalte ‘created’ darf nicht null sein

Um dies zu lösen, fügen Sie update = false zur @Column-Annotation des Attributs “created” hinzu:

 @Temporal(TemporalType.TIMESTAMP) @Column(name = "created", nullable = false, updatable=false) private Date created; 

Wenn Sie die Session-API verwenden, funktionieren die PrePersist- und PreUpdate-Callbacks nicht gemäß dieser Antwort .

Ich verwende die persist () -Methode von Hibernate Session in meinem Code, so dass ich diese function nur mit dem unten stehenden Code ausführen und diesem Blogbeitrag folgen konnte (der auch in der Antwort steht ).

 @MappedSuperclass public abstract class AbstractTimestampEntity { @Temporal(TemporalType.TIMESTAMP) @Column(name = "created") private Date created=new Date(); @Temporal(TemporalType.TIMESTAMP) @Column(name = "updated") @Version private Date updated; public Date getCreated() { return created; } public void setCreated(Date created) { this.created = created; } public Date getUpdated() { return updated; } public void setUpdated(Date updated) { this.updated = updated; } } 

Ein guter Ansatz ist eine gemeinsame Basisklasse für alle Ihre Entitäten. In dieser Basisklasse können Sie Ihre id-Eigenschaft haben, wenn sie in allen Ihren Entitäten (ein gemeinsames Design) allgemein genannt wird, sowie die Eigenschaften für die Erstellung und das letzte Aktualisierungsdatum.

Für das Erstellungsdatum behalten Sie einfach die Eigenschaft java.util.Date bei . Stellen Sie sicher, dass Sie es immer mit new Date () initialisieren.

Für das letzte Aktualisierungsfeld können Sie eine Timestamp-Eigenschaft verwenden, die Sie mit @Version zuordnen müssen. Mit dieser Annotation wird die Eigenschaft automatisch von Hibernate aktualisiert. Beachten Sie, dass Hibernate auch optimistisches Sperren verwendet (es ist eine gute Sache).

Nur zur Verstärkung: java.util.Calender ist nicht für Timestamps . java.util.Date ist für einen Moment in der Zeit, Agnostiker von regionalen Dingen wie Zeitzonen. Die meisten databaseen speichern Dinge auf diese Weise (auch wenn sie nicht erscheinen; dies ist normalerweise eine Zeitzoneneinstellung in der Client-Software; die Daten sind gut)

Als Datentyp in JAVA empfehle ich dringend, java.util.Date zu verwenden. Bei der Verwendung von Calendar stieß ich auf ziemlich unangenehme Zeitzonenprobleme. Sieh diesen Thread .

Zum Einstellen der Zeitstempel würde ich entweder einen AOP-Ansatz empfehlen oder einfach Trigger in der Tabelle verwenden (eigentlich ist das die einzige Sache, für die ich die Verwendung von Triggern jemals akzeptabel finde).

Sie könnten die Zeit als DateTime und in UTC speichern. Normalerweise verwende ich DateTime anstelle von Timestamp, da MySql beim Speichern und Abrufen der Daten Daten in UTC und zurück in die lokale Zeit konvertiert. Ich würde lieber diese Art von Logik an einem Ort behalten (Business-Ebene). Ich bin mir sicher, dass es andere Situationen gibt, in denen die Verwendung von Timestamp jedoch vorzuziehen ist.

Wir hatten eine ähnliche Situation. Wir benutzten Mysql 5.7.

 CREATE TABLE my_table ( ... updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); 

Das hat für uns funktioniert.