web-dev-qa-db-ja.com

HibernateとMySQLを使用した作成タイムスタンプと最終更新タイムスタンプ

特定のHibernateエンティティについては、作成時刻と最後に更新された時刻を保存する必要があります。これをどのように設計しますか?

  • データベースでどのデータ型を使用しますか(MySQL、おそらくJVMとは異なるタイムゾーンにあると仮定)?データ型はタイムゾーンに対応しますか?

  • Java(DateCalendarlong、...)でどのデータ型を使用しますか?

  • データベース、ORMフレームワーク(Hibernate)、またはアプリケーションプログラマなど、タイムスタンプの設定を誰に任せますか?

  • マッピングにどのアノテーションを使用しますか(例:@Temporal)?

実用的なソリューションを探しているだけでなく、安全で適切に設計されたソリューションを探しています。

220
ngn

JPAアノテーションを使用している場合、@PrePersistおよび@PreUpdateイベントフックを使用してこれを実行できます。

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

または、クラスで@EntityListener注釈を使用して、イベントコードを外部クラスに配置できます。

250

単に@CreationTimestamp@UpdateTimestampを使用できます:

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

@UpdateTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "modify_date")
private Date modifyDate;
118
idmitriev

この投稿のリソースをさまざまなソースから左右に取得した情報とともに、このエレガントなソリューションを使用して、次の抽象クラスを作成しました

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

そして、すべてのエンティティにそれを拡張させます、例えば:

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

インターセプターを使用して値を設定することもできます

エンティティが実装するTimeStampedというインターフェイスを作成します

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

インターセプターを定義する

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

そして、それをセッションファクトリに登録します

17
Kieren Dixon

Olivierのソリューションを使用すると、更新ステートメント中に次のことが発生する可能性があります。

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:列 'created'はnullにできません

これを解決するには、「created」属性の@Columnアノテーションにupdatable = falseを追加します。

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

Session APIを使用している場合、PrePersistコールバックとPreUpdateコールバックは、この answer に従って機能しません。

私はコードでHibernate Sessionのpersist()メソッドを使用しているので、この作業を行うことができる唯一の方法は以下のコードとこれに従うことでした ブログ投稿answer =)。

@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

現在、@ CreatedDateおよび@LastModifiedDateアノテーションもあります。

=> https://programmingmitra.blogspot.fr/2017/02/automatic-spring-data-jpa-auditing-saving-CreatedBy-createddate-lastmodifiedby-lastmodifieddate-automatically.html

(春のフレームワーク)

3
Oreste Viron

ただ補強するために:Java.util.Calenderはタイムスタンプ用ではありませんJava.util.Dateはしばらくの間、タイムゾーンなどの地域的なものにとらわれません。ほとんどのデータベースはこのように物事を保存します(たとえそうでないように見えても;これは通常クライアントソフトウェアのタイムゾーン設定です;データは良好です)

2
davetron5000

良いアプローチは、すべてのエンティティに共通の基本クラスを用意することです。この基本クラスでは、すべてのエンティティ(共通デザイン)、作成および最終更新日のプロパティで一般的に名前が付けられている場合、idプロパティを設定できます。

作成日については、単にJava.util.Dateプロパティを保持します。必ずnew Date()で初期化してください。

最終更新フィールドには、Timestampプロパティを使用できます。@ Versionでマップする必要があります。このアノテーションを使用すると、Hibernateによってプロパティが自動的に更新されます。 Hibernateは楽観的ロックも適用することに注意してください(これは良いことです)。

2
bernardn

時刻をDateTimeとして、UTCで保存することを検討してください。 MySqlがデータを保存および取得するときに日付をUTCに変換し、現地時間に戻すという事実のため、私は通常Timestampの代わりにDateTimeを使用します。むしろ、そのようなロジックを1か所(ビジネスレイヤー)に保持したいと思います。ただし、Timestampの使用が望ましい他の状況があると確信しています。

1
mmacaulay

次のコードは私のために働いた。

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

Javaのデータ型として、Java.util.Dateを使用することを強くお勧めします。カレンダーを使用しているときに、かなり厄介なタイムゾーンの問題に遭遇しました。これを参照してください Thread

タイムスタンプを設定するには、AOPアプローチを使用するか、テーブルで単純にトリガーを使用することをお勧めします(実際、これはトリガーの使用が受け入れられる唯一の方法です)。

1
huo73

同様の状況がありました。 Mysql 5.7を使用していました。

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

これはうまくいきました。

0
amdg