特定のHibernateエンティティについては、作成時刻と最後に更新された時刻を保存する必要があります。これをどのように設計しますか?
データベースでどのデータ型を使用しますか(MySQL、おそらくJVMとは異なるタイムゾーンにあると仮定)?データ型はタイムゾーンに対応しますか?
Java(Date
、Calendar
、long
、...)でどのデータ型を使用しますか?
データベース、ORMフレームワーク(Hibernate)、またはアプリケーションプログラマなど、タイムスタンプの設定を誰に任せますか?
マッピングにどのアノテーションを使用しますか(例:@Temporal
)?
実用的なソリューションを探しているだけでなく、安全で適切に設計されたソリューションを探しています。
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
注釈を使用して、イベントコードを外部クラスに配置できます。
単に@CreationTimestamp
と@UpdateTimestamp
を使用できます:
@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "create_date")
private Date createDate;
@UpdateTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "modify_date")
private Date modifyDate;
この投稿のリソースをさまざまなソースから左右に取得した情報とともに、このエレガントなソリューションを使用して、次の抽象クラスを作成しました
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 {
...
}
インターセプターを使用して値を設定することもできます
エンティティが実装する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;
}
}
そして、それをセッションファクトリに登録します
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;
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;
}
}
現在、@ CreatedDateおよび@LastModifiedDateアノテーションもあります。
(春のフレームワーク)
ただ補強するために:Java.util.Calender
はタイムスタンプ用ではありません。 Java.util.Date
はしばらくの間、タイムゾーンなどの地域的なものにとらわれません。ほとんどのデータベースはこのように物事を保存します(たとえそうでないように見えても;これは通常クライアントソフトウェアのタイムゾーン設定です;データは良好です)
良いアプローチは、すべてのエンティティに共通の基本クラスを用意することです。この基本クラスでは、すべてのエンティティ(共通デザイン)、作成および最終更新日のプロパティで一般的に名前が付けられている場合、idプロパティを設定できます。
作成日については、単にJava.util.Dateプロパティを保持します。必ずnew Date()で初期化してください。
最終更新フィールドには、Timestampプロパティを使用できます。@ Versionでマップする必要があります。このアノテーションを使用すると、Hibernateによってプロパティが自動的に更新されます。 Hibernateは楽観的ロックも適用することに注意してください(これは良いことです)。
時刻をDateTimeとして、UTCで保存することを検討してください。 MySqlがデータを保存および取得するときに日付をUTCに変換し、現地時間に戻すという事実のため、私は通常Timestampの代わりにDateTimeを使用します。むしろ、そのようなロジックを1か所(ビジネスレイヤー)に保持したいと思います。ただし、Timestampの使用が望ましい他の状況があると確信しています。
次のコードは私のために働いた。
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;
}
Javaのデータ型として、Java.util.Dateを使用することを強くお勧めします。カレンダーを使用しているときに、かなり厄介なタイムゾーンの問題に遭遇しました。これを参照してください Thread 。
タイムスタンプを設定するには、AOPアプローチを使用するか、テーブルで単純にトリガーを使用することをお勧めします(実際、これはトリガーの使用が受け入れられる唯一の方法です)。
同様の状況がありました。 Mysql 5.7を使用していました。
CREATE TABLE my_table (
...
updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
これはうまくいきました。