使用JPA在PostgreSQL中保留UUID

我试图在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。

我已经在同样的问题上或多或少地看到了很多关于SO的问题,但是它们都很老,似乎没有一个直接的答案。

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

任何帮助将不胜感激。

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

好吧,好吧……有点儿我使用JTDS驱动程序(v1.2.5)和SQL Server 2008 R2测试完全相同的代码(普通UUID,没有转换 – 上面发布的代码的第一个版本),这很有趣,猜猜看,它作为一个魅力(当然我不得不在persistence.xml中更改与连接相关的信息)。

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

不幸的是,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中对此进行了介绍

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

 @javax.persistence.Converter(autoApply = true) public class PostgresUuidConverter implements AttributeConverter { @Override public UUID convertToDatabaseColumn(UUID attribute) { return attribute; } @Override public UUID convertToEntityAttribute(UUID dbData) { return dbData; } } 

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

要让它与Hibernate 5.1.x一起使用,您可以在此处关注Steve Ebersole的评论

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

较新版本>= 8.4-701的Postgresql JDBC驱动程序正确处理java.util.UUID映射。 Hibernate >= 4.3.x

请参阅https://stackoverflow.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. 

Oleg建议的解决方案对我来说并不完美(如果你试图保持空值,则失败)。 这是一个调整的解决方案,也适用于空值。

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

 public class UuidConverter implements AttributeConverter { @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; } } 

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

像那样

JDBC:在PostgreSQL://本地主机:5432 / yourdatabase stringtype =未指定的

我确信,一旦为记录生成UUID,它将是静态的。 因此,将UUID作为对象映射到数据库,然后在检索时将其转换回String是没有意义的。

使用UUID作为字符串

 @Id @Column(name = "customer_id") private String id; //getter and setter public void setId(UUID id) { this.id = id.toString(); } 

还有一些事情需要考虑:

  1. 是否使用ID,UUID或两者。 对此进行了快速讨论 。
  2. 使用’uuid’代表名称而不是’id’,这是误导性的。
  3. 从UUID中删除“ – ”符号,保存4个字节。
  4. 如果使用Spring,则在@PrePersist部分生成UUID。