Pregunta

No se puede obtener Hibernate trabajar con java.util.UUID para PostgreSQL.

Esta es la asignación mediante javax.persistence * anotaciones:.

private UUID itemUuid;

@Column(name="item_uuid",columnDefinition="uuid NOT NULL")
public UUID getItemUuid() {
    return itemUuid;
}

public void setItemUuid(UUID itemUuid) {
    this.itemUuid = itemUuid;
}

Cuando la persistencia de un objeto transitorio consigo un SQLGrammarException:

column "item_uuid" is of type uuid but expression is of type bytea at character 149

versión de PostgreSQL 8.4.4 es
Controlador JDBC - 8.4.4-702 (también trató 9,0 - lo mismo)
versión Hibernate es 3,6, principales propiedades de configuración:

<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>
¿Fue útil?

Solución

Esto se puede resolver mediante la adición de la siguiente anotación a la UUID:

import org.hibernate.annotations.Type;
...
@Type(type="pg-uuid")
private java.util.UUID itemUuid;

En cuanto a por qué Hibernate no se limita a hacer este ajuste por defecto, no podría usted ...

decirle

ACTUALIZACIÓN: Todavía se parece haber problemas utilizando el método de createNativeQuery a objetos abiertos que tienen campos UUID. Afortunadamente, el método CreateQuery hasta ahora ha funcionado bien para mí.

Otros consejos

Se trata de persistir objeto de tipo UUID, que no es entidad de hibernación-anotada. Por lo que la hibernación quiere serializarlo de matriz de bytes (tipo burbuja). Es por eso que este mensaje 'expresión de tipo bytea'.

Se pueden almacenar ya sea UUID como manchas en la base de datos (no elegante), o proporcionar su serializador personalizado (mucho trabajo) o convertir manualmente ese objeto. UUID clase tiene métodos fromstring y toString, así que almacenarla como cadena.

Como otros han mencionado, la solución a este problema es añadir una anotación de @Type(type = "pg-uuid"). Sin embargo, este tipo no es compatible con los tipos de UUID de otros proveedores, por lo que esto se vincula a sus clases de Hibernate Postgres. Para evitar esto, es posible insertar esta anotación en tiempo de ejecución. La continuación de las obras de Hibernate 4.3.7.

En primer lugar, es necesario insertar un proveedor de metadatos personalizados para insertar las anotaciones. Para ello, como primer paso después de crear una instancia de la clase 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()));
}

Este proveedor de metadatos personalizados encuentra campos y métodos de tipo UUID. Si encuentra uno, se inserta una instancia de la anotación org.hibernate.annotations.Type que indica que el tipo debe ser "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;
        }
    }
}

Solución para alguien que no utilizan JPA.

Antes:

<property name="testId" >
        <column name="test_id"  sql-type="uuid"  not-null="true"/>
</property>

Después de:

<property name="testId" column="test_id" type="org.hibernate.type.PostgresUUIDType">
</property>
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top