使用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(); }
还有一些事情需要考虑:
- 是否使用ID,UUID或两者。 对此进行了快速讨论 。
- 使用’uuid’代表名称而不是’id’,这是误导性的。
- 从UUID中删除“ – ”符号,保存4个字节。
- 如果使用Spring,则在@PrePersist部分生成UUID。
- Hibernate使用hibernate.jdbc.batch_versioned_data保存过时数据
- 如何从代理类中检索代理类?
- Hibernate JPA ManyToOne复合键
- Spring + Hibernate的多租户:“SessionFactory配置为多租户,但没有指定租户标识符”
- 用于MSSQL 2014的错误的hibernate方言
- 与hibernate注释的接口
- JavaFX + Hibernate(JPA)持久性+ Derby DB
- 使用EAGER @ElementCollection对find()进行Hibernate LazyInitializationException
- Hibernate:无法执行本机批量操作查询