使用 JPA 在 PostgreSQL 中持久化 UUID

Posted

技术标签:

【中文标题】使用 JPA 在 PostgreSQL 中持久化 UUID【英文标题】:Persisting UUID in PostgreSQL using JPA 【发布时间】:2012-07-02 07:37:19 【问题描述】:

我正在尝试在 PostgreSQL 中保留一个使用 UUID 作为主键的实体。我尝试将其作为普通 UUID 持久化:

@Id
@Column(name = "customer_id")
private UUID id;

通过上述,我得到这个错误:

ERROR: column "customer_id" is of type uuid but expression is of type bytea
Hint: You will need to rewrite or cast the expression.
Position: 137

我也尝试将 UUID 保存为 byte[] 无济于事:

@Transient
private UUID id;

@Id
@Column(name = "customer_id")
@Access(AccessType.PROPERTY)
@Lob
protected byte[] getRowId() 
    return id.toString().getBytes();


protected void setRowId(byte[] rowId) 
    id = UUID.fromString(new String(rowId));

如果我删除@Lob,我得到的错误与上面发布的错误相同。但是应用@Lob 后,错误会稍微变为:

ERROR: column "customer_id" is of type uuid but expression is of type bigint
Hint: You will need to rewrite or cast the expression.
Position: 137

无法做这么简单的事情,我感觉非常糟糕!

我正在使用 Hibernate 4.1.3.Final 和 PostgreSQL 9.1。

我已经或多或少地看到过很多关于相同问题的问题,但它们都是旧的,似乎没有一个直截了当的答案。

我想以一种标准的方式来实现这一点,而不是求助于丑陋的黑客。但是,如果这只能通过(丑陋的)黑客来实现,那么这就是我要做的。但是,我不想将 UUID 作为 varchar 存储在数据库中,因为这不利于性能。另外,如果可能的话,我不想在我的代码中引入 Hibernate 依赖项。

任何帮助将不胜感激。

更新 1(2012-07-03 12:15 pm)

嗯,嗯,嗯...这有点有趣,我使用 JTDS 驱动程序(v1.0)在 SQL Server 2008 R2 上测试了完全相同的代码(纯 UUID,没有转换——上面发布的代码的第一个版本)。 2.5),你猜怎么着,它就像一个魅力(当然我必须在persistence.xml中更改与连接相关的信息)。

现在,这是 PostgreSQL 特有的问题还是什么?

【问题讨论】:

Postgresql UUID supported by Hibernate? 的可能重复项 正如我在帖子中提到的,有很多关于 SO 的问题都存在相同的问题,但它们都很老旧,似乎没有一个好的、有效的解决方案。您提到的帖子也不例外(顺便说一句,我在发布问题之前就看到了)。 【参考方案1】:

不幸的是,PostgreSQL JDBC 驱动程序选择了一种表示非 JDBC 标准类型代码的方式。他们只是将所有这些映射到 Types.OTHER。长话短说,您需要启用特殊的 Hibernate 类型映射来处理 UUID 映射(到 postgres 特定的 uuid 数据类型的列):

@Id
@Column(name = "customer_id")
@org.hibernate.annotations.Type(type="org.hibernate.type.PostgresUUIDType")
private UUID id;

或更简洁:

@Id
@Column(name = "customer_id")
@org.hibernate.annotations.Type(type="pg-uuid")
private UUID id;

另一个(更好的)选项是将 org.hibernate.type.PostgresUUIDType 注册为所有作为 java.util.UUID 公开的属性的默认 Hibernate 类型映射。这在文档 @http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch06.html#types-registry

中有介绍

【讨论】:

+1 这有效:@org.hibernate.annotations.Type(type="pg-uuid") 嗨,我试过了,但我得到了一个 org.hibernate.MappingException:没有 JDBC 类型的方言映射:1111。任何人都可以帮助我吗? @jonfornari 我遇到了同样的问题,但忘记在我的 Grails 项目中选择 org.hibernate.dialect.PostgreSQLDialect。也许你错过了那个或其他一些设置?我不需要以下内容,但它可能会对您有所帮助:andrew-arch.blogspot.com/2008/02/… 要使其与 JPA 2.0EclipseLink (Glassfish 3.1.2) 一起使用,请查看 github.com/ancoron/pg-inet-maven/wiki/… 使用EclipseLinkJPA 2.0 找到更简单的解决方案 - 只需使用@Convert("uuidConverter") @TypeConverter(name = "uuidConverter", dataType = Object.class, objectType = UUID.class) private UUID id; 注释您的UUID 字段(来自'org.eclipse.persistence.annotations' 包的两个注释)【参考方案2】:

