Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to use Postgres' UUID generation with Hibernate's IDENTITY ID generation strategy?

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?

like image 405
Strengthiness Avatar asked Nov 28 '16 18:11

Strengthiness


People also ask

Can Postgres generate UUID?

Unfortunately, while PostgreSQL is great for storing and comparing UUID data, it lacks capabilities for creating UUID values in its core. Instead, it relies on third-party modules to create UUIDs using specified techniques.

What is the data type for UUID in PostgreSQL?

The data type uuid stores Universally Unique Identifiers (UUID) as defined by RFC 4122, ISO/IEC 9834-8:2005, and related standards. (Some systems refer to this data type as a globally unique identifier, or GUID, instead.)

What is UUID in Hibernate?

This annotation defines the Hibernate type mapping. Using “uuid-char” instructs Hibernate to store the UUID as a String, instead of the binary value. This way, it can be written to and read from a VARCHAR(36) column without the need for any pre-processing on our side.


2 Answers

You can use strategy: AUTO

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

Here is a working example

like image 95
Maksim Kostromin Avatar answered Sep 18 '22 14:09

Maksim Kostromin


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.

like image 38
Strengthiness Avatar answered Sep 19 '22 14:09

Strengthiness