我想将PostgreSQL uuid类型用于对象的主键。 为此,我创建了一个转换器(实现Converter接口)。 贝娄是相关代码:
@Override
public void initialize(DatabaseMapping mapping, Session session) {
final DatabaseField field;
if (mapping instanceof DirectCollectionMapping) {
field = ((DirectCollectionMapping) mapping).getDirectField();
} else {
field = mapping.getField();
}
field.setSqlType(Types.OTHER);
field.setTypeName("uuid");
field.setColumnDefinition("UUID");
}
然后我用下面的注释注释了相关的实体X:
@Converter(name="uuidConverter",converterCalss=UUIDConverter.class)
@Convert("uuidConverter")
@Id
public UUID getId()
{
return id;
}
问题是我有另一个类(Y),其定义如下:
@ManyToOne(targetEntity = X.class)
@JoinColumn(name = "x_id")
public X getX();
尽管EclipseLink按预期创建了表,但在尝试插入Y类型的对象时,它会向数据库发送一个字符串。 Postgres返回以下错误消息:
column "id" is of type uuid but expression is of type character varying at character
任何解决方案/解决方案都将受到赞赏。
答案 0 :(得分:2)
我将EclipseLink JPA + Postgresql + UUID作为主键存在同样的问题。
要解决这个问题,我已经合并了Github和以下链接的代码: https://forums.oracle.com/forums/thread.jspa?messageID=4584157
下面的UUIDConverter代码对我有用,虽然代码肯定不是最好的。
public void initialize(DatabaseMapping ARGMapping, Session ARGSession)
{
final DatabaseField Field;
if (ARGMapping instanceof DirectCollectionMapping)
{
Field = ((DirectCollectionMapping) ARGMapping).getDirectField();
}
else
{
Field = ARGMapping.getField();
}
Field.setSqlType(Types.OTHER);
Field.setTypeName("uuid");
Field.setColumnDefinition("UUID");
for (DatabaseMapping m : ARGMapping.getDescriptor().getMappings())
{
assert OneToOneMapping.class.isAssignableFrom(ManyToOneMapping.class);
if (m instanceof OneToOneMapping)
{
for (DatabaseField field : ((OneToOneMapping) m).getForeignKeyFields())
{
field.setSqlType(Types.OTHER);
field.setColumnDefinition("UUID");
field.setTypeName("uuid");
}
}
}
}
答案 1 :(得分:1)
尝试检查初始化方法中映射的fieldClassification。它可能以某种方式获取String.class,尝试将其设置为Object.class。
或,field.setType(Object.class)
答案 2 :(得分:1)
我在使用EclipseLink JPA 2.1 + Postgresql + UUID作为主键时出现了一些问题,但我发现了不同的解决方案。我采用了AttributeConverter,但我遇到了EclipseLink实现的问题,我用这段代码解决了这个问题:
@javax.persistence.Converter(autoApply = true)
public class PostgresUuidConverter implements AttributeConverter<UUID, Object> {
@Override
public Object convertToDatabaseColumn(UUID uuid) {
PostgresUuid object = new PostgresUuid();
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) {
if (dbData instanceof String) {
return UUID.fromString(dbData.toString());
} else {
return (UUID) dbData;
}
}
}
public class PostgresUuid extends PGobject implements Comparable<Object> {
private static final long serialVersionUID = 1L;
@Override
public int compareTo(Object arg0) {
return 0;
}
}
正如我在这篇文章http://blog-ungarida.rhcloud.com/persisting-uuid-in-postgresql-using-jpa-eclipselink/
中详细介绍的那样答案 3 :(得分:0)
EclipseLink的通用UUIDConverter(不仅仅是PostgreSQL)
<强>代码:强>
import java.nio.ByteBuffer;
import java.util.UUID;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.DirectCollectionMapping;
import org.eclipse.persistence.mappings.converters.Converter;
import org.eclipse.persistence.sessions.Session;
public class UUIDConverter implements Converter {
private Boolean isUUIDasByteArray = true;
@Override
public Object convertObjectValueToDataValue(Object objectValue,
Session session) {
if (isUUIDasByteArray) {
UUID uuid = (UUID)objectValue;
if (uuid == null) return null;
byte[] buffer = new byte[16];
ByteBuffer bb = ByteBuffer.wrap(buffer);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return buffer;
}
return objectValue;
}
@Override
public UUID convertDataValueToObjectValue(Object dataValue,
Session session) {
if (isUUIDasByteArray) {
byte[] bytes = (byte[])dataValue;
if (bytes == null) return null;
ByteBuffer bb = ByteBuffer.wrap(bytes);
long high = bb.getLong();
long low = bb.getLong();
return new UUID(high, low);
}
return (UUID) dataValue;
}
@Override
public boolean isMutable() {
return true;
}
@Override
public void initialize(DatabaseMapping mapping, Session session) {
final DatabaseField field;
if (mapping instanceof DirectCollectionMapping) {
// handle @ElementCollection...
field = ((DirectCollectionMapping) mapping).getDirectField();
} else {
field = mapping.getField();
}
if (session != null && session.getLogin()!= null && session.getLogin().getPlatform() != null) {
String platform = session.getLogin().getPlatform().getClass().getSimpleName();
if (platform.equals("PostgreSQLPlatform")) {
field.setSqlType(java.sql.Types.OTHER);
field.setTypeName("java.util.UUID");
field.setColumnDefinition("UUID");
isUUIDasByteArray = false;
} else if (platform.equals("H2Platform")) {
field.setColumnDefinition("UUID");
} else if (platform.equals("OraclePlatform")) {
field.setColumnDefinition("RAW(16)");
} else if (platform.equals("MySQLPlatform")) {
field.setColumnDefinition("BINARY(16)");
} else if (platform.equals("SQLServerPlatform")) {
field.setColumnDefinition("UNIQUEIDENTIFIER");
}
}
}
}
答案 4 :(得分:0)
你不需要转换。在实体中使用此列定义。您需要先注册uuid扩展。这适用于Postgres 10和Wildfly 10.1
@Column(name = "UUID", nullable=false, insertable = false, columnDefinition="uuid DEFAULT uuid_generate_v4()")
private String uuid;
答案 5 :(得分:0)
似乎EclipseLink和PostgresQL之间存在错误/不兼容。如果仅将UUID用作主键,则应该可以。但是,如果您具有可为空的UUID列,并尝试在其中存储null,则会收到报告的错误:
column "whatever" is of type uuid but expression is of type character varying
请参阅:https://bugs.eclipse.org/bugs/show_bug.cgi?id=538138(如果有时间,请登录并投票!)
该错误报告对我非常有用。专门指向论坛主题的链接位于:
https://www.eclipse.org/forums/index.php?t=msg&th=1073632&goto=1719530&#msg_1719530
我在SO以及网络上的其他地方尝试了各种解决方案。似乎对我有用的唯一一个是David Wheeler在那发布的那个。具体来说,是在数据库中创建从character varying
到uuid
的转换。
请注意,您必须是用户postgres才能创建演员表:
$ sudo su - postgres
$ psql <your database name>
# drop cast if exists (character varying as uuid);
# create or replace function uuid(_text character varying) returns uuid language sql as 'select uuid_in(_text::cstring)';
# create cast (character varying as uuid) with function uuid(character varying) as assignment;
为了完整起见,这是我使用的其余内容(以防万一)
我所有的实体(具有UUID主键)都扩展了名为EntityBase
的基类:
package com.example.entity;
import java.io.Serializable;
import java.util.UUID;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import org.eclipse.persistence.annotations.Convert;
import org.eclipse.persistence.annotations.Converter;
import com.example.converter.UUIDTypeConverter;
@MappedSuperclass
@Converter(name="uuidConverter", converterClass=UUIDTypeConverter.class)
public class EntityBase implements Serializable, Cloneable
{
private static final long serialVersionUID = 1L;
@Id
@Convert("uuidConverter")
private UUID id;
public EntityBase() {
this.id = UUID.randomUUID();
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof EntityBase)) {
return false;
}
EntityBase other = (EntityBase) obj;
return getId().equals(other.getId());
}
public UUID getId()
{
return this.id;
}
public void setId(UUID id)
{
this.id = id;
}
}
UUID转换器类如下:
package com.example.converter;
import java.sql.Types;
import java.util.UUID;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.converters.Converter;
import org.eclipse.persistence.sessions.Session;
public class UUIDTypeConverter implements Converter
{
@Override
public UUID convertObjectValueToDataValue(Object objectValue, Session session)
{
return (UUID) objectValue;
}
@Override
public UUID convertDataValueToObjectValue(Object dataValue, Session session)
{
return (UUID) dataValue;
}
@Override
public boolean isMutable()
{
return true;
}
@Override
public void initialize(DatabaseMapping mapping, Session session)
{
DatabaseField field = mapping.getField();
field.setSqlType(Types.OTHER);
field.setTypeName("java.util.UUID");
field.setColumnDefinition("UUID");
}
}
如果您的实体具有不是主键的UUID列,则可以按以下方式注释它们:
import org.eclipse.persistence.annotations.Convert
import org.eclipse.persistence.annotations.Converter;
@Entity
@Converter(name="uuidConverter", converterClass=UUIDTypeConverter.class)
public class BillingEvent extends EntityBase
{
@Convert("uuidConverter")
private UUID entityId;
}
请注意,如果该实体还有其他使用标准javax.persistence.convert
注释的列,则需要区分两个Convert
注释以避免编译错误。
例如:
import javax.persistence.Convert;
import org.eclipse.persistence.annotations.Converter;
@Entity
@Converter(name="uuidConverter", converterClass=UUIDTypeConverter.class)
public class BillingEvent extends EntityBase
{
@org.eclipse.persistence.annotations.Convert("uuidConverter")
private UUID entityId;
@Convert(converter = JSR310InstantTypeConverter.class)
private Instant createdOn;
}
我希望这可以节省一些时间。祝你好运!