Hibernate 4.3.x 內建時間戳註解僅支援 Date、Calendar、Timestamp 等舊類型。Java 8 java.time 套件(LocalDateTime、LocalDate、LocalTime)成為新標準,需自訂 AnnotationValueGeneration 實作註冊至 Hibernate,實現實體欄位自動填充創建/更新時間。
public abstract class AbstractTimeGeneration<A extends Annotation>
implements AnnotationValueGeneration<A> {
protected ValueGenerator<?> generator;
@Override
public void initialize(A annotation, Class<?> propertyType) {
if (LocalDateTime.class.isAssignableFrom(propertyType)) {
generator = (session, owner) -> LocalDateTime.now();
} else if (LocalDate.class.isAssignableFrom(propertyType)) {
generator = (session, owner) -> LocalDate.now();
} else if (LocalTime.class.isAssignableFrom(propertyType)) {
generator = (session, owner) -> LocalTime.now();
} else if (java.sql.Date.class.isAssignableFrom(propertyType)) {
generator = (session, owner) -> new java.sql.Date(System.currentTimeMillis());
} else if (Time.class.isAssignableFrom(propertyType)) {
generator = (session, owner) -> new Time(System.currentTimeMillis());
} else if (Timestamp.class.isAssignableFrom(propertyType)) {
generator = (session, owner) -> new Timestamp(System.currentTimeMillis());
} else if (Date.class.isAssignableFrom(propertyType)) {
generator = (session, owner) -> new Date();
} else if (Calendar.class.isAssignableFrom(propertyType)) {
generator = (session, owner) -> {
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
return cal;
};
} else {
throw new HibernateException("Unsupported type for " + annotation.getClass().getSimpleName());
}
}
@Override public ValueGenerator<?> getValueGenerator() { return generator; }
@Override public boolean referenceColumnInSql() { return false; }
@Override public String getDatabaseGeneratedReferencedColumnValue() { return null; }
}
// 1. 定義註解(保留策略 RUNTIME,目標 FIELD/METHOD)
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CreationTimestamp {}
// 2. 實作生成器(繼承抽象類)
public class CreationTimestampGenerator
extends AbstractTimeGeneration<CreationTimestamp> {}
// 3. 註冊至 Hibernate(hibernate.cfg.xml 或 persistence.xml)
//
// com.yourpkg.CreationTimestampGenerator
//
// 或程式碼註冊:
// MetadataSources src = new MetadataSources(...);
// src.getMetadataBuilder().applyValueGenerators(...);
@Entity
public class Article {
@Id @GeneratedValue Long id;
String title;
@CreationTimestamp
@Column(updatable = false) // 僅創建時設定
LocalDateTime createdAt;
@UpdateTimestamp // 需同理實作 UpdateTimestampGenerator
LocalDateTime updatedAt;
}
LocalDateTime.now() 使用 JVM 預設時區,分布式系統建議統一 UTC,或改用 OffsetDateTime、Instant。java.time 類型(需 hibernate-java8 模組),可直接用 @CreationTimestamp 無需自訂生成器。@PrePersist/@PreUpdate 回調方法同樣可實現,無需擴充 Hibernate SPI,移植性更好。DEFAULT CURRENT_TIMESTAMP、ON UPDATE CURRENT_TIMESTAMP(MySQL)或 Trigger(PostgreSQL/Oracle)更可靠,不依賴應用層時鐘。