Zeitstempel für Erstellung und Zeitstempel für die letzte Aktualisierung mit Hibernate und MySQL

244

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.

ngn
quelle

Antworten:

266

Wenn Sie die JPA-Annotationen verwenden, können Sie Folgendes verwenden @PrePersistund @PreUpdateEvent-Hooks tun dies:

@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 @EntityListenerAnnotation für die Klasse verwenden und den Ereigniscode in einer externen Klasse platzieren.

Guðmundur Bjarni
quelle
7
Funktioniert problemlos in J2SE, da @PrePersist und @PerUpdate JPA-Anmerkungen sind.
Kdeveloper
2
@Kumar - Wenn Sie eine einfache Hibernate-Sitzung (anstelle von JPA) verwenden, können Sie die Listener für Hibernate-Ereignisse ausprobieren, obwohl dies im Vergleich zu JPA-Anmerkungen nicht sehr elegant und kompakt ist.
Shailendra
43
Im aktuellen Ruhezustand mit JPA kann man "@CreationTimestamp" und "@UpdateTimestamp"
Florian Loch
@FlorianLoch gibt es ein Äquivalent für Datum anstelle von Zeitstempel? Oder müsste ich meine eigenen erstellen?
Mike
150

Sie können einfach verwenden @CreationTimestampund @UpdateTimestamp:

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

@UpdateTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "modify_date")
private Date modifyDate;
idmitriev
quelle
3
danke bro so eine kleine sache muss den zeitstempel aktualisieren. Ich wusste es nicht. du hast meinen Tag gerettet.
Virendra Sagar
Es gibt TemporalType.DATEim ersten Fall und TemporalType.TIMESTAMPim zweiten.
v.ladynev
Wollen Sie damit sagen, dass dies auch automatisch die Werte festlegt? Das ist nicht meine Erfahrung; es scheint sogar mit @CreationTimestampund @UpdateTimestampman braucht entweder etwas @Column(..., columnDefinition = "timestamp default current_timestamp")oder verwendet @PrePersistund @PreUpdate(letzteres stellt auch gut sicher, dass Kunden keinen anderen Wert festlegen können).
Arjan
2
Wenn ich das Objekt aktualisiere und beibehalten habe, hat der BD das create_date verloren ... warum?
Brenno Leal
1
Ich bin mein Fall nullable=falseaus der @Column(name = "create_date" , nullable=false)Arbeit zu entfernen
Shantaram Tupe
113

Ich 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

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 es erweitern, zum Beispiel:

@Entity
@Table(name = "campaign")
public class Campaign extends AbstractTimestampEntity implements Serializable {
...
}
Olivier Refalo
quelle
5
Dies ist so lange gut, bis Sie Ihren Entitäten verschiedene exklusive Verhaltensweisen hinzufügen möchten (und Sie nicht mehr als eine Basisklasse erweitern können). afaik der einzige Weg, um den gleichen Effekt ohne eine Basisklasse zu erzielen, ist, obwohl Aspektj itd oder Ereignis-Listener @kieren dixon answer
gpilotino
3
Ich würde dies mit einem MySQL-Trigger tun, damit diese Felder auch dann aktualisiert werden, wenn die vollständige Entität nicht gespeichert oder durch eine externe Anwendung oder manuelle Abfrage geändert wird.
Webnet
3
not-null property references a null or transient value: package.path.ClassName.created
Können
@ RishiAgar, Nein, habe ich nicht. Aber jetzt habe ich meiner Eigenschaft vom Standardkonstruktor ein Datum zugewiesen. Werde es dich wissen lassen, sobald ich es gefunden habe.
Sumit Ramteke
1
Ändern Sie es in @Column(name = "updated", nullable = false, insertable = false), damit es funktioniert. Interessant, dass diese Antwort so viele positive
Stimmen
20

1. Welche Datenbankspaltentypen sollten Sie verwenden?

Ihre erste Frage war:

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?

In MySQL TIMESTAMPverschiebt 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 DATETIMEstattdessen besser , da diese Obergrenze nicht begrenzt ist. Allerdings DATETIMEist nicht bekannt , Zeitzone. Aus diesem Grund ist es am besten, UTC auf der Datenbankseite zu verwenden und die hibernate.jdbc.time_zoneEigenschaft Hibernate zu verwenden.

Weitere Informationen zur hibernate.jdbc.time_zoneEinstellung finden Sie in diesem Artikel .

2. Welchen Entitätseigenschaftstyp sollten Sie verwenden?

Ihre zweite Frage war:

Welche Datentypen würden Sie in Java verwenden (Datum, Kalender, lang, ...)?

