在不同情况下使用不同的Hibernate用户类型

7

我正在使用Hibernate + JPA作为我的ORM解决方案。

我在单元测试中使用HSQL,在真正的数据库中使用PostgreSQL。

我想要能够使用Postgres的原生UUID类型与Hibernate一起使用,并在单元测试中使用字符串表示的UUID与HSQL一起使用(因为HSQL没有UUID类型)。

我正在使用一个持久化XML,其中包含不同的配置用于Postgres和HSQL单元测试。

以下是我如何让Hibernate“看到”我的自定义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;
}

这样做很好。但是我需要的是能够在不重新编译的情况下从XML或属性文件中交换注释中的“com.xxx.UUIDStringType”部分的能力。

有什么想法吗?

5个回答

11

嗨,对于那些正在寻找Hibernate 4解决方案的人(因为Dialect#addTypeOverride方法不再可用),我找到了一个解决方法,基于Steve Ebersole的这个评论

你需要构建一个自定义用户类型,就像这样:

public class UUIDStringCustomType extends AbstractSingleColumnStandardBasicType {

    public UUIDStringCustomType() {
        super(VarcharTypeDescriptor.INSTANCE, UUIDTypeDescriptor.INSTANCE);
    }

    @Override
    public String getName() {
        return "pg-uuid";
    }

}

要将其绑定到HSQLDB方言,您必须构建一个自定义方言,覆盖Dialect#contributeTypes方法,例如:

并将其绑定到HSQLDB方言,您必须构建一个自定义方言,覆盖Dialect#contributeTypes方法,如下所示:

public class CustomHsqlDialect extends HSQLDialect {


    @Override
    public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
        super.contributeTypes(typeContributions,serviceRegistry);
        typeContributions.contributeType(new UUIDStringCustomType());
    }

}

您可以在这两个数据库中使用@Type(type="pg-uuid")。

希望它能帮助到某些人...


你如何将HSQLDialect链接到JPA? - delucasvb
1
你需要在Hibernate(或其他实现)的配置文件中定义属性:hibernate.dialect。在这里,你可以指定你的新方言CustomHsqlDialect及其类路径。希望你仍然感兴趣... - Philippe
2
spring.jpa.properties.hibernate.dialect = com.yourpackage.etc.CustomHsqlDialect - Timothy Mugayi

7
这个问题很旧,早已有答案,但我最近也遇到了这种情况,并找到了一个好的解决方案。首先,我发现Hibernate有三种不同的内置UUID类型实现:
  1. binary-uuid:以二进制形式存储UUID
  2. uuid-char:将UUID作为字符序列存储
  3. pg-uuid:使用本机Postgres UUID类型
这些类型已默认注册,并可以使用@Type注释指定给定字段,例如:
@Column
@Type(type = "pg-uuid")
private UUID myUuidField;

Dialect 中,还有一种覆盖默认类型的机制。因此,如果最终部署是与 Postgres 数据库交互,但单元测试使用 HSQL,则可以通过编写自定义方言来覆盖 pg-uuid 类型以读取/写入字符数据,如下所示:

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";
            }
        });
    }
}

现在只需插入自定义方言,pg-uuid类型就可以在两个环境中使用了。

5
我没有看到那个类中调用了addTypeOverride方法。我正在使用Hibernate 5。 - visola
似乎是Hibernate中已知的问题。https://github.com/spring-projects/spring-data-jpa/issues/1796#issuecomment-752499066 上述解决方案使用@Type("pg-uuid")对我没有起作用。 - Praveen Raghav

6
为了避免在不指定@Type注释的情况下出现UUID类型之间的问题(这基本上意味着当您想从postgres更改到mysql或反过来时,您必须调整所有注释...),我正在使用hibernates @TypeDef注释的package-info.java
以下是应用程序的示例设置:
假设module/src/main/java/app.package.domain包含您的实体。并且您的测试存储在module/src/test/java/app.package中。
只需在您的domain包中创建两个package-info.java即可。

请确保package-info文件始终位于同一包中(用于测试和生产)。请参见下面的示例:

src/main/java
  app
    package
      domain
        package-info.java 

src/test/java
  app
    package
      domain
        package-info.java 

您的生产环境中 package-info.java 的内容应该像这样(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;

以下是您的“配置”测试应该如何看起来的方式(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;

希望能对您有所帮助。

我觉得这很困难,可能是因为Eclipse使用了类路径的原因。但是,我通过将我的“生产”包信息放在更高级别的命名空间中,并将我的“测试”包信息放在较低级别的命名空间中来解决了这个问题。 - Jeremy

3

也许您可以在用户类型中构建一些智能功能,根据数据库的能力来执行正确的操作。Hibernate本身采用类似的方法,使用其“本地”ID生成器,具体取决于您正在使用的数据库类型。这样的方法消除了在运行时切换映射的需要。

例如,您可以为每个数据库创建一个strategy类。然后在用户类型类中,在第一次调用时检测您连接的数据库,实例化该数据库的适当策略,然后将所有调用委托给策略对象。


谢谢,你是救命恩人。策略模式起作用了,棒极了。 - mainstringargs

1

本答案基于其他回答,并适用于Hibernate 4。

使用@Type(type = "org.hibernate.type.PostgresUUIDType")的列定义解决方案1

使用以下HSQL方言:

import org.hibernate.boot.model.TypeContributions;
import org.hibernate.dialect.HSQLDialect;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.AbstractSingleColumnStandardBasicType;
import org.hibernate.type.PostgresUUIDType;
import org.hibernate.type.descriptor.java.UUIDTypeDescriptor;
import org.hibernate.type.descriptor.sql.VarcharTypeDescriptor;

public class UuidHsqlDialect extends HSQLDialect {

    @Override
    public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
        super.contributeTypes(typeContributions, serviceRegistry);

        typeContributions.contributeType(new AbstractSingleColumnStandardBasicType(VarcharTypeDescriptor.INSTANCE, UUIDTypeDescriptor.INSTANCE) {
            @Override
            public String getName() {
                return PostgresUUIDType.class.getName();
            }
        });
    }
}

使用@Type(type = "pg-uuid")定义列的解决方案2

使用以下HSQL方言:

import org.hibernate.boot.model.TypeContributions;
import org.hibernate.dialect.HSQLDialect;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.AbstractSingleColumnStandardBasicType;
import org.hibernate.type.descriptor.java.UUIDTypeDescriptor;
import org.hibernate.type.descriptor.sql.VarcharTypeDescriptor;

public class UuidHsqlDialect extends HSQLDialect {

    @Override
    public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
        super.contributeTypes(typeContributions, serviceRegistry);

        typeContributions.contributeType(new AbstractSingleColumnStandardBasicType(VarcharTypeDescriptor.INSTANCE, UUIDTypeDescriptor.INSTANCE) {
            @Override
            public String getName() {
                return PostgresUUIDType.class.getName();
            }
        });
    }
}

JPA配置

在(测试)application.properties中引用你的新的UuidHsqlDialect类:

spring.jpa.properties.hibernate.dialect=<full-package-name>.UuidHsqlDialect

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接