Hibernate kennt keine Zeitzonen in Dates (weil es keine gibt), aber es ist tatsächlich die JDBC-Ebene, die Probleme verursacht. ResultSet.getTimestamp
und PreparedStatement.setTimestamp
beide sagen in ihren Dokumenten, dass sie beim Lesen und Schreiben von / in die Datenbank standardmäßig Daten in / aus der aktuellen JVM-Zeitzone transformieren.
Ich habe in Hibernate 3.5 eine Lösung für dieses Problem gefunden, indem ich eine Unterklasse erstellt habe org.hibernate.type.TimestampType
, die diese JDBC-Methoden zwingt, UTC anstelle der lokalen Zeitzone zu verwenden:
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));
}
}
Dasselbe sollte getan werden, um TimeType und DateType zu reparieren, wenn Sie diese Typen verwenden. Der Nachteil ist, dass Sie manuell angeben müssen, dass diese Typen anstelle der Standardeinstellungen für jedes Datumsfeld in Ihren POJOs verwendet werden sollen (und auch die reine JPA-Kompatibilität beeinträchtigen), es sei denn, jemand kennt eine allgemeinere Überschreibungsmethode.
UPDATE: Hibernate 3.6 hat die Typ-API geändert. In 3.6 habe ich eine Klasse UtcTimestampTypeDescriptor geschrieben, um dies zu implementieren.
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 );
}
};
}
}
Wenn Sie beim Starten der App TimestampTypeDescriptor.INSTANCE auf eine Instanz von UtcTimestampTypeDescriptor setzen, werden alle Zeitstempel gespeichert und als in UTC behandelt, ohne dass die Anmerkungen zu POJOs geändert werden müssen. [Ich habe das noch nicht getestet]