Using Different Hibernate User Types in Different Situations
-
06-07-2019 - |
Question
I am using Hibernate + JPA as my ORM solution.
I am using HSQL for unit testing and PostgreSQL as the real database.
I want to be able to use Postgres's native UUID type with Hibernate, and use the UUID in its String representation with HSQL for unit testing (since HSQL does not have a UUID type).
I am using a persistence XML with different configurations for Postgres and HSQL Unit Testing.
Here is how I have Hibernate "see" my custom UserType:
@Id
@Column(name="UUID", length=36)
@org.hibernate.annotations.Type(type="com.xxx.UUIDStringType")
public UUID getUUID() {
return uuid;
}
public void setUUID(UUID uuid) {
this.uuid = uuid;
}
and that works great. But what I need is the ability to swap out the "com.xxx.UUIDStringType" part of the annotation in XML or from a properties file that can be changed without re-compiling.
Any ideas?
Solution
This question is really old and has been answered for a long time, but I recently found myself in this same situation and found a good solution. For starters, I discovered that Hibernate has three different built-in UUID type implementations:
binary-uuid
: stores the UUID as binaryuuid-char
: stores the UUID as a character sequencepg-uuid
: uses the native Postgres UUID type
These types are registered by default and can be specified for a given field with a @Type
annotation, e.g.
@Column
@Type(type = "pg-uuid")
private UUID myUuidField;
There's also a mechanism for overriding default types in the Dialect
. So if the final deployment is to talk to a Postgres database, but the unit tests use HSQL, you can override the pg-uuid
type to read/write character data by writing a custom dialect like so:
public class CustomHSQLDialect extends HSQLDialect {
public CustomHSQLDialect() {
super();
// overrides the default implementation of "pg-uuid" to replace it
// with varchar-based storage.
addTypeOverride(new UUIDCharType() {
@Override
public String getName() {
return "pg-uuid";
}
});
}
}
Now just plug in the custom dialect, and the the pg-uuid
type is available in both environments.
OTHER TIPS
Hy, for those who are seeking for a solution in Hibernate 4 (because the Dialect#addTypeOverride method is no more available), I've found one, underlying on this Steve Ebersole's comment
You have to build a custom user type like this one :
public class UUIDStringCustomType extends AbstractSingleColumnStandardBasicType {
public UUIDStringCustomType() {
super(VarcharTypeDescriptor.INSTANCE, UUIDTypeDescriptor.INSTANCE);
}
@Override
public String getName() {
return "pg-uuid";
}
}
And to bind it to the HSQLDB dialect, you must build a custom dialect that override the Dialect#contributeTypes method like this :
public class CustomHsqlDialect extends HSQLDialect {
@Override
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
super.contributeTypes(typeContributions,serviceRegistry);
typeContributions.contributeType(new UUIDStringCustomType());
}
}
Then you can use the @Type(type="pg-uuid") with the two databases.
Hope it will help someone...
To avoid problems between the UUID types without specifying the @Type
annotation (which basically means you have to adjust all annotations when you want to change from postgres to mysql or the other way around...) I'm using a package-info.java
with the hibernates @TypeDef
annotation on that package.
Here's an example setup of your application:
Assuming module/src/main/java/app.package.domain
contains your entities. And you'r tests are stored in module/src/test/java/app.package
.
Simply create two package-info.java
in your domain
packages.
Make sure the package-info files are always in the same package (for testing and production). See the following example below:
src/main/java
app
package
domain
package-info.java
src/test/java
app
package
domain
package-info.java
The content of you're production package-info.java
should look like this (Postgres):
@TypeDef(
name = "pg-uuid",
defaultForType = UUID.class,
typeClass = PostgresUUIDType.class
)
package app.package.domain;
import org.hibernate.annotations.TypeDef;
import org.hibernate.type.PostgresUUIDType;
import java.util.UUID;
And this is how you'r testing "configuration" should look like (H2):
@TypeDef(
name = "uuid-char",
defaultForType = UUID.class,
typeClass = UUIDCharType.class
)
package app.package.domain;
import org.hibernate.annotations.TypeDef;
import org.hibernate.type.UUIDCharType;
import java.util.UUID;
Hope it helps
Perhaps you can build some smarts in your user type to do the right thing depending on the database capabilities. Hibernate itself takes a similar approach with its "native" ID generator, which behaves differently depending on the type of database you are using. An approach like this eliminates the need to switch the mapping at runtime.
For example, you could create one strategy class for each database. Then in your user type class, detect what database you're connected to when you're called for the first time, instantiate the proper strategy for that database, and then delegate all calls to the strategy object.