Hibernate は Dates のタイム ゾーンを認識しませんが (何もないため)、実際に問題を引き起こしているのは JDBC レイヤーです。どちらも、データベースとの間で読み書きするときに、デフォルトで現在のJVMタイムゾーンとの間で日付を変換するとドキュメントで述べていますResultSet.getTimestamp
。PreparedStatement.setTimestamp
org.hibernate.type.TimestampType
Hibernate 3.5では、これらの JDBC メソッドがローカル タイム ゾーンの代わりに UTC を使用するように強制するサブクラス化によって、これに対する解決策を思いつきました。
public class UtcTimestampType extends TimestampType {
private static final long serialVersionUID = 8088663383676984635L;
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
@Override
public Object get(ResultSet rs, String name) throws SQLException {
return rs.getTimestamp(name, Calendar.getInstance(UTC));
}
@Override
public void set(PreparedStatement st, Object value, int index) throws SQLException {
Timestamp ts;
if(value instanceof Timestamp) {
ts = (Timestamp) value;
} else {
ts = new Timestamp(((java.util.Date) value).getTime());
}
st.setTimestamp(index, ts, Calendar.getInstance(UTC));
}
}
TimeType と DateType を使用する場合は、同じことを修正して修正する必要があります。欠点は、誰かがより一般的なオーバーライド方法を知らない限り、POJO のすべての Date フィールドでデフォルトの代わりにこれらの型を使用することを手動で指定する必要があることです (また、純粋な JPA 互換性を壊します)。
更新: Hibernate 3.6 はタイプ API を変更しました。3.6 では、これを実装するクラス UtcTimestampTypeDescriptor を作成しました。
public class UtcTimestampTypeDescriptor extends TimestampTypeDescriptor {
public static final UtcTimestampTypeDescriptor INSTANCE = new UtcTimestampTypeDescriptor();
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
st.setTimestamp( index, javaTypeDescriptor.unwrap( value, Timestamp.class, options ), Calendar.getInstance(UTC) );
}
};
}
public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>( javaTypeDescriptor, this ) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap( rs.getTimestamp( name, Calendar.getInstance(UTC) ), options );
}
};
}
}
アプリの起動時に TimestampTypeDescriptor.INSTANCE を UtcTimestampTypeDescriptor のインスタンスに設定すると、すべてのタイムスタンプが保存され、POJO の注釈を変更しなくても UTC として扱われます。【まだ試していません】