Znacznik czasu utworzenia i znacznik czasu ostatniej aktualizacji z Hibernacja i MySQL

244

W przypadku pewnej jednostki Hibernacji mamy obowiązek przechowywania czasu jej utworzenia i ostatniej aktualizacji. Jak byś to zaprojektował?

  • Jakich typów danych używałbyś w bazie danych (zakładając MySQL, być może w innej strefie czasowej niż JVM)? Czy typy danych będą uwzględniać strefę czasową?

  • Jakie typy danych należałoby użyć w Javie ( Date, Calendar, long, ...)?

  • Kogo udzieliłbyś odpowiedzialności za ustawienie znaczników czasu - bazy danych, frameworka ORM (Hibernacja) lub programisty aplikacji?

  • Jakich adnotacji użyłbyś do mapowania (np. @Temporal)?

Szukam nie tylko działającego rozwiązania, ale także bezpiecznego i dobrze zaprojektowanego rozwiązania.

ngn
źródło

Odpowiedzi:

266

Jeśli używasz adnotacji JPA, możesz użyć haczyków @PrePersisti @PreUpdatezdarzeń:

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

lub możesz użyć @EntityListeneradnotacji na klasie i umieścić kod zdarzenia w klasie zewnętrznej.

Guðmundur Bjarni
źródło
7
Działa bez żadnych problemów w J2SE, ponieważ @PrePersist i @PerUpdate są adnotacjami JPA.
Kdeveloper
2
@Kumar - jeśli używasz zwykłej sesji hibernacji (zamiast JPA), możesz wypróbować nasłuchiwania zdarzeń hibernacji, chociaż nie jest to zbyt eleganckie i kompaktowe w porównaniu z adnotacjami JPA.
Shailendra
43
W obecnym Hibernacji z JPA można używać „@CreationTimestamp” i „@UpdateTimestamp”
Florian Loch
@FlorianLoch czy istnieje odpowiednik daty zamiast datownika? A może musiałbym stworzyć swój własny?
Mike
151

Możesz po prostu użyć @CreationTimestampi @UpdateTimestamp:

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

@UpdateTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "modify_date")
private Date modifyDate;
idmitriew
źródło
3
dzięki stary, tak mała rzecz musi zaktualizować znacznik czasu. Nie wiedziałem uratowałeś mi dzień
Virendra Sagar
Jest TemporalType.DATEw pierwszym i TemporalType.TIMESTAMPdrugim przypadku.
v.ladynev
Czy mówisz, że to także automatycznie ustawia wartości? To nie jest moje doświadczenie; Wydaje się nawet @CreationTimestampi @UpdateTimestampjeden albo potrzebuje @Column(..., columnDefinition = "timestamp default current_timestamp")lub wykorzystanie @PrePersisti @PreUpdate(te ostatnie klienci również ładnie zapewniające nie można ustawić inną wartość).
Arjan
2
Kiedy aktualizuję obiekt i utrwalam go, bd stracił datę_tworzenia ... dlaczego?
Brenno Leal,
1
Usuwam moją skrzynkę nullable=falsez @Column(name = "create_date" , nullable=false)pracy
Shantaram Tupe
113

Biorąc zasoby w tym poście wraz z informacjami pobranymi z lewej i prawej strony z różnych źródeł, przyszedłem z tym eleganckim rozwiązaniem, utwórz następującą klasę abstrakcyjną

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

i niech wszystkie twoje byty ją rozszerzą, na przykład:

