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?
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.
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.)
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.
You can use strategy: AUTO
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
Here is a working example
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With