web-dev-qa-db-fra.com

UUID persistant dans PostgreSQL avec JPA

J'essaie de conserver une entité dans PostgreSQL qui utilise UUID comme clé primaire. J'ai essayé de le conserver comme un UUID ordinaire:

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

Avec ce qui précède, je reçois cette erreur: 

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

J'ai aussi essayé de conserver l'UUID sous forme d'octet [] en vain:

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

Si je supprime @Lob, l'erreur que je reçois est la même que celle affichée ci-dessus. Mais avec @Lob appliqué, l'erreur se modifie légèrement en:

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

Je me sens extrêmement mal d'être incapable de faire quelque chose d'aussi simple que cela! 

J'utilise Hibernate 4.1.3.Final avec PostgreSQL 9.1. 

J'ai vu de nombreuses questions sur SO plus ou moins avec le même problème, mais elles sont toutes anciennes et aucune ne semble avoir une réponse directe. 

Je voudrais y parvenir de manière standard sans recourir à des hacks laids. Mais si cela ne peut être réalisé que si (laid) hacks, alors peut-être que c'est ce que je vais faire. Cependant, je ne veux pas stocker l'UUID en tant que varchar dans la base de données, car cela ne convient pas aux performances. De plus, je ne voudrais pas introduire une dépendance Hibernate dans mon code si possible.

Toute aide serait grandement appréciée.

MISE À JOUR 1 (2012-07-03 12:15)

Eh bien, bien, bien ... Il est intéressant de noter que j'ai testé exactement le même code (UUID simple, aucune conversion - la première version du code publiée ci-dessus) avec SQL Server 2008 R2 en utilisant le pilote JTDS (v1.2.5) et , devinez quoi, cela a fonctionné comme un charme (bien sûr, je devais changer les informations relatives à la connexion dans persistence.xml). 

Maintenant, est-ce un problème spécifique à PostgreSQL ou quoi?

24
ehsanullahjan

Le pilote JDBC de PostgreSQL a malheureusement choisi un moyen de représenter des codes de type non standard JDBC. Ils mappent simplement tous à Types.OTHER. En résumé, vous devez activer un mappage de type Hibernate spécial pour gérer les mappages UUID (vers les colonnes du type de données uuid spécifique à postgres):

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

ou plus succinctement:

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

Une autre (meilleure) option consiste à enregistrer org.hibernate.type.PostgresUUIDType en tant que mappage de type Hibernate par défaut pour tous les attributs exposés en tant que Java.util.UUID. Ceci est couvert dans la documentation @ http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch06.html#types-registry

32
Steve Ebersole

JPA 2.1 offre un moyen très simple d'utiliser le type de colonne PostgreSQL uuid et Java.util.UUID comme type du champ d'entité correspondant:

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

}

Ajoutez simplement cette classe à votre configuration de persistance et annotez les champs UUID avec @Column(columnDefinition="uuid").

15
Oleg Estekhin

Pour que cela fonctionne avec Hibernate 5.1.x, vous pouvez suivre Steve Ebersole commentez ici

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

La version plus récente >= 8.4-701 du pilote JDBC Postgresql gère correctement le mappage Java.util.UUID. Et ainsi fait Hibernate >= 4.3.x.

Voir les détails sur 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.
2
gavenkoa

La solution suggérée par Oleg ne fonctionnait pas parfaitement pour moi (elle échouait si vous tentiez de conserver une valeur nulle). Voici une solution modifiée qui fonctionne également pour les valeurs nulles.

Dans mon cas, j'utilise EclipseLink, donc si vous utilisez Hibernate, vous n'en aurez peut-être pas besoin.

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;
    }
}
1
Ixim

Ajoute ce drapeau sur la connexion postgresql url:? Stringtype = non spécifié

comme ça

jdbc: postgresql: // localhost: 5432/yourdatabase? stringtype = non spécifié

0
user8157076