Using UUID with EclipseLink and PostgreSQL

强颜欢笑 提交于 2019-12-01 05:38:47

问题


I want to use the PostgreSQL uuid type for objects' primary keys. For that I've created a converter (implementing the Converter interface). Bellow is the relevant code:

    @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");
    }

Then I've annotated the relevant entity X with the bellow annotations:

@Converter(name="uuidConverter",converterCalss=UUIDConverter.class)
@Convert("uuidConverter")
@Id
public UUID getId()
{
    return id;
}

The problem is that I have another class (Y) which has the following definition:

@ManyToOne(targetEntity = X.class)
@JoinColumn(name = "x_id")
public X getX();

Although EclipseLink created the tables as expected it sends a string to the database when trying to insert objects of type Y. Postgres returns the following error message:

column "id" is of type uuid but expression is of type character varying at character

Any solutions / work around will be appreciated.


回答1:


I had the same issue with EclipseLink JPA + Postgresql + UUID as primary key.

To solve it, I've merged codes from Github and below link: https://forums.oracle.com/forums/thread.jspa?messageID=4584157

The below code for UUIDConverter worked for me, though the code surely isn't the best.

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");
            }
        }
    }
}



回答2:


Try checking what the fieldClassification of the mapping is in the initialize method. It might be getting String.class somehow, try setting it to Object.class.

or, field.setType(Object.class)




回答3:


I had some issues with EclipseLink JPA 2.1 + Postgresql + UUID as primary key but I find out different solution. I adopted AttributeConverter but I faced a problem with EclipseLink implementation that I resolved with this code:

@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;
    }

}

As I exaplined in detail in this post http://blog-ungarida.rhcloud.com/persisting-uuid-in-postgresql-using-jpa-eclipselink/




回答4:


Universal UUIDConverter for EclipseLink (not only PostgreSQL)

Code:

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");
            }
        }

    }
}



回答5:


You don't need a converted. Use this column definition in the entity. You need to register the uuid extension first. This works with Postgres 10 and Wildfly 10.1

@Column(name = "UUID", nullable=false, insertable = false, columnDefinition="uuid DEFAULT uuid_generate_v4()")
private String uuid;


来源:https://stackoverflow.com/questions/13346089/using-uuid-with-eclipselink-and-postgresql

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!