it-swarm.com.de

Erstellungszeitstempel und Zeitstempel der letzten Aktualisierung mit Hibernate und MySQL

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, dass sich MySQL möglicherweise in einer anderen Zeitzone als die JVM befindet)? Werden die Datentypen zeitzonenabhängig sein?

  • Welche Datentypen würden Sie in Java (Date, Calendar, long, ...) verwenden?

  • 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 die Zuordnung verwenden (z. B. @Temporal)?

Ich bin nicht nur auf der Suche nach einer funktionierenden Lösung, sondern auch nach einer sicheren und durchdachten Lösung.

222
ngn

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

@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 du kannst das @EntityListener Anmerkung zur Klasse und Platzieren des Ereigniscodes in einer externen Klasse.

251

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;
122
idmitriev

Indem ich die Ressourcen in diesem Beitrag zusammen mit Informationen aus verschiedenen Quellen nach links und rechts genommen habe, habe ich mit dieser eleganten Lösung die folgende abstrakte Klasse erstellt

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 {
...
}
106
Olivier Refalo

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

Erstellen Sie eine Schnittstelle mit dem Namen 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 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 registrieren Sie es bei der Session Factory

17
Kieren Dixon

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;
14
endriju

Vielen Dank an alle, die mitgeholfen haben. Nachdem ich einige Nachforschungen angestellt habe (ich bin derjenige, der die Frage gestellt hat), habe ich Folgendes für sinnvoll befunden:

  • Typ der Datenbankspalte: Die zeitzonenunabhängige Anzahl von Millisekunden seit 1970, dargestellt als decimal(20), da 2 ^ 64 20 Stellen hat und der Speicherplatz günstig 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 in verschiedenen Bibliotheken gut unterstützt, long hat keine Probleme mit Y2038, die Zeitstempel-Arithmetik ist schnell und einfach (hauptsächlich Operator < Und Operator +, Vorausgesetzt keine Tage/Monate)/Jahre sind an den Berechnungen beteiligt). Und am wichtigsten ist, dass beide primitiven longs und Java.lang.Longnveränderlich - im Gegensatz zu Java.util.Date - effektiv als Wert übergeben werden. Ich wäre wirklich sauer, wenn ich etwas wie foo.getLastUpdate().setTime(System.currentTimeMillis()) finden würde, wenn ich den Code eines anderen debuggen würde.

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

  • Ich habe das noch nicht getestet, aber ich gehe nur davon aus, dass @Temporal den Job erledigt; Ich bin mir nicht sicher, ob ich @Version für diesen Zweck verwenden könnte. @PrePersist und @PreUpdate sind gute Alternativen, um dies manuell zu steuern. Dies dem Layer-Supertyp (gemeinsame Basisklasse) für alle Entitäten hinzuzufügen, ist eine nette Idee, vorausgesetzt, Sie möchten wirklich eine Zeitstempelung für alle Ihrer Entitäten.

12
ngn

Wenn Sie die Sitzungs-API verwenden, funktionieren die PrePersist- und PreUpdate-Rückrufe nicht entsprechend answer .

Ich verwende die persist () -Methode von Hibernate Session in meinem Code, so dass ich diese Arbeit nur mit dem folgenden Code ausführen konnte Blog-Beitrag (auch in Antwort =).

@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;
    }
}
6
vicch
3
Oreste Viron

Nur zur Verstärkung: Java.util.Calender ist nicht für Zeitstempel. Java.util.Date ist für einen Moment agnostisch gegenüber regionalen Dingen wie Zeitzonen. Die meisten Datenbankspeicherfunktionen werden auf diese Weise ausgeführt (auch wenn dies nicht der Fall zu sein scheint; dies ist normalerweise eine Zeitzoneneinstellung in der Client-Software; die Daten sind in Ordnung).

2
davetron5000

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;
}
2
prranay

Ein guter Ansatz ist es, 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 Entwurf), Ihren Eigenschaften für das Erstellungsdatum und das Datum der letzten Aktualisierung gemeinsam benannt ist.

Für das Erstellungsdatum behalten Sie einfach die Eigenschaft Java.util.Date. 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 von Hibernate automatisch aktualisiert. Beachten Sie, dass Hibernate auch optimistische Sperren anwendet (das ist eine gute Sache).

2
bernardn

Sie können die Uhrzeit als DateTime und in UTC speichern. Ich verwende in der Regel DateTime anstelle von Timestamp, da MySql beim Speichern und Abrufen der Daten Datumsangaben in UTC und zurück in die Ortszeit umwandelt. Ich möchte diese Art von Logik lieber an einem Ort aufbewahren (Business-Ebene). Ich bin mir sicher, dass es andere Situationen gibt, in denen die Verwendung von Timestamp vorzuziehen ist.

1
mmacaulay

Als Datentyp Java Ich empfehle dringend, Java.util.Date zu verwenden. Bei der Verwendung des Kalenders sind ziemlich unangenehme Zeitzonenprobleme aufgetreten. Siehe hierzu Thread .

Zum Setzen der Zeitstempel würde ich entweder einen AOP-Ansatz empfehlen oder Sie könnten einfach Trigger für die Tabelle verwenden (eigentlich ist dies das einzige, was ich jemals für akzeptabel halte, Trigger zu verwenden).

1
huo73

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.

0
amdg