@Entity
@Table(name = "campaign")
public class Campaign extends AbstractTimestampEntity implements Serializable {
...
}
Olivier Refalo
źródło
5
jest to dobre, dopóki nie chcesz dodać różnych zachowań wyłącznych do swoich bytów (i nie możesz rozszerzyć więcej niż jednej klasy podstawowej). afaik jedynym sposobem na uzyskanie tego samego efektu bez klasy podstawowej jest aspektj itd. lub osoby nasłuchujące zdarzeń widzą odpowiedź @kieren dixon
gpilotino
3
Zrobiłbym to przy użyciu wyzwalacza MySQL, więc nawet jeśli pełny byt nie zostanie zapisany lub zostanie zmodyfikowany przez dowolną aplikację zewnętrzną lub ręczne zapytanie, nadal zaktualizuje te pola.
Webnet,
3
czy możesz podać mi jakiś działający przykład, ponieważ mam wyjąteknot-null property references a null or transient value: package.path.ClassName.created
Sumit Ramteke
@rishiAgar, Nie, nie mam. Ale na razie przypisałem datę do mojej właściwości z domyślnego konstruktora. Dadzę ci znać, gdy tylko znajdę.
Sumit Ramteke
1
Zmień go na, @Column(name = "updated", nullable = false, insertable = false)aby działał. Ciekawe, że ta odpowiedź, ale tak wiele upvotes ..
displayname
20

1. Jakich typów kolumn bazy danych należy użyć

Twoje pierwsze pytanie brzmiało:

Jakich typów danych używałbyś w bazie danych (zakładając MySQL, być może w innej strefie czasowej niż JVM)? Czy typy danych będą uwzględniać strefę czasową?

W MySQL TIMESTAMPtyp kolumny przesuwa się z lokalnej strefy czasowej sterownika JDBC do strefy czasowej bazy danych, ale może przechowywać tylko znaczniki czasu do '2038-01-19 03:14:07.999999, więc nie jest to najlepszy wybór na przyszłość.

Lepiej więc użyj DATETIMEzamiast tego, który nie ma tego górnego ograniczenia. Jednak DATETIMEnie jest świadomy strefy czasowej. Z tego powodu najlepiej używać UTC po stronie bazy danych i używać hibernate.jdbc.time_zonewłaściwości Hibernacja.

Aby uzyskać więcej informacji na temat hibernate.jdbc.time_zoneustawienia, sprawdź ten artykuł .

2. Jakiego typu właściwości encji należy użyć

Twoje drugie pytanie brzmiało:

Jakich typów danych używałbyś w Javie (data, kalendarz, długie, ...)?

Po stronie Java możesz używać Java 8 LocalDateTime. Możesz także użyć starszej wersji Date, ale typy daty / godziny Java 8 są lepsze, ponieważ są niezmienne i nie rejestrują strefy czasowej w lokalnej strefie czasowej podczas ich rejestrowania.

Więcej informacji o typach daty / godziny Java 8 obsługiwanych przez Hibernację można znaleźć w tym artykule .

Teraz możemy również odpowiedzieć na to pytanie:

Jakich adnotacji użyłbyś do mapowania (np. @Temporal)?

Jeśli używasz LocalDateTimelub java.sql.Timestampdo mapowania właściwości encji znacznika czasu, nie musisz jej używać, @Temporalponieważ HIbernate już wie, że ta właściwość ma zostać zapisana jako sygnatura czasowa JDBC.

Tylko jeśli używasz java.util.Date, musisz określić @Temporaladnotację:

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

Ale jest znacznie lepiej, jeśli zamapujesz to w następujący sposób:

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

Jak wygenerować wartości kolumny kontroli

Twoje trzecie pytanie brzmiało:

Kogo udzieliłbyś odpowiedzialności za ustawienie znaczników czasu - bazy danych, frameworka ORM (Hibernacja) lub programisty aplikacji?

Jakich adnotacji użyłbyś do mapowania (np. @Temporal)?

Istnieje wiele sposobów osiągnięcia tego celu. Możesz na to pozwolić bazie danych…

W przypadku create_onkolumny można użyć DEFAULTograniczenia DDL, takiego jak:

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

W przypadku updated_onkolumny można użyć wyzwalacza DB, aby ustawić wartość kolumny przy CURRENT_TIMESTAMP()każdej modyfikacji danego wiersza.

Lub użyj JPA lub Hibernacji, aby je ustawić.

Załóżmy, że masz następujące tabele bazy danych:

Tabele bazy danych z kolumnami kontroli

