Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Different Hibernate User Types in Different Situations

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?

like image 691
mainstringargs Avatar asked Jun 17 '09 13:06

mainstringargs


3 Answers

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...

like image 86
Philippe Avatar answered Oct 29 '22 10:10

Philippe


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:

  1. binary-uuid : stores the UUID as binary
  2. uuid-char : stores the UUID as a character sequence
  3. pg-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.

like image 45
stevevls Avatar answered Oct 29 '22 09:10

stevevls


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

like image 44
Tim Avatar answered Oct 29 '22 11:10

Tim