Formatting Timestamps in DBUnit
Another recent problem I’ve had with DBUnit has been the mandated format for TIMESTAMP columns in the test data. You’re forced to define TIMESTAMPs as yyyy-MM-dd HH:mm:ss.fffffffff, which is great if you need that level of granularity. I don’t, and revisiting all of my test data was too cumbersome. So I set about changing the format for my TIMESTAMPs.
The DataType
Each data type is represented by an implementation of the DataType class. DBUnit’s existing TimestampDataType class is what enforces the verbose format. By creating a new implementation of that class, I was able to define multiple TIMESTAMP formats, using the SimpleDateFormat class.
public CustomTimestampDataType() {
super(”TIMESTAMP”, Types.TIMESTAMP, Timestamp.class, false);
}
public Object typeCast(Object value) throws TypeCastException {
if (value == null || value == ITable.NO_VALUE) {
return null;
}
if (value instanceof java.sql.Timestamp) {
return value;
}
if (value instanceof java.util.Date) {
java.util.Date date = (java.util.Date) value;
return new java.sql.Timestamp(date.getTime());
}
if (value instanceof Long) {
Long date = (Long) value;
return new java.sql.Timestamp(date.longValue());
}
if (value instanceof String) {
String stringValue = (String) value;
// Probably a java.sql.Date, try it just in case!
if (stringValue.length() == 10) {
try {
long time = java.sql.Date.valueOf(stringValue).getTime();
return new java.sql.Timestamp(time);
}
catch (IllegalArgumentException e) {
// Was not a java.sql.Date, let Timestamp handle this value
}
}
try {
String formats[] =
{”yyyy-MM-dd HH:mm”,
“yyyy-MM-dd HH:mm a”,
“yyyy-MM-dd HH:mm:ss.fffffffff”};
Timestamp ts = null;
for (int i = 0; i < formats.length; i++) {
SimpleDateFormat sdf = new SimpleDateFormat(formats[i]);
try {
Date date = sdf.parse(stringValue);
ts = new Timestamp(date.getTime());
return ts;
}
catch (ParseException e) {
}
}
}
catch (IllegalArgumentException e) {
throw new TypeCastException(value, this, e);
}
}
throw new TypeCastException(value, this);
}
public boolean isDateTime() {
return true;
}
public Object getSqlValue(int column, ResultSet resultSet)
throws SQLException, TypeCastException {
Timestamp value = resultSet.getTimestamp(column);
if (value == null || resultSet.wasNull()) {
return null;
}
return value;
}
public void setSqlValue(Object value, int column, PreparedStatement statement)
throws SQLException, TypeCastException {
statement.setTimestamp(column, (java.sql.Timestamp) typeCast(value));
}
}
Unfortunately, DBUnit doesn’t allow me to simply register this new class. Instead, I have to go about creating a DataTypeFactory.
The DataTypeFactory
The DataTypeFactory is pretty simple since I just needed to override a small portion of the base class’s functionality.
public DataType createDataType(int sqlType, String sqlTypeName)
throws DataTypeException {
if (sqlType == Types.TIMESTAMP) {
return new CustomTimestampDataType();
}
else {
return super.createDataType(sqlType, sqlTypeName);
}
}
}
Next, I had to tell DBUnit about the new factory. (You can register new factories but not individual data types.)
Registering the DataTypeFactory
This was the easiest part to figure out:
conn.getConfig().setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY,
new CustomDataTypeFactory());
Hopefully this saves someone a good deal of aggravation.
Uncategorized