I każda tabela ma kolumny takie jak:

  • created_by
  • created_on
  • updated_by
  • updated_on

Korzystanie z hibernacji @CreationTimestampi @UpdateTimestampadnotacji

Hibernacja oferuje @CreationTimestampi @UpdateTimestampadnotacje, których można użyć do zmapowania kolumn created_oni updated_on.

Możesz użyć @MappedSuperclassdo zdefiniowania klasy bazowej, która zostanie rozszerzona przez wszystkie jednostki:

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

I wszystkie podmioty rozszerzą BaseEntity, w ten sposób:

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

Więcej informacji na temat korzystania znajduje @MappedSuperclasssię w tym artykule .

Jednak nawet jeśli właściwości createdOni updateOnsą ustawione przez Hibernację @CreationTimestampi @UpdateTimestampadnotacje, createdByi updatedBywymagają rejestracji wywołania zwrotnego aplikacji, jak pokazano w poniższym rozwiązaniu JPA.

Korzystanie z JPA @EntityListeners

Możesz zawrzeć właściwości kontroli w Embeddable:

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

I utwórz wartość, AuditListeneraby ustawić właściwości kontroli:

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

Aby zarejestrować AuditListener, możesz użyć @EntityListenersadnotacji JPA:

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

Aby uzyskać więcej informacji na temat wdrażania właściwości inspekcji za pomocą JPA @EntityListener, sprawdź ten artykuł .

Vlad Mihalcea
źródło
Bardzo dokładna odpowiedź, dzięki. Nie zgadzam się preferowanie datetimeponad timestamp. Chcesz, aby twoja baza danych znała strefę czasową twoich znaczników czasu. Zapobiega to błędom konwersji strefy czasowej.
Ole VV
timestsmpTyp nie przechowuje informacje o strefie czasowej. Po prostu rozmawia z aplikacji TZ do DB TZ. W rzeczywistości chcesz przechowywać klient TZ osobno i przeprowadzić rozmowę w aplikacji przed renderowaniem interfejsu użytkownika.
Vlad Mihalcea
Poprawny. MySQL timestampjest zawsze w UTC. MySQL konwertuje TIMESTAMPwartości z bieżącej strefy czasowej na UTC w celu przechowywania i z powrotem z UTC do bieżącej strefy czasowej w celu ich pobrania. Dokumentacja MySQL: Typy DATA, DATETIME i TIMESTAMP
Ole VV
17

Można również użyć przechwytywacza, aby ustawić wartości

Utwórz interfejs o nazwie TimeStamped, który implementują Twoje podmioty

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

Zdefiniuj przechwytywacz

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

I zarejestruj to w fabryce sesji

Kieren Dixon
źródło
1
Działa dzięki. Informacje dodatkowe docs.jboss.org/hibernate/core/4.0/manual/en-US/html_single/…
Andrii Nemchenko
To jedno rozwiązanie, jeśli pracujesz z SessionFactory zamiast EntityManager!
olivmir
Tylko dla tych, którzy cierpią z podobnym problemem jak ja w tym kontekście: jeśli twoja istota sama nie definiuje tych dodatkowych pól (utworzonych At ...), ale dziedziczy je od klasy nadrzędnej, wówczas należy nadać adnotację tej klasie nadrzędnej with @MappedSuperclass - w przeciwnym razie Hibernacja nie znajdzie tych pól.
olivmir
17

Dzięki rozwiązaniu Oliviera podczas aktualizacji instrukcji możesz natknąć się na:

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Kolumna „utworzona” nie może mieć wartości NULL

Aby rozwiązać ten problem, dodaj updatable = false do adnotacji @Column atrybutu „utworzono”:

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created", nullable = false, updatable=false)
private Date created;
endriju
źródło
1
Używamy @Version. Po ustawieniu encji wykonywane są dwa połączenia, jeden ma zostać zapisany, a drugi do aktualizacji. Z tego powodu miałem do czynienia z tym samym problemem. Po dodaniu @Column(updatable = false)rozwiązałem mój problem.
Ganesh Satpute
12