JPA 2.1 提供了一种非常简单的方法来使用 PostgreSQL uuid 列类型和java.util.UUID 作为对应实体字段的类型:

@javax.persistence.Converter(autoApply = true)
public class PostgresUuidConverter implements AttributeConverter<UUID, UUID> 

    @Override
    public UUID convertToDatabaseColumn(UUID attribute) 
        return attribute;
    

    @Override
    public UUID convertToEntityAttribute(UUID dbData) 
        return dbData;
    


只需将此类添加到您的持久性配置中,并使用 @Column(columnDefinition="uuid") 注释 UUID 字段。

【讨论】:

对此没有任何运气。 @Converter 注解不适用于类。 我不确定你的意思。 javax.persistence.Converter 和 org.eclipse.persistence.annotations.Converter 都可以应用于一个类。我编辑了答案以明确说明它使用 javax.persistence.Converter。 成功了 (PG9.4) - 我也不需要 @Column 注释。 您不能将它用于@Id 字段。【参考方案3】:

要让它与 Hibernate 5.1.x 一起使用,您可以关注 Steve Ebersole comment here

@Id
@GeneratedValue
@Column( columnDefinition = "uuid", updatable = false )
public UUID getId() 
    return id;

【讨论】:

如果可以的话,我会为这个答案发两次声。 columnDefinition 规范解决了我在创建表时将 UUID 替换为几何类型的问题。发生这种情况是因为使用了 PostgisDialect。【参考方案4】:

Postgresql JDBC 驱动程序的较新版本 &gt;= 8.4-701 正确处理 java.util.UUID 映射。 Hibernate &gt;= 4.3.x 也是如此。

查看https://***.com/a/780922/173149的详细信息:

Map the database uuid type to java.util.UUID.
This only works for relatively new server (8.3) and JDK (1.5) versions.

【讨论】:

【参考方案5】:

Oleg 建议的解决方案对我来说并不完美(如果您尝试保留空值,它会失败)。这是一个经过调整的解决方案,也适用于空值。

在我的情况下,我使用的是 EclipseLink,所以如果你使用的是 Hibernate,你可能不需要这个。

public class UuidConverter implements AttributeConverter<UUID, Object> 
    @Override
    public Object convertToDatabaseColumn(UUID uuid) 
        PGobject object = new PGobject();
        object.setType("uuid");
        try 
            if (uuid == null) 
                object.setValue(null);
             else 
                object.setValue(uuid.toString());
            
         catch (SQLException e) 
            throw new IllegalArgumentException("Error when creating Postgres uuid", e);
        
        return object;
    

    @Override
    public UUID convertToEntityAttribute(Object dbData) 
        return (UUID) dbData;
    

【讨论】:

【参考方案6】:

在 postgresql url 连接上添加这个标志:?stringtype=unspecified

这样

jdbc:postgresql://localhost:5432/yourdatabase?stringtype=unspecified

【讨论】:

虽然这可能会回答这个问题,但最好解释一下答案的基本部分,以及 OP 代码可能存在什么问题。

以上是关于使用 JPA 在 PostgreSQL 中持久化 UUID的主要内容,如果未能解决你的问题,请参考以下文章

JPA移植到PostgreSQL时关于CLOB, BLOB及JSON类型的处理

如何使用JPA持久化多维Java数组?

如何使每个表中的主键值应从数字一(1)开始 - PostgreSQL,Spring data jpa

为啥需要在执行修改查询之前清除 jpa 持久性上下文?

如何在 JPA 2 实体中映射 postgresql“带有时区的时间戳”

JAVA spring (JPA) 中的PostgreSQL + Elasticsearch 同步