You will have to create a custom type to handle this situation. There are two approaches you can take.
1) Use the oracle.sql.TIMESTAMPTZ type for reading to and from the prepared statement. This has one major drawback, the TIMESTAMPTZ construction initiates a socket connection to the database to retrieve database settings.
2) Use string values to read and write to the database. This has one minor drawback, the format of the string can be modified on the database side via settings. As long as you know the settings this is probably the prefered way. We developed this approach and here is our code....
Code:
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date;
import java.util.Calendar;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;
import oracle.jdbc.OracleTypes;
public class TimestampWithTimeZoneType implements UserType, Serializable
{
private static final int[] SQL_TYPES = {OracleTypes.TIMESTAMPTZ};
public int[] sqlTypes()
{
return SQL_TYPES;
}
public Class returnedClass()
{
return Calendar.class;
}
public boolean equals(Object x, Object y) throws HibernateException
{
if ( x == y ) return true;
if ( x == null || y == null) return false;
return x.equals(y);
}
public int hashCode(Object x) throws HibernateException
{
return x.hashCode();
}
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException
{
String tzString = rs.getString(names[0]);
// transform TimestampWithTZ to a Timestamp object
Calendar cal = StringUtil.getCalendar(tzString);
return cal;
}
public void nullSafeSet(PreparedStatement stmt, Object value, int index) throws HibernateException, SQLException
{
String tsString = StringUtil.getTimestampTZString((Calendar) value);
stmt.setString(index,tsString);
}
public Object deepCopy(Object value) throws HibernateException
{
return value;
}
public boolean isMutable()
{
return true;
}
public Serializable disassemble(Object value) throws HibernateException
{
return (Serializable) value;
}
public Object assemble(Serializable cached, Object owner) throws HibernateException
{
return cached;
}
public Object replace(Object original, Object target, Object owner) throws HibernateException
{
return original;
}
}
Also, here is the associated StringUtil functions which expect the default setting for the timestamp with timezone string format.
Code:
public static String getTimestampTZString(Calendar cal)
{
StringBuilder sb = new StringBuilder();
sb.append(cal.get(Calendar.DAY_OF_MONTH));
sb.append(DASH);
sb.append(months[cal.get(Calendar.MONTH)]);
sb.append(DASH);
String year = Integer.toString(cal.get(Calendar.YEAR)).substring(2);
sb.append(year);
sb.append(" ");
sb.append(cal.get(Calendar.HOUR));
sb.append(COLON);
sb.append(cal.get(Calendar.MINUTE));
sb.append(COLON);
sb.append(cal.get(Calendar.SECOND));
sb.append(".");
int millis = cal.get(Calendar.MILLISECOND);
if (millis < 100) sb.append('0');
if (millis < 10) sb.append('0');
sb.append(millis);
sb.append(ampm[cal.get(Calendar.AM_PM)]);
sb.append(" ");
sb.append(cal.getTimeZone().getID());
return sb.toString();
}
public static Calendar getCalendar(String in)
{
Calendar cal = Calendar.getInstance();
String[] tokens = in.split("\\s");
cal.setTimeZone(TimeZone.getTimeZone(tokens[2]));
StringTokenizer st = new StringTokenizer(tokens[0],"-");
cal.set(Calendar.YEAR,Integer.valueOf(st.nextToken()));
cal.set(Calendar.MONTH,Integer.valueOf(st.nextToken()) - 1);
cal.set(Calendar.DAY_OF_MONTH,Integer.valueOf(st.nextToken()));
st = new StringTokenizer(tokens[1],".");
cal.set(Calendar.HOUR_OF_DAY,Integer.valueOf(st.nextToken()));
cal.set(Calendar.MINUTE,Integer.valueOf(st.nextToken()));
cal.set(Calendar.SECOND,Integer.valueOf(st.nextToken()));
//get only millis, Calendar doesn't deal with nano seconds
cal.set(Calendar.MILLISECOND,Integer.valueOf(st.nextToken().substring(0,3)));
return cal;
}