Dziękuję wszystkim, którzy pomogli. Po tym, jak sam przeprowadziłem badania (jestem facetem, który zadał pytanie), oto, co znalazłem najbardziej sensowne:

  • Typ kolumny bazy danych: agnostyczna liczba stref czasowych w milisekundach od 1970 r. Reprezentowana jako, decimal(20)ponieważ 2 ^ 64 ma 20 cyfr, a miejsce na dysku jest tanie; bądźmy szczerzy. Ponadto DEFAULT CURRENT_TIMESTAMPnie użyję ani wyzwalaczy. Nie chcę magii w DB.

  • Java typ pola: long. Uniksowy znacznik czasu jest dobrze obsługiwany w różnych bibliotekach, longnie ma problemów z Y2038, arytmetyka znaczników czasu jest szybka i łatwa (głównie operator <i operator +, zakładając, że w obliczeniach nie są uwzględniane dni / miesiące / lata). I, co najważniejsze, zarówno prymitywne, jak longi java.lang.Longsi są niezmienne - skutecznie przekazane przez wartość - w przeciwieństwie do java.util.Dates; Byłbym naprawdę wkurzony, gdybym znalazł coś takiego jak foo.getLastUpdate().setTime(System.currentTimeMillis())debugowanie kodu innej osoby.

  • Struktura ORM powinna być odpowiedzialna za automatyczne wypełnianie danych.

  • Nie testowałem tego jeszcze, ale patrząc tylko na dokumenty, które zakładam, że @Temporalwykonają zadanie; nie jestem pewien, czy mogę użyć @Versiondo tego celu. @PrePersisti @PreUpdatesą dobrą alternatywą do kontrolowania tego ręcznie. Dodanie tego do nadtypu warstwy (wspólnej klasy bazowej) dla wszystkich encji jest uroczym pomysłem, pod warunkiem, że naprawdę chcesz znaczników czasu dla wszystkich swoich encji.

ngn
źródło
Chociaż długie i długie mogą być niezmienne, nie pomoże ci to w opisanej sytuacji. Nadal mogą powiedzieć foo.setLastUpdate (nowy Long (System.currentTimeMillis ());
Ian McLaird
2
W porządku. Hibernacja i tak wymaga ustawienia (lub spróbuje uzyskać dostęp do pola bezpośrednio przez odbicie). Mówiłem o trudnościach w ściganiu, kto modyfikuje znacznik czasu z naszego kodu aplikacji. Jest to trudne, kiedy możesz to zrobić za pomocą gettera.
ngn
Zgadzam się z twoim twierdzeniem, że struktura ORM powinna być odpowiedzialna za automatyczne wypełnianie daty, ale poszedłbym o krok dalej i powiedziałbym, że data powinna być ustawiona na podstawie zegara serwera bazy danych, a nie klienta. Nie jestem pewien, czy to osiąga ten cel. W sql mogę to zrobić za pomocą funkcji sysdate, ale nie wiem, jak to zrobić w Hibernacji lub jakiejkolwiek implementacji JPA.
MiguelMunoz
Nie chcę magii w DB. Rozumiem, co masz na myśli, ale lubię brać pod uwagę fakt, że baza danych powinna chronić się przed złymi / nowymi / nieświadomymi programistami. Integralność danych jest bardzo ważna w dużej firmie, nie można polegać na innych, aby wstawiać dobre dane. Ograniczenia, wartości domyślne i FK pomogą to osiągnąć.
Icegras,
6

W przypadku korzystania z interfejsu API sesji sesje zwrotne PrePersist i PreUpdate nie będą działać zgodnie z tą odpowiedzią .

Korzystam z metody persist () Hibernate Session w moim kodzie, więc jedynym sposobem, w jaki mogłem to zrobić, był kod poniżej i śledzenie tego postu na blogu (również zamieszczonego w odpowiedzi ).

@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;
    }
}
worek
źródło
Powinny zwrócić sklonowane obiekty, tak jak updated.clone()inne komponenty mogą manipulować stanem wewnętrznym (data)
1ambda
3

Poniższy kod działał dla mnie.

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
źródło
Cześć, dlaczego mielibyśmy potrzebować protected Integer id;tak jak protectedw klasie rodzicielskiej, ponieważ nie mogłem tego użyć w moich testowych przypadkach jako.getId()
shareef
2

Dobrym podejściem jest posiadanie wspólnej klasy bazowej dla wszystkich twoich bytów. W tej klasie bazowej możesz mieć swoją własność id, jeśli jest ona powszechnie nazywana we wszystkich twoich jednostkach (wspólny projekt), twoja własność i właściwości daty ostatniej aktualizacji.

Na dzień utworzenia wystarczy zachować właściwość java.util.Date . Pamiętaj, aby zawsze inicjować go za pomocą nowej daty () .

W przypadku ostatniego pola aktualizacji możesz użyć właściwości znacznika czasu, musisz zmapować ją za pomocą @Version. Dzięki tej adnotacji właściwość zostanie automatycznie zaktualizowana przez Hibernację. Uważaj, że Hibernacja zastosuje również optymistyczne blokowanie (to dobrze).

Bernardn
źródło
2
użycie kolumny sygnatury czasowej do optymistycznego blokowania jest złym pomysłem. Zawsze używaj kolumny z liczbą całkowitą. Powodem jest to, że 2 maszyny JVM mogą działać w różnych momentach i mogą nie mieć milisekundowej dokładności. Jeśli zamiast tego hibernacja użyje znacznika czasu DB, oznaczałoby to dodatkowe wybory z DB. Zamiast tego użyj numeru wersji.
sethu
2

Tylko dla wzmocnienia: java.util.Calendernie dotyczy znaczników czasu . java.util.Datejest chwilowo agnostyczny dla regionalnych rzeczy, takich jak strefy czasowe. Większość baz danych przechowuje rzeczy w ten sposób (nawet jeśli wydaje się, że tak nie jest; zwykle jest to ustawienie strefy czasowej w oprogramowaniu klienta; dane są dobre)

davetron5000
źródło
1

Jako typ danych w JAVA zdecydowanie zalecam użycie java.util.Date. Podczas korzystania z Kalendarza napotkałem dość nieprzyjemne problemy ze strefą czasową. Zobacz ten wątek .

Do ustawiania znaczników czasu zalecałbym użycie metody AOP lub możesz po prostu użyć wyzwalaczy na stole (w rzeczywistości jest to jedyna rzecz, dla której uważam użycie wyzwalaczy za dopuszczalne).

huo73
źródło
1

Możesz rozważyć zapisanie czasu jako DateTime oraz w UTC. Zazwyczaj używam DateTime zamiast Datownika ze względu na fakt, że MySql konwertuje daty na UTC i wraca do czasu lokalnego podczas przechowywania i pobierania danych. Wolałbym zachować taką logikę w jednym miejscu (warstwa biznesowa). Jestem pewien, że istnieją inne sytuacje, w których preferowane jest używanie znacznika czasu.

mmacaulay
źródło
1

Mieliśmy podobną sytuację. Używaliśmy MySQL 5.7.

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

To działało dla nas.

Amdg
źródło
Działa również w przypadku, gdy dane są modyfikowane przez zapytanie SQL bezpośrednio w bazie danych. @PrePersisti @PrePersistnie obejmują takiej sprawy.
pidabrow
1

Jeśli używamy @Transactional w naszych metodach, @CreationTimestamp i @UpdateTimestamp zapiszą wartość w DB, ale zwrócą null po użyciu save (...).

W tej sytuacji załatwienie sprawiło użycie saveAndFlush (...)

chomp
źródło
0

Myślę, że fajniej jest nie robić tego w kodzie Java, możesz po prostu ustawić domyślną wartość kolumny w definicji tabeli MySql. wprowadź opis zdjęcia tutaj

Wayne Wei
źródło