休眠@CreationTimestamp 和@UpdateTimestamp
一、概述
在本文中,我們將學習如何使用 Hibernate 註釋@CreationTimestamp
和@UpdateTimestamp
來跟踪我們創建和更新實體的時間。
2.型號
為了說明這些註解是如何運作的,我們將從一個簡單的Book
實體開始:
@Entity
public class Book {
@Id
@GeneratedValue
private Long id;
private String title;
public Book() {
}
// standard setters and getters
}
我們將使用 H2 數據庫和基於Book
自動創建的模式 DDL。 Hibernate 要求映射到帶有@CreationTimestamp
或@UpdateTimestamp
註釋的字段的數據庫列是基於時間戳的類型,例如Timestamp
或DateTime
。
3. 使用@CreationTimestamp
跟踪創建日期和時間
我們經常需要持久化實體的創建日期。 @CreationtTimestamp
是一個方便的註釋,可在首次保存實體時將字段值設置為當前時間戳。讓我們使用此註解在Book
中添加一個字段createdOn
。由於該字段需要存儲過去的確切時刻,我們將其聲明為Instant
,它代表 UTC 中的特定時刻。該字段在我們生成的模式中將具有時間戳類型:
@Entity
public class Book {
//other fields
@CreationTimestamp
private Instant createdOn;
// standard setters and getters
現在,讓我們驗證 Hibernate 在保存一本新書後是否設置了createdOn
:
@Test
void whenCreatingEntity_ThenCreatedOnIsSet() {
session = sessionFactory.openSession();
session.beginTransaction();
Book book = new Book();
session.save(book);
session.getTransaction()
.commit();
session.close();
assertNotNull(book.getCreatedOn());
}
4. 使用@UpdateTimestamp
跟踪上次更新時間
同樣,我們可能會要求記錄上次實體更新的日期/時間。 The @UpdateTimestamp
是 Hibernate 提供的另一個註解。它會在每次實體更新時自動將字段值設置為當前時間戳。讓我們添加另一個名為lastUpdatedOn
的Instant
字段,這次用@UpdateTimestamp
註釋:
@Entity
public class Book {
//other fields
@UpdateTimestamp
private Instant lastUpdatedOn;
// standard setters and getters
讓我們檢查 Hibernate 是否在創建實體時填充lastUpdatedOn
:
@Test
void whenCreatingEntity_ThenCreatedOnAndLastUpdatedOnAreBothSet() {
session = sessionFactory.openSession();
session.beginTransaction();
Book book = new Book();
session.save(book);
session.getTransaction()
.commit();
session.close();
assertNotNull(book.getCreatedOn());
assertNotNull(book.getLastUpdatedOn());
}
我們確認 Hibernate 按預期生成了lastUpdatedOn
。我們還要檢查更新book
時lastUpdatedOn
是否發生變化,而createdOn
保持不變:
@Test
void whenUpdatingEntity_ThenLastUpdatedOnIsUpdatedAndCreatedOnStaysTheSame() {
session = sessionFactory.openSession();
session.beginTransaction();
Book book = new Book();
session.save(book);
session.flush();
Instant createdOnAfterCreation = book.getCreatedOn();
Instant lastUpdatedOnAfterCreation = book.getLastUpdatedOn();
String newName = "newName";
book.setTitle(newName);
session.getTransaction()
.commit();
session.close();
Instant createdOnAfterUpdate = book.getCreatedOn();
Instant lastUpdatedOnAfterUpdate = book.getLastUpdatedOn();
assertEquals(newName, book.getTitle());
assertNotNull(createdOnAfterUpdate);
assertNotNull(lastUpdatedOnAfterUpdate);
assertEquals(createdOnAfterCreation, createdOnAfterUpdate);
assertNotEquals(lastUpdatedOnAfterCreation, lastUpdatedOnAfterUpdate);
}
在我們為book
設置新標題後,只有lastUpdatedOn
發生了變化。
5. 當前日期的來源
為了使註釋有用,我們必須正確設置時鐘以確保時間戳準確。默認情況下,這兩個註釋在設置屬性值時都使用 Java 虛擬機的當前日期。
從 Hibernate 6.0.0 開始,我們可以選擇指定數據庫作為日期的來源:
@CreationTimestamp(source = SourceType.DB)
private Instant createdOn;
@UpdateTimestamp(source = SourceType.DB)
private Instant lastUpdatedOn;
在這種情況下,底層數據庫指定如何確定當前日期。例如,這可能是數據庫函數current_timestamp()
。
6. 注意事項
正如我們之前演示的,我們在實體創建時同時設置了createdOn
和lastUpdatedOn
。我們可能會驚訝地發現這兩個值不相等:
@Test
void whenCreatingEntity_ThenCreatedOnAndLastUpdatedOnAreNotEqual() {
session = sessionFactory.openSession();
session.beginTransaction();
Book book = new Book();
session.save(book);
session.getTransaction()
.commit();
session.close();
assertNotEquals(book.getCreatedOn(), book.getLastUpdatedOn());
}
這是因為 Hibernate 不會同時將兩個字段設置為相同的值。相反,它使用Instant
的新實例分別設置兩個字段。因此,這兩個時間戳相差毫秒。如果出於某種原因,我們要求這兩個在創建後的日期相同,我們應該使用另一種方法來設置時間戳。我們可以通過使用 JPA @PrePersist
和@PreUpdate
回調來實現這一點,如使用 JPA、Hibernate 和 Spring Data JPA 進行審計中所述。
此外,我們必須記住,這些註釋只會在我們的 Java 應用程序創建和修改數據時生成新的時間戳。它們對外面的桌子沒有任何影響。如果其他應用程序或 SQL 腳本修改了book
表,我們必須使用不同的方法更新我們的時間戳。
Instant
是 Java 8 中添加的用於日期和時間的新 API 的一部分,從 5.2.3 版開始,Hibernate 開箱即用地支持這些 API。建議使用這些新類來表示日期。它們在java.time
包中可用。但是,如果我們需要支持舊版本的 Hibernate 或 Java,我們可能不得不求助於使用不同類型的時間戳。我們可以通過閱讀 Hibernate – 映射日期和時間來了解有關使用 Hibernate 將時間列映射到 Java 類字段的更多信息。
七、結論
在本教程中,我們展示瞭如何使用@CreationTimestamp
和@UpdateTimestamp
自動生成時間戳。使用這些註釋是監視實體修改日期的最簡單方法之一。但是,在使用它們時,我們必須記住 Hibernate 在每個字段的基礎上生成新的時間戳。這會導致多個時間戳不同,即使它們是由相同的INSERT
或UPDATE
語句設置的。
此外,我們需要注意這些註釋不會為表創建任何全局機制。如果我們的數據庫表將被不同的應用程序修改,我們需要確保每個應用程序都正確設置更新和創建時間戳。
與往常一樣,本文的完整代碼示例可在 GitHub 上獲得。