Postgres的UUID生成方式能否与Hibernate的IDENTITY ID生成策略配合使用?

13

我正在使用包含Hibernate 5的Spring Boot 1.4.1版本和Postgres 9.6,尝试创建一个带有UUID ID的实体,但是要使用Postgres的UUID生成而不是Hibernate的。许多类似的问题建议将列类型设置为pg-uuid。这似乎适用于非数据库生成的ID列,但是当我尝试将其用于ID列时,会出现以下错误:

org.hibernate.id.IdentifierGenerationException: unrecognized id type : pg-uuid -> java.util.UUID

所以看起来Hibernate正在正确应用该类型,但没有进行转换。

下面是我的实体的ID列设置:

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@org.hibernate.annotations.Type(type="pg-uuid")
private UUID id;

并且表格的设置与以下类似 (uuid-ossp已安装)

create table example (
    id UUID NOT NULL DEFAULT uuid_generate_v1mc(),
    ...
);

我希望数据库生成UUID,不想使用Hibernate的生成策略。有没有办法让它工作?


仅使用数据库UUID的原因是什么? - Mohan Radhakrishnan
1
可移植性。有些记录不会通过Hibernate创建。 - Strengthiness
3个回答

11

5
使用GenerationType.AUTO不能让数据库生成uuid,而是由jvm生成uuid。 - Olivier Boissé

3

解决这个问题的一种方法是创建一个实现 ResultSetIdentifierConsumer 接口的自定义 UserType。例如,创建一个名为 PostgresIdUUIDType 的类,继承了 PostgresUUIDType 并额外实现了 ParameterizedType 接口进行配置:

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import java.util.TimeZone;
import java.util.UUID;

import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.id.IdentifierGenerationException;
import org.hibernate.id.ResultSetIdentifierConsumer;
import org.hibernate.type.PostgresUUIDType;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.usertype.ParameterizedType;

public class PostgresIdUUIDType
    extends PostgresUUIDType
    implements ResultSetIdentifierConsumer, ParameterizedType {

  private String idColumnName = "id";

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

  @Override
  public void setParameterValues(Properties params) {
    idColumnName = params.getProperty("column");
  }

  @Override
  public UUID consumeIdentifier(ResultSet resultSet) throws IdentifierGenerationException {
    try {
      return nullSafeGet(resultSet, idColumnName, wrapperOptions());
    } catch (SQLException e) {
      throw new IdentifierGenerationException("Error converting type", e);
    }
  }

  private WrapperOptions wrapperOptions() {
    return new WrapperOptions() {
      @Override
      public boolean useStreamForLobBinding() {
        return false;
      }

      @Override
      public LobCreator getLobCreator() {
        return null;
      }

      @Override
      public SqlTypeDescriptor remapSqlTypeDescriptor(final SqlTypeDescriptor sqlTypeDescriptor) {
        return PostgresUUIDSqlTypeDescriptor.INSTANCE;
      }

      @Override
      public TimeZone getJdbcTimeZone() {
        return TimeZone.getDefault();
      }
    };
  }
}

然后在你的实体中定义自定义类型,并像这样将其用于ID列:

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;

import org.hibernate.annotations.Parameter;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;

@TypeDefs({@TypeDef(name = "pg-id-uuid", typeClass = PostgresIdUUIDType.class) })
@Entity
public class Example {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Type(type="pg-id-uuid", parameters = @Parameter(name = "column", value = "id_column_name"))
    private UUID id;

    ...

}

@Parameter使您可以指定不同的数据库列来代替提供的默认id列。类似于@Column(name = "tag_id")的工作原理。


-2

应该使用策略GenerationType.AUTOGenerationType(默认为AUTO)来为@GeneratedValue生成主键

(可选)持久性提供程序必须使用的主键生成策略来生成注释实体的主键。

GenerationType strategy() default AUTO;

@Entity
@Table("custom")
data class Custom(
        @Id @GeneratedValue @Column(name = "column_uuid") val columnUUID: UUID
)

GL

源代码


这个回答与 @Maksim Kostromin 的回答有何不同?其次,问题是关于 Java 而不是 Kotlin。 - Jacob van Lingen
你需要一张图吗?AUTO是默认值,列的名称是uuid而不是id,最好使用注释与数据库匹配。问题在于JVM,而不是语言。你不理解Kotlin并不意味着它对其他方面没有价值,你可以使用转换器。 - Braian Coronel

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