Für eine bestimmte Hibernate-Entität müssen die Erstellungszeit und die letzte Aktualisierung gespeichert werden. Wie würden Sie das gestalten?
Welche Datentypen würden Sie in der Datenbank verwenden (unter der Annahme von MySQL, möglicherweise in einer anderen Zeitzone als die JVM)? Werden die Datentypen zeitzonenabhängig sein?
Welche Datentypen würden Sie in Java verwenden (
Date
,Calendar
,long
, ...)?Wen würden Sie für das Festlegen der Zeitstempel verantwortlich machen - die Datenbank, das ORM-Framework (Ruhezustand) oder den Anwendungsprogrammierer?
Welche Anmerkungen würden Sie für das Mapping verwenden (z. B.
@Temporal
)?
Ich suche nicht nur eine funktionierende Lösung, sondern auch eine sichere und gut konzipierte Lösung.
Sie können einfach verwenden
@CreationTimestamp
und@UpdateTimestamp
:quelle
TemporalType.DATE
im ersten Fall undTemporalType.TIMESTAMP
im zweiten.@CreationTimestamp
und@UpdateTimestamp
man braucht entweder etwas@Column(..., columnDefinition = "timestamp default current_timestamp")
oder verwendet@PrePersist
und@PreUpdate
(letzteres stellt auch gut sicher, dass Kunden keinen anderen Wert festlegen können).nullable=false
aus der@Column(name = "create_date" , nullable=false)
Arbeit zu entfernenIch nahm die Ressourcen in diesem Beitrag zusammen mit Informationen, die links und rechts aus verschiedenen Quellen stammen, und kam mit dieser eleganten Lösung, um die folgende abstrakte Klasse zu erstellen
und lassen Sie alle Ihre Entitäten es erweitern, zum Beispiel:
quelle
not-null property references a null or transient value: package.path.ClassName.created
@Column(name = "updated", nullable = false, insertable = false)
, damit es funktioniert. Interessant, dass diese Antwort so viele positive1. Welche Datenbankspaltentypen sollten Sie verwenden?
Ihre erste Frage war:
In MySQL
TIMESTAMP
verschiebt der Spaltentyp von der lokalen Zeitzone des JDBC-Treibers in die Datenbank-Zeitzone, kann jedoch nur Zeitstempel bis zu speichern'2038-01-19 03:14:07.999999
, sodass dies nicht die beste Wahl für die Zukunft ist.Verwenden Sie
DATETIME
stattdessen besser , da diese Obergrenze nicht begrenzt ist. AllerdingsDATETIME
ist nicht bekannt , Zeitzone. Aus diesem Grund ist es am besten, UTC auf der Datenbankseite zu verwenden und diehibernate.jdbc.time_zone
Eigenschaft Hibernate zu verwenden.2. Welchen Entitätseigenschaftstyp sollten Sie verwenden?
Ihre zweite Frage war:
Auf der Java-Seite können Sie Java 8 verwenden
LocalDateTime
. Sie können auch das Legacy verwendenDate
, aber die Java 8-Datums- / Zeittypen sind besser, da sie unveränderlich sind und beim Protokollieren keine Zeitzonenverschiebung in die lokale Zeitzone durchführen.Jetzt können wir auch diese Frage beantworten:
Wenn Sie die Eigenschaft
LocalDateTime
oderjava.sql.Timestamp
zum@Temporal
Zuordnen einer Zeitstempel-Entitätseigenschaft verwenden, müssen Sie diese nicht verwenden, da HIbernate bereits weiß, dass diese Eigenschaft als JDBC-Zeitstempel gespeichert werden soll.Nur wenn Sie verwenden
java.util.Date
, müssen Sie die@Temporal
Anmerkung wie folgt angeben :Aber es ist viel besser, wenn Sie es so abbilden:
So generieren Sie die Prüfspaltenwerte
Ihre dritte Frage war:
Es gibt viele Möglichkeiten, wie Sie dieses Ziel erreichen können. Sie können der Datenbank erlauben, dies zu tun.
Für die
create_on
Spalte können Sie eineDEFAULT
DDL-Einschränkung verwenden, z.Für die
updated_on
Spalte können Sie einen DB-Trigger verwenden, um den Spaltenwert mit festzulegenCURRENT_TIMESTAMP()
jeder Änderung einer bestimmten Zeile festzulegen.Oder verwenden Sie JPA oder Hibernate, um diese festzulegen.
Angenommen, Sie haben die folgenden Datenbanktabellen:
Und jede Tabelle hat Spalten wie:
created_by
created_on
updated_by
updated_on
Verwenden des Ruhezustands
@CreationTimestamp
und von@UpdateTimestamp
AnmerkungenDer Ruhezustand bietet die Anmerkungen
@CreationTimestamp
und@UpdateTimestamp
, mit denen die Spaltencreated_on
und zugeordnet werdenupdated_on
können.Sie können
@MappedSuperclass
eine Basisklasse definieren, die von allen Entitäten erweitert wird:Und alle Entitäten werden das
BaseEntity
wie folgt erweitern :Selbst wenn die Eigenschaften
createdOn
undupdateOn
durch die Ruhezustandsspezifischen@CreationTimestamp
und@UpdateTimestamp
Anmerkungen festgelegt werden, müssen fürcreatedBy
undupdatedBy
ein Anwendungsrückruf registriert werden, wie in der folgenden JPA-Lösung dargestellt.Verwenden von JPA
@EntityListeners
Sie können die Überwachungseigenschaften in eine eingebettete Datei einkapseln:
Erstellen Sie eine
AuditListener
, um die Überwachungseigenschaften festzulegen:Um das zu registrieren
AuditListener
, können Sie die@EntityListeners
JPA-Anmerkung verwenden:quelle
datetime
übertimestamp
. Sie möchten, dass Ihre Datenbank die Zeitzone Ihrer Zeitstempel kennt. Dies verhindert Zeitzonenkonvertierungsfehler.timestsmp
Typ speichert keine Zeitzoneninformationen. Es wird nur ein Gespräch von App TZ zu DB TZ geführt. In der Realität möchten Sie die Client-TZ separat speichern und die Konversation in der Anwendung durchführen, bevor Sie die Benutzeroberfläche rendern.timestamp
ist immer in UTC. MySQL konvertiertTIMESTAMP
Werte aus der aktuellen Zeitzone zum Speichern in UTC und zum Abrufen von UTC zurück in die aktuelle Zeitzone. MySQL-Dokumentation: Die Typen DATE, DATETIME und TIMESTAMPSie können die Werte auch mit einem Interceptor festlegen
Erstellen Sie eine Schnittstelle namens TimeStamped, die Ihre Entitäten implementieren
Definieren Sie den Interceptor
Und registrieren Sie es bei der Session Factory
quelle
Mit Oliviers Lösung können Sie bei Update-Anweisungen auf Folgendes stoßen:
Um dies zu lösen, fügen Sie updatable = false zur @ Column-Annotation des Attributs "created" hinzu:
quelle
@Version
. Wenn eine Entität festgelegt ist, werden zwei Aufrufe getätigt, einer zum Speichern und ein weiterer zum Aktualisieren. Aus diesem Grund stand ich vor dem gleichen Problem. Sobald ich hinzugefügt habe, hat@Column(updatable = false)
es mein Problem gelöst.Vielen Dank an alle, die geholfen haben. Nachdem ich selbst einige Nachforschungen angestellt habe (ich bin der Typ, der die Frage gestellt hat), fand ich Folgendes am sinnvollsten:
Datenbankspaltentyp: Die zeitzonenunabhängige Anzahl von Millisekunden seit 1970, dargestellt als
decimal(20)
2 ^ 64 mit 20 Ziffern und Speicherplatz ist billig. Lassen Sie uns einfach sein. Außerdem werde ich wederDEFAULT CURRENT_TIMESTAMP
noch Trigger verwenden. Ich will keine Magie in der DB.Java-Feldtyp :
long
. Der Unix-Zeitstempel wird in verschiedenenlong
Bibliotheken gut unterstützt, hat keine Y2038-Probleme, die Zeitstempelarithmetik ist schnell und einfach (hauptsächlich Operator<
und Operator+
, vorausgesetzt, dass keine Tage / Monate / Jahre in die Berechnungen involviert sind). Und vor allem sind sowohl primitivelong
s als auchjava.lang.Long
s unveränderlich - im Gegensatz zujava.util.Date
s effektiv vom Wert übergeben ; Ich wäre wirklich sauer,foo.getLastUpdate().setTime(System.currentTimeMillis())
wenn ich so etwas wie beim Debuggen des Codes eines anderen finden würde .Das ORM-Framework sollte für das automatische Ausfüllen der Daten verantwortlich sein.
Ich habe dies noch nicht getestet, aber ich schaue mir nur die Dokumente an, von denen ich annehme, dass
@Temporal
sie den Job machen werden. Ich bin mir nicht sicher, ob ich es@Version
für diesen Zweck verwenden könnte.@PrePersist
und@PreUpdate
sind gute Alternativen, um dies manuell zu steuern. Das Hinzufügen zum Layer-Supertyp (gemeinsame Basisklasse) für alle Entitäten ist eine nette Idee, vorausgesetzt, Sie möchten wirklich Zeitstempel für alle Ihre Entitäten.quelle
Wenn Sie die Sitzungs-API verwenden, funktionieren die PrePersist- und PreUpdate-Rückrufe gemäß dieser Antwort nicht .
Ich verwende die persist () -Methode von Hibernate Session in meinem Code, sodass ich diese Funktion nur mit dem folgenden Code und dem Folgen dieses Blogposts (ebenfalls in der Antwort veröffentlicht ) durchführen konnte.
quelle
updated.clone()
sonst zurückgeben, können andere Komponenten den internen Zustand (Datum) manipulierenJetzt gibt es auch Anmerkungen zu @CreatedDate und @LastModifiedDate.
=> https://programmingmitra.blogspot.fr/2017/02/automatic-spring-data-jpa-auditing-saving-CreatedBy-createddate-lastmodifiedby-lastmodifieddate-automatically.html
(Frühlingsrahmen)
quelle
Der folgende Code hat bei mir funktioniert.
quelle
protected Integer id;
wieprotected
in der.getId()
Ein guter Ansatz besteht darin, eine gemeinsame Basisklasse für alle Ihre Entitäten zu haben. In dieser Basisklasse können Sie Ihre id-Eigenschaft haben, wenn sie in all Ihren Entitäten (einem gemeinsamen Design), Ihrer Erstellung und den Eigenschaften für das Datum der letzten Aktualisierung allgemein benannt ist.
Für das Erstellungsdatum behalten Sie einfach eine java.util.Date- Eigenschaft bei. Stellen Sie sicher, dass Sie es immer mit new Date () initialisieren .
Für das letzte Aktualisierungsfeld können Sie eine Timestamp-Eigenschaft verwenden. Sie müssen sie mit @Version zuordnen. Mit dieser Anmerkung wird die Eigenschaft von Hibernate automatisch aktualisiert. Beachten Sie, dass Hibernate auch optimistische Sperren anwendet (eine gute Sache).
quelle
Nur um zu verstärken:
java.util.Calender
ist nicht für Zeitstempel .java.util.Date
ist für einen Moment unabhängig von regionalen Dingen wie Zeitzonen. Die meisten Datenbanken speichern Dinge auf diese Weise (auch wenn dies nicht der Fall zu sein scheint; dies ist normalerweise eine Zeitzoneneinstellung in der Client-Software; die Daten sind gut).quelle
Als Datentyp in JAVA empfehle ich dringend, java.util.Date zu verwenden. Bei der Verwendung des Kalenders sind mir ziemlich unangenehme Zeitzonenprobleme aufgetreten. Siehe diesen Thread .
Zum Einstellen der Zeitstempel würde ich empfehlen, entweder einen AOP-Ansatz zu verwenden oder einfach Trigger für die Tabelle zu verwenden (eigentlich ist dies das einzige, was ich jemals für akzeptabel halte).
quelle
Sie können die Uhrzeit als DateTime und in UTC speichern. Normalerweise verwende ich DateTime anstelle von Timestamp, da MySql beim Speichern und Abrufen der Daten Datumsangaben in UTC und zurück in die Ortszeit konvertiert. Ich würde diese Logik lieber an einem Ort aufbewahren (Business-Schicht). Ich bin mir sicher, dass es andere Situationen gibt, in denen die Verwendung von Timestamp vorzuziehen ist.
quelle
Wir hatten eine ähnliche Situation. Wir haben MySQL 5.7 verwendet.
Das hat bei uns funktioniert.
quelle
@PrePersist
und@PrePersist
decken Sie einen solchen Fall nicht ab.Wenn wir verwenden @Transactional in unseren Methoden verwenden, speichern @CreationTimestamp und @UpdateTimestamp den Wert in DB, geben jedoch nach Verwendung von save (...) null zurück.
In dieser Situation hat saveAndFlush (...) den Trick gemacht
quelle
Ich denke, es ist ordentlicher, dies nicht in Java-Code zu tun. Sie können einfach den Spaltenstandardwert in der MySql-Tabellendefinition festlegen.
quelle