Auf der Java-Seite können Sie Java 8 verwenden LocalDateTime. Sie können auch das Legacy verwenden Date, aber die Java 8-Datums- / Zeittypen sind besser, da sie unveränderlich sind und beim Protokollieren keine Zeitzonenverschiebung in die lokale Zeitzone durchführen.

Weitere Informationen zu den von Hibernate unterstützten Java 8-Datums- / Uhrzeittypen finden Sie in diesem Artikel .

Jetzt können wir auch diese Frage beantworten:

Welche Anmerkungen würden Sie für das Mapping verwenden (z. B. @Temporal)?

Wenn Sie die Eigenschaft LocalDateTimeoder java.sql.Timestampzum @TemporalZuordnen 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 @TemporalAnmerkung wie folgt angeben :

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_on")
private Date createdOn;

Aber es ist viel besser, wenn Sie es so abbilden:

@Column(name = "created_on")
private LocalDateTime createdOn;

So generieren Sie die Prüfspaltenwerte

Ihre dritte Frage war:

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)?

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_onSpalte können Sie eine DEFAULTDDL-Einschränkung verwenden, z.

ALTER TABLE post 
ADD CONSTRAINT created_on_default 
DEFAULT CURRENT_TIMESTAMP() FOR created_on;

Für die updated_onSpalte 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:

Datenbanktabellen mit Überwachungsspalten

Und jede Tabelle hat Spalten wie:

  • created_by
  • created_on
  • updated_by
  • updated_on

Verwenden des Ruhezustands @CreationTimestampund von @UpdateTimestampAnmerkungen

Der Ruhezustand bietet die Anmerkungen @CreationTimestampund @UpdateTimestamp, mit denen die Spalten created_onund zugeordnet werden updated_onkönnen.

Sie können @MappedSuperclasseine Basisklasse definieren, die von allen Entitäten erweitert wird:

@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
}

Und alle Entitäten werden das BaseEntitywie folgt erweitern :

@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
}

Weitere Informationen zur Verwendung @MappedSuperclassfinden Sie in diesem Artikel .

Selbst wenn die Eigenschaften createdOnund updateOndurch die Ruhezustandsspezifischen @CreationTimestampund @UpdateTimestampAnmerkungen festgelegt werden, müssen für createdByund updatedByein 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:

@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
}

Erstellen Sie eine AuditListener, um die Überwachungseigenschaften festzulegen:

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());
    }
}

Um das zu registrieren AuditListener, können Sie die @EntityListenersJPA-Anmerkung verwenden:

@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
}

Weitere Informationen zum Implementieren von Audit-Eigenschaften mit dem JPA @EntityListenerfinden Sie in diesem Artikel .

Vlad Mihalcea
quelle
Sehr gründliche Antwort, danke. Ich bin nicht einverstanden zu bevorzugen datetimeüber timestamp. Sie möchten, dass Ihre Datenbank die Zeitzone Ihrer Zeitstempel kennt. Dies verhindert Zeitzonenkonvertierungsfehler.
Ole VV
Der timestsmpTyp 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.
Vlad Mihalcea
Richtig. MySQL timestampist immer in UTC. MySQL konvertiert TIMESTAMPWerte 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 TIMESTAMP
Ole VV
17

Sie können die Werte auch mit einem Interceptor festlegen

Erstellen Sie eine Schnittstelle namens TimeStamped, die Ihre Entitäten implementieren

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

Definieren Sie den Interceptor

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 registrieren Sie es bei der Session Factory

Kieren Dixon
quelle
1
Funktioniert, danke. Zusätzliche Informationen docs.jboss.org/hibernate/core/4.0/manual/en-US/html_single/…
Andrii Nemchenko
Dies ist eine Lösung, wenn Sie mit SessionFactory anstelle von EntityManager arbeiten!
Olivmir
Nur für diejenigen, die unter einem ähnlichen Problem leiden wie ich in diesem Zusammenhang: Wenn Ihre Entität diese zusätzlichen Felder (createdAt, ...) nicht selbst definiert, sondern von einer übergeordneten Klasse erbt, muss diese übergeordnete Klasse mit Anmerkungen versehen werden mit @MappedSuperclass - andernfalls findet Hibernate diese Felder nicht.
Olivmir
17

