JDBC doesn’t include UUIDs as one of its generic SQL types and so the different relational databases supported by Hibernate have different mechanisms to save and retrieve UUIDs. PostgreSQL’s JDBC driver, for example, requires that you use Types.OTHER
as a surrogate for the missing UUID constant:
preparedStatement.setObject( index, uuid, Types.OTHER );
Whereas H2 requires the simpler:
preparedStatement.setObject( index, uuid );
Developers may also choose not to use a database’s native mechanism, preferring instead to stipulate storage using a 36 character string or as an ordinary binary number. The way of telling Hibernate which mechanism you wish to use is done using an @Type
annotation on the column:
@Type("pg-uuid")
private UUID id;
Or an @TypeDef
annotation on the package object:
@TypeDef(
name = "pg-uuid",
defaultForType = UUID.class,
typeClass = PostgresUUIDType.class
)
package com.example.domain;
This works fine with one database, but causes problems when you wish to test your application with a different database. The solution is to create another Hibernate type definition which can delegate to a database specific type depending on some environmental setting.
The following implementation loads the database dialect in use from a properties file on the classpath. Strictly speaking, this information is already available in the persistence.xml
file, but extracting that would entail much more effort. This is done just once when the class is loaded, so shouldn’t result in a performance impact.
/**
* @author David Beaumont
* @see org.hibernate.type.PostgresUUIDType
*/
public class UUIDCustomType extends AbstractSingleColumnStandardBasicType {
private static final long serialVersionUID = 902830399800029445L;
private static final SqlTypeDescriptor SQL_DESCRIPTOR;
private static final JavaTypeDescriptor TYPE_DESCRIPTOR;
static {
Properties properties = new Properties();
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
properties.load(loader.getResourceAsStream("database.properties"));
} catch (IOException e) {
throw new RuntimeException("Could not load properties!", e);
}
String dialect = properties.getProperty("dialect");
if(dialect.equals("org.hibernate.dialect.PostgreSQLDialect")) {
SQL_DESCRIPTOR = PostgresUUIDType.PostgresUUIDSqlTypeDescriptor.INSTANCE;
} else if(dialect.equals("org.hibernate.dialect.H2Dialect")) {
SQL_DESCRIPTOR = VarcharTypeDescriptor.INSTANCE;
} else {
throw new UnsupportedOperationException("Unsupported database!");
}
TYPE_DESCRIPTOR = UUIDTypeDescriptor.INSTANCE;
}
public UUIDCustomType() {
super(SQL_DESCRIPTOR, TYPE_DESCRIPTOR);
}
@Override
public String getName() {
return "uuid-custom";
}
}
Now you just have to register this new type as detailed above using @Type("uuid-custom")
.
One response to “Switching Hibernate’s UUID Type Mapping per Database”
This was exactly the answer we were trying to find for two days. It saved my client lots of time! Thanks!