| Java 類型 | Hibernate 類型 | MySQL 類型 |
|---|---|---|
| java.util.Date / java.sql.Date | date | DATE |
| java.util.Date / java.sql.Time | time | TIME |
| java.util.Date / java.sql.Timestamp | timestamp | TIMESTAMP |
public class User {
private Date registerDate; // java.util.Date
// getter/setter 略
}
<property name="registerDate" type="timestamp" column="registerdate"/>
User testUser = new User();
testUser.setRegisterDate(new Date());
session.save(testUser);
session.flush();
session.clear();
User savedUser = session.load(User.class, id);
// testUser.getRegisterDate().equals(savedUser.getRegisterDate()) → false
// 原因:testUser.registerDate 是 java.util.Date
// savedUser.registerDate 是 java.sql.Timestamp
java.sql.Timestamp 繼承 java.util.Date,但覆寫 equals(Object):
Timestamp.equals(Object) 若參數非 Timestamp 實例,永遠回傳 false(因納秒精度未知)。
因此 Date.equals(Timestamp) 可能為 true,但 Timestamp.equals(Date) 必為 false —— 違反對稱性。
public void setRegisterDate(Date registerDate) {
this.registerDate = new Date(registerDate.getTime()); // 截斷納秒
}
存入資料庫前統一轉為 java.util.Date,載入時 Hibernate 仍會產生 Timestamp,但比較時雙方皆為 Date 型別(若同時正規化),或改用 getTime() 比較毫秒數。
java.util.Calendar:Hibernate 支援 calendar / calendar_date 類型。java.time.LocalDateTime / Instant:Hibernate 5.2+ 原生支援,精度明確、不可變、無 equals 陷阱。public class DateAsTimestampUserType implements UserType {
// 實作 nullSafeGet/Set,統一以 Timestamp 存取
}
映射時指定 type="com.yourpkg.DateAsTimestampUserType"。
java.time.* 類型。java.util.Date,在 Setter 正規化毫秒數。equals() 比較日期,改比較 getTime() 或使用 compareTo()。DATE(僅日期)、TIME(僅時間)、TIMESTAMP(日期時間)、DATETIME(MySQL 專用,無時區轉換)。Hibernate 將 java.util.Date 映射為 timestamp 時,載入物件實為 java.sql.Timestamp,導致 equals 語義不對稱。理解此機制並採取正規化或升級至現代時間 API,可徹底規避此類 Bug。