Mit Oliviers Lösung können Sie bei Update-Anweisungen 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 updatable = false zur @ Column-Annotation des Attributs "created" hinzu:

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created", nullable = false, updatable=false)
private Date created;
endriju
quelle
1
Wir verwenden @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.
Ganesh Satpute
12

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 weder DEFAULT CURRENT_TIMESTAMPnoch Trigger verwenden. Ich will keine Magie in der DB.

  • Java-Feldtyp : long. Der Unix-Zeitstempel wird in verschiedenen longBibliotheken 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 primitive longs als auch java.lang.Longs unveränderlich - im Gegensatz zu java.util.Dates 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 @Temporalsie den Job machen werden. Ich bin mir nicht sicher, ob ich es @Versionfür diesen Zweck verwenden könnte. @PrePersistund @PreUpdatesind 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.

ngn
quelle
Während Longs und Longs unveränderlich sein können, hilft Ihnen dies in der von Ihnen beschriebenen Situation nicht weiter. Sie können immer noch foo.setLastUpdate (new Long (System.currentTimeMillis ()) sagen;
Ian McLaird
2
Das ist gut. Für den Ruhezustand ist der Setter ohnehin erforderlich (oder er versucht, durch Reflexion direkt auf das Feld zuzugreifen). Ich habe über Schwierigkeiten gesprochen, herauszufinden, wer den Zeitstempel aus unserem Anwendungscode ändert. Es ist schwierig, wenn Sie dies mit einem Getter tun können.
ngn
Ich stimme Ihrer Behauptung zu, dass das ORM-Framework für das automatische Ausfüllen des Datums verantwortlich sein sollte, aber ich würde noch einen Schritt weiter gehen und sagen, dass das Datum von der Uhr des Datenbankservers und nicht vom Client aus festgelegt werden sollte. Mir ist nicht klar, ob dies dieses Ziel erreicht. In SQL kann ich dies mithilfe der Funktion sysdate tun, aber ich weiß nicht, wie dies in Hibernate oder einer JPA-Implementierung geschehen soll.
MiguelMunoz
Ich will keine Magie in der DB. Ich verstehe, was Sie meinen, aber ich möchte die Tatsache berücksichtigen, dass sich die Datenbank vor schlechten / neuen / ahnungslosen Entwicklern schützen sollte. Datenintegrität ist in einem großen Unternehmen sehr wichtig. Sie können sich nicht darauf verlassen, dass andere gute Daten einfügen. Einschränkungen, Standardeinstellungen und FKs helfen dabei.
Icegras
6

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.

@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;
    }
}
Vicch
quelle
Sollte geklonte Objekte wie updated.clone()sonst zurückgeben, können andere Komponenten den internen Zustand (Datum) manipulieren
1ambda
3

Der folgende Code hat bei mir funktioniert.

package com.my.backend.models;

import java.util.Date;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

import com.fasterxml.jackson.annotation.JsonIgnore;

import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import lombok.Getter;
import lombok.Setter;

@MappedSuperclass
@Getter @Setter
public class BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Integer id;

    @CreationTimestamp
    @ColumnDefault("CURRENT_TIMESTAMP")
    protected Date createdAt;

    @UpdateTimestamp
    @ColumnDefault("CURRENT_TIMESTAMP")
    protected Date updatedAt;
}
prranay
quelle
Hallo, warum sollten wir protected Integer id;wie protectedin der .getId()
Elternklasse
2

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).

bernardn
quelle
2
Die Verwendung einer Zeitstempelspalte für optimistisches Sperren ist eine schlechte Idee. Verwenden Sie immer eine ganzzahlige Versionsspalte. Grund dafür ist, dass sich 2 JVMs möglicherweise zu unterschiedlichen Zeiten befinden und möglicherweise keine Millisekundengenauigkeit aufweisen. Wenn Sie stattdessen den DB-Zeitstempel in den Ruhezustand versetzen, bedeutet dies zusätzliche Auswahlen aus der DB. Verwenden Sie stattdessen einfach die Versionsnummer.
Sethu
2

Nur um zu verstärken: java.util.Calenderist nicht für Zeitstempel . java.util.Dateist 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).

davetron5000
quelle
1

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).

huo73
quelle
1

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.

mmacaulay
quelle
1

Wir hatten eine ähnliche Situation. Wir haben MySQL 5.7 verwendet.

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

Das hat bei uns funktioniert.

amdg
quelle
Dies funktioniert auch in einem Fall, in dem die Daten durch eine SQL-Abfrage direkt in der Datenbank geändert werden. @PrePersistund @PrePersistdecken Sie einen solchen Fall nicht ab.
Pidabrow
1

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

chomp
quelle
0

Ich denke, es ist ordentlicher, dies nicht in Java-Code zu tun. Sie können einfach den Spaltenstandardwert in der MySql-Tabellendefinition festlegen. Geben Sie hier die Bildbeschreibung ein

Wayne Wei
quelle