Зимова сплячка не знає про часові пояси в датах (оскільки таких немає), але насправді шар JDBC викликає проблеми. ResultSet.getTimestamp
і PreparedStatement.setTimestamp
обидва в своїх документах кажуть, що вони перетворюють дати в / з поточного часового поясу JVM за замовчуванням під час читання та запису з / у базу даних.
Я вирішив це в Hibernate 3.5 шляхом підкласифікації, org.hibernate.type.TimestampType
яка змушує ці методи 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, якщо ви використовуєте ці типи. Мінус полягає в тому, що вам доведеться вручну вказати, що ці типи повинні використовуватися замість значень за замовчуванням у кожному полі Date у ваших POJO (а також порушує чисту сумісність 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, всі часові позначки зберігатимуться і трактуватимуться як UTC, не змінюючи анотації на POJO. [Я цього ще не перевіряв]