Postgresql Uuid поддерживается Hibernate?
-
12-10-2019 - |
Вопрос
Я не могу заставить Hibernate работать с java.util.uuid для 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>
Решение
Это можно решить, добавив следующую аннотацию в UUID:
import org.hibernate.annotations.Type;
...
@Type(type="pg-uuid")
private java.util.UUID itemUuid;
Что касается того, почему Hibernate не просто делает это настройкой по умолчанию, я не мог вам сказать ...
ОБНОВЛЕНИЕ: Похоже, есть проблемы с использованием метода CreatenativeQuery для открытия объектов, которые имеют поля UUID. К счастью, метод CreateQuery до сих пор хорошо работал для меня.
Другие советы
Вы пытаетесь сохранить объект типа UUID, который не является аннулированным с гибернатом сущностью. Таким образом, Hibernate хочет сериализовать его на массив байтов (тип BLOB). Вот почему вы получаете это сообщение «Выражение типа байта».
Вы можете либо хранить UUID в виде каждых в базе данных (не элегантно), либо предоставить свой собственный сериализатор (много работы), либо вручную конвертировать этот объект. У класса UUID есть методы от стрижки и топлива, поэтому я бы хранил его как строку.
Как упоминалось другие, решение этой проблемы состоит в том, чтобы добавить @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;
}
}
}
Решение для тех, кто не использует 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>