Hazırda bekletme, Tarihlerdeki saat dilimi konularından habersizdir (çünkü hiç yoktur), ancak aslında sorunlara neden olan JDBC katmanıdır. ResultSet.getTimestamp
ve PreparedStatement.setTimestamp
her ikisi de belgelerinde, veritabanından / veritabanına okurken ve veritabanına yazarken varsayılan olarak tarihleri geçerli JVM saat dilimine / diliminden dönüştürdüklerini söylüyor.
Hibernate 3.5'te org.hibernate.type.TimestampType
, bu JDBC yöntemlerini yerel saat dilimi yerine UTC'yi kullanmaya zorlayan alt sınıflandırma yoluyla buna bir çözüm buldum :
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));
}
}
Bu türleri kullanıyorsanız, TimeType ve DateType'ı düzeltmek için aynı şey yapılmalıdır. Bunun dezavantajı, birisi daha genel bir geçersiz kılma yöntemini bilmediği sürece, POJO'larınızdaki her Tarih alanındaki varsayılanlar yerine bu türlerin kullanılacağını (ve ayrıca saf JPA uyumluluğunu bozduğunu) manuel olarak belirtmeniz gerekmesidir.
GÜNCELLEME: Hibernate 3.6, API türlerini değiştirdi. 3.6'da, bunu uygulamak için bir UtcTimestampTypeDescriptor sınıfı yazdım.
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 );
}
};
}
}
Artık uygulama başladığında, TimestampTypeDescriptor.INSTANCE öğesini UtcTimestampTypeDescriptor örneğine ayarlarsanız, tüm zaman damgaları POJO'lardaki ek açıklamaları değiştirmek zorunda kalmadan UTC olarak saklanacak ve işlenecektir. [Bunu henüz test etmedim]