PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Sospensione UUID con PostgreSQL e SQL Server

Avevo requisiti simili, ma volevo anche utilizzare la generazione di DDL Hibernate e assicurarmi che SQL Server generasse un tipo di identificatore univoco e volevo anche supportare hsqldb per i test di unità. Per utilizzare gli UUID in SQL Server devi assolutamente passare attraverso stringhe non binarie , poiché la rappresentazione binaria in SQL Server è per un GUID diverso da un UUID. Vedi ad esempio Rappresentazione diversa di UUID in Java Hibernate e SQL Server

È possibile creare la mappatura corretta e generare l'SQL corretto per SQL Server con:

@Type(type = "uuid-char")
@Column(columnDefinition="uniqueidentifier")
public UUID getUuid()

E funziona così com'è, ma sfortunatamente non c'è modo in Hibernate di avere definizioni di colonna diverse per database diversi, quindi questo fallirà su qualcosa di diverso da SQL Server.

Quindi, devi fare molta strada. Questo è tutto per Hibernate 4.3:

  1. Registra un nuovo tipo di GUID sql in un SQLServerDialect sovrascritto e usa questo dialetto invece di quello di base

    public class SQLServer2008UnicodeDialect extends SQLServer2008Dialect
    {
        public SQLServer2008UnicodeDialect() 
        {
            // the const is from the MS JDBC driver, the value is -145
            registerColumnType( microsoft.sql.Types.GUID, "uniqueidentifier" ); 

            // etc. Bonus hint: I also remap all the varchar types to nvarchar while I'm at it, like so:
            registerColumnType( Types.CLOB, "nvarchar(MAX)" );
            registerColumnType( Types.LONGVARCHAR, "nvarchar(MAX)" );
            registerColumnType( Types.LONGNVARCHAR, "nvarchar(MAX)" );
            registerColumnType( Types.VARCHAR, "nvarchar(MAX)" );
            registerColumnType( Types.VARCHAR, 8000, "nvarchar($l)" );
        }
     }
  1. Crea un wrapper UUIDCustomType che si comporti in modo simile all'UUIDCharType integrato ma deleghi a seconda del tipo di database. Devi chiamare init(databaseType) prima Configurazione ibernazione. Forse il dialetto personalizzato potrebbe farlo, ma lo chiamo nell'avvio della mia app di primavera.

DatabaseType era un enum che avevo già impostato in base alla configurazione di sistema, modificarlo a piacere usando una classe dialettale o una stringa o altro.

Questa è una variazione di quanto descritto in https://zorq.net/b/2012/04/21/switching-hibernates-uuid-type-mapping-per-database/

public enum DatabaseType
{
    hsqldb,
    sqlserver,
    mysql,
    postgres
}

public class UUIDCustomType extends AbstractSingleColumnStandardBasicType<UUID> implements LiteralType<UUID>
{
    private static final long serialVersionUID = 1L;

    private static SqlTypeDescriptor SQL_DESCRIPTOR;
    private static JavaTypeDescriptor<UUID> TYPE_DESCRIPTOR;

    public static void init( DatabaseType databaseType )
    {
        if ( databaseType == DatabaseType.sqlserver )
        {
            SQL_DESCRIPTOR = SqlServerUUIDTypeDescriptor.INSTANCE;
        }
        else if ( databaseType == DatabaseType.postgres  )
        {
            SQL_DESCRIPTOR = PostgresUUIDType.PostgresUUIDSqlTypeDescriptor.INSTANCE;
        }
        else
        {
            SQL_DESCRIPTOR = VarcharTypeDescriptor.INSTANCE;
        }

        TYPE_DESCRIPTOR = UUIDTypeDescriptor.INSTANCE;
    }

    public UUIDCustomType()
    {
        super( SQL_DESCRIPTOR, TYPE_DESCRIPTOR );
    }

    @Override
    public String getName()
    {
        return "uuid-custom";
    }

    @Override
    public String objectToSQLString( UUID value, Dialect dialect ) throws Exception
    {
        return StringType.INSTANCE.objectToSQLString( value.toString(), dialect );
    }

    public static class SqlServerUUIDTypeDescriptor extends VarcharTypeDescriptor
    {
        private static final long serialVersionUID = 1L;

        public static final SqlServerUUIDTypeDescriptor INSTANCE = new SqlServerUUIDTypeDescriptor();

        public SqlServerUUIDTypeDescriptor()
        {
        }

        @Override
        public int getSqlType()
        {
            return microsoft.sql.Types.GUID;
        }
    }
}
  1. Registra il tipo personalizzato in una posizione che Hibernate rileverà (ho una classe base comune per tutte le entità). Lo registro usando defaultForType =UUID.class in modo che tutti gli UUID lo utilizzino, il che significa che non ho bisogno di annotare le proprietà dell'UUID.

    @TypeDefs( {
            @TypeDef( name = "uuid-custom", typeClass = UUIDCustomType.class, defaultForType = UUID.class )
    } )
    public class BaseEntityWithId { 

Avvertenza:non effettivamente testato con Postgres, ma funziona benissimo per hsqldb e sql server su Hibernate 4.3.