Hibernate是否支持PostgreSQL UUID?

k4ymrczo  于 2023-06-22  发布在  PostgreSQL
关注(0)|答案(7)|浏览(187)

我无法让Hibernate与PostgreSQL的java.util.UUID一起工作。
下面是使用javax.persistence.* annotations的Map:

private UUID itemUuid;

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

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

当持久化一个 transient 对象时,我得到一个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>
xfb7svmp

xfb7svmp1#

这可以通过向UUID添加以下注解来解决:

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

至于为什么Hibernate不把它设为默认设置,我不能告诉你……
更新:使用createNativeQuery方法打开具有UUID字段的对象似乎仍然存在问题。幸运的是,createQuery方法到目前为止对我来说工作得很好。

pexxcrt2

pexxcrt22#

现在你也可以使用java.util.UUID提供的UUID类,它被HibernateMap到Postgres的uuid数据类型,而不需要在从数据库阅读/写入时进行任何转换。

@Id
  @GeneratedValue
  private UUID id;

默认情况下生成的值是auto,这允许JVM定义UUID。这也允许hibernate使用批量插入优化。
您可以配置数据库以设置UUID值。更多信息可在此处找到https://vladmihalcea.com/uuid-identifier-jpa-hibernate/

e5njpo68

e5njpo683#

您尝试持久化UUID类型的对象,该对象不是带休眠注解的实体。所以hibernate希望将其序列化为字节数组(blob类型)。这就是为什么你得到这个消息'expression of type bytea'。
您可以将UUID存储为数据库中的blob(不优雅),或提供自定义序列化程序(需要大量工作)或手动转换该对象。UUID类有fromString和toString方法,所以我将其存储为String。

bprjcwpo

bprjcwpo4#

正如其他人提到的,这个问题的解决方案是添加一个@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;
        }
    }
}
r7xajy2e

r7xajy2e5#

不使用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>
2w3kk1z5

2w3kk1z56#

我在postgres中使用Sprint Data和jsonb时遇到了类似的问题。谢谢Sri的解决方案!
在模型中,替换

@Type(type="pg-uuid")

@Type(type="org.hibernate.type.PostgresUUIDType")

解决了使用@SpringBootTest运行JUnit测试的问题。
实体(Kotlin)中的示例:

@Type(type="org.hibernate.type.PostgresUUIDType")
@Column(
    nullable = false,
    unique = true,
    updatable = false,
    columnDefinition = "CHAR(36)"
)
var uuid: UUID = UUID.randomUUID()
dzhpxtsq

dzhpxtsq7#

对于我使用的新hibernate

@JdbcType(UUIDJdbcType.class)

相关问题