【问题标题】:Postgresql UUID supported by Hibernate?Hibernate 支持 Postgresql UUID?
【发布时间】:2011-05-28 13:41:36
【问题描述】:

我无法让 Hibernate 使用 java.util.UUID for PostgreSQL。

这是使用 javax.persistence.* 注解的映射:

private UUID itemUuid;

@Column(name="item_uuid",columnDefinition="uuid NOT NULL")
public UUID getItemUuid() {
    return itemUuid;
}

public void setItemUuid(UUID itemUuid) {
    this.itemUuid = itemUuid;
}

当持久化一个瞬态对象时,我得到一个 SQLGrammarException:

column "item_uuid" is of type uuid but expression is of type bytea at character 149

PostgreSQL 版本是 8.4.4
JDBC 驱动程序 - 8.4.4-702(也尝试过 9.0 - 同样的事情)
Hibernate版本是3.6,主要配置属性:

<property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
<property name="hibernate.connection.url">jdbc:postgresql://192.168.1.1/db_test</property>

【问题讨论】:

    标签: java hibernate postgresql orm hibernate-mapping


    【解决方案1】:

    您尝试持久化 UUID 类型的对象,该对象不是休眠注释的实体。所以hibernate想把它序列化为字节数组(blob类型)。这就是为什么您会收到此消息“bytea 类型的表达式”。

    您可以将 UUID 作为 blob 存储在数据库中(不优雅),或者提供您的自定义序列化程序(大量工作)或手动转换该对象。 UUID 类有 fromString 和 toString 方法,所以我会将它存储为 String。

    【讨论】:

    • 谢谢你的回答,lech,但根据opensource.atlassian.com/projects/hibernate/browse/HHH-3579 Hibernate 应该像使用其他任何类型一样使用 UUID 类型。从性能的角度来看,使用 char 类型的 uuid 是有害的。还有一个教程项目opensource.atlassian.com/projects/hibernate/browse/HHH-4093 显示了在没有冗余解决方法的情况下使用 UUID。
    • 该死,我刚刚注意到我提到的票证中的补丁不适用于 hibernate 3.6.0 发行版!解决了。​​
    • 此外,这些补丁不适用于显然具有一些重构的 hibernate 3.6.0 源。
    • 一般来说,将 ID 作为字符串存储在数据库中并不是一个好主意。因为它比数字类型消耗更多的字节,受编码的影响,它们被排序为文本。
    • 除了我对使用字符串作为键的批评之外:感谢您解释实际出了什么问题。 (点赞)
    【解决方案2】:

    这可以通过在 UUID 中添加以下注解来解决:

    import org.hibernate.annotations.Type;
    ...
    @Type(type="pg-uuid")
    private java.util.UUID itemUuid;
    

    至于为什么 Hibernate 不只是将其设为默认设置,我无法告诉你...

    更新: 使用 createNativeQuery 方法打开具有 UUID 字段的对象似乎仍然存在问题。幸运的是,到目前为止,createQuery 方法对我来说效果很好。

    【讨论】:

    • 只有当你不告诉 Hibernate 期望返回的类型时才会出现 createNativeQuery 的问题。问题是 Postgres 决定将多个自定义数据类型映射到其 JDBC 驱动程序中的 JDBC 类型代码 OTHER。因此,当在没有用户指示的情况下从结果集中读取这些值时,Hibernate 必须决定使用哪个。它是根据方言中注册的类型映射来实现的。
    • (续)如果您希望它在不为查询指定类型信息的情况下工作,请创建一个自定义方言(扩展您现在使用的 Postgres),在其构造函数中执行以下调用:registerHibernateType( Types.OTHER, "pg-uuid" );
    • 无法H2 数据库。 Caused by: org.hibernate.MappingException: No Dialect mapping for JDBC type: 1111
    • @Type(type="pg-uuid") 在最新版本的休眠 (5.2.12) 中无法识别。相反,我使用@Type(type="org.hibernate.type.PostgresUUIDType") 来解决问题。
    【解决方案3】:

    不使用 JPA 的人的解决方案。

    之前:

    <property name="testId" >
            <column name="test_id"  sql-type="uuid"  not-null="true"/>
    </property>
    

    之后:

    <property name="testId" column="test_id" type="org.hibernate.type.PostgresUUIDType">
    </property>
    

    【讨论】:

      【解决方案4】:

      正如其他人提到的,这个问题的解决方案是添加一个@Type(type = "pg-uuid") 注释。但是,这种类型与其他供应商的 UUID 类型不兼容,因此这会将您的 Hibernate 类与 Postgres 联系起来。要解决此问题,可以在运行时插入此注释。以下适用于 Hibernate 4.3.7。

      首先,您需要插入自定义元数据提供程序来插入注释。在创建Configuration 类的实例后,首先执行此操作:

      // Perform some test to verify that the current database is Postgres.
      if (connectionString.startsWith("jdbc:postgresql:")) {
          // Replace the metadata provider with our custom metadata provider.
          MetadataProviderInjector reflectionManager = MetadataProviderInjector)cfg.getReflectionManager();
          reflectionManager.setMetadataProvider(new UUIDTypeInsertingMetadataProvider(reflectionManager.getMetadataProvider()));
      }
      

      此自定义元数据提供程序可查找 UUID 类型的字段和方法。如果找到,它会插入一个 org.hibernate.annotations.Type 注释的实例,说明类型应该是 "pg-uuid"

      package nl.gmt.data;
      
      import org.hibernate.annotations.Parameter;
      import org.hibernate.annotations.Type;
      import org.hibernate.annotations.common.reflection.AnnotationReader;
      import org.hibernate.annotations.common.reflection.MetadataProvider;
      
      import java.lang.annotation.Annotation;
      import java.lang.reflect.AnnotatedElement;
      import java.lang.reflect.Field;
      import java.lang.reflect.Method;
      import java.util.Arrays;
      import java.util.HashMap;
      import java.util.Map;
      import java.util.UUID;
      
      class UUIDTypeInsertingMetadataProvider implements MetadataProvider {
          private final Map<AnnotatedElement, AnnotationReader> cache = new HashMap<>();
          private final MetadataProvider delegate;
      
          public UUIDTypeInsertingMetadataProvider(MetadataProvider delegate) {
              this.delegate = delegate;
          }
      
          @Override
          public Map<Object, Object> getDefaults() {
              return delegate.getDefaults();
          }
      
          @Override
          public AnnotationReader getAnnotationReader(AnnotatedElement annotatedElement) {
              // This method is called a lot of times on the same element, so annotation
              // readers are cached. We only cache our readers because the provider
              // we delegate to also caches them.
      
              AnnotationReader reader = cache.get(annotatedElement);
              if (reader != null) {
                  return reader;
              }
      
              reader = delegate.getAnnotationReader(annotatedElement);
      
              // If this element is a method that returns a UUID, or a field of type UUID,
              // wrap the returned reader in a new reader that inserts the "pg-uuid" Type
              // annotation.
      
              boolean isUuid = false;
              if (annotatedElement instanceof Method) {
                  isUuid = ((Method)annotatedElement).getReturnType() == UUID.class;
              } else if (annotatedElement instanceof Field) {
                  isUuid = ((Field)annotatedElement).getType() == UUID.class;
              }
      
              if (isUuid) {
                  reader = new UUIDTypeInserter(reader);
                  cache.put(annotatedElement, reader);
              }
      
              return reader;
          }
      
          private static class UUIDTypeInserter implements AnnotationReader {
              private static final Type INSTANCE = new Type() {
                  @Override
                  public Class<? extends Annotation> annotationType() {
                      return Type.class;
                  }
      
                  @Override
                  public String type() {
                      return "pg-uuid";
                  }
      
                  @Override
                  public Parameter[] parameters() {
                      return new Parameter[0];
                  }
              };
      
              private final AnnotationReader delegate;
      
              public UUIDTypeInserter(AnnotationReader delegate) {
                  this.delegate = delegate;
              }
      
              @Override
              @SuppressWarnings("unchecked")
              public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
                  if (annotationType == Type.class) {
                      return (T)INSTANCE;
                  }
      
                  return delegate.getAnnotation(annotationType);
              }
      
              @Override
              public <T extends Annotation> boolean isAnnotationPresent(Class<T> annotationType) {
                  return annotationType == Type.class || delegate.isAnnotationPresent(annotationType);
              }
      
              @Override
              public Annotation[] getAnnotations() {
                  Annotation[] annotations = delegate.getAnnotations();
                  Annotation[] result = Arrays.copyOf(annotations, annotations.length + 1);
                  result[result.length - 1] = INSTANCE;
                  return result;
              }
          }
      }
      

      【讨论】:

        【解决方案5】:

        现在您还可以使用 java.util.UUID 提供的 UUID 类,该类通过 Hibernate 映射到 Postgres 的 uuid 数据类型,而无需在从数据库读取/写入时进行任何转换。

          @Id
          @GeneratedValue
          private UUID id;
        

        默认情况下生成的值是自动的,这让您的 JVM 可以定义 UUID。这也允许 hibernate 使用批量插入优化。

        您可以配置数据库以设置 UUID 值。更多信息可以在这里找到https://vladmihalcea.com/uuid-identifier-jpa-hibernate/

        【讨论】:

          【解决方案6】:

          我在 postgres 中使用 Sprint Data 和 jsonb 时遇到了一个类似的问题。谢谢Sri 的解决方案!

          在模型中,替换

          @Type(type="pg-uuid")
          

          @Type(type="org.hibernate.type.PostgresUUIDType")
          

          解决了使用 @SpringBootTest 运行 JUnit 测试的问题。

          实体 (Kotlin) 中的示例:

          @Type(type="org.hibernate.type.PostgresUUIDType")
          @Column(
              nullable = false,
              unique = true,
              updatable = false,
              columnDefinition = "CHAR(36)"
          )
          var uuid: UUID = UUID.randomUUID()
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-08-14
            • 2012-07-02
            • 2021-03-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多