13

I'm using Spring Boot 1.4.1, which includes Hibernate 5, with Postgres 9.6, and I'm trying to create an entity with a UUID ID but using Postgres' UUID generation instead of Hibernate's. Many similar questions say to set the column type as pg-uuid. That seems to work for non-database-generated ID columns, but when I try to use it for the ID column I get

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

So it looks like Hibernate is applying the type correctly, but not converting it.

Here is how the ID column of my entity is setup:

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

And the table is setup similar to as follows (uuid-ossp is installed)

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

I would prefer to have the database generate the UUIDs and don't want to use Hibernate's generation strategies. Is there a way to get this to work?

Strengthiness
  • 561
  • 1
  • 5
  • 12

3 Answers3

11

You can use strategy: AUTO

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;

Here is a working example

Cray
  • 2,774
  • 7
  • 22
  • 32
Maksim Kostromin
  • 3,273
  • 1
  • 32
  • 30
3

One way to solve this is to create a custom UserType that implements ResultSetIdentifierConsumer. As an example, create a class called PostgresIdUUIDType that extends PostgresUUIDType and additionally implement the interface ParameterizedType for configuration:

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

Then in your entity, define the custom type and use it for your ID column like this:

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;

    ...

}

The column @Parameter makes it possible to specify a different database column to use instead of the default provided id column. Similar to how @Column(name = "tag_id") works.

chvndb
  • 615
  • 8
  • 18
Strengthiness
  • 561
  • 1
  • 5
  • 12
-2

Should use the strategy GenerationType.AUTO or GenerationType (with AUTO as default) for @GeneratedValue

(Optional) The primary key generation strategy that the persistence provider must use to generate the annotated entity primary key.

GenerationType strategy() default AUTO;

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

GL

Source

Braian Coronel
  • 22,105
  • 4
  • 57
  • 62
  • How does this answer differ to @Maksim Kostromin answer? Secondly the question was about Java, not Kotlin. – Jacob van Lingen Oct 09 '20 at 06:13
  • Do you need a drawing? AUTO is by default, the name of the column is a uuid not an id, and it is better to have the annotation to match with the db. The problem is JVM, not language. That you don't understand Kotlin does not mean that it does not add value to the rest, you can use a converter. – Braian Coronel Oct 09 '20 at 14:29