簡體   English   中英

使用PostgreSQL和SQL Server Hibernate UUID

[英]Hibernate UUID with PostgreSQL and SQL Server

我有一個應用程序,我想在PostgreSQL和SQL Server上運行。 我想使用java.util.UUID作為ID。

我已經在SQL Server中定義了我的列

id  UNIQUEIDENTIFIER ROWGUIDCOL NOT NULL UNIQUE

我已經在PostgreSQL中定義了我的列

id  UUID NOT NULL

列在我的JPA實體中定義為

@Id
@Column(name = "id")
public UUID getId() {
    return id;
}

這適用於PostgreSQL,因為它將UUID傳遞給PostgreSQL JDBC驅動程序。 這種方式適用於SQL Server,因為Hibernate在將UUID發送到SQL Server之前將其轉換為二進制形式。 不幸的是,二進制格式略有不同 ,導致GUID的字符串表示(例如,當使用SSMS查看它們時)不同,這至少令人困惑。

通過將列的類型更改為uuid-char,可以在SQL Server中解決此問題

@Id
@Type(type = "uuid-char")
@Column(name = "id")
public UUID getId() {
    return id;
}

然而它在PostgreSQL中不再起作用,因為在Postgres中沒有從varchar到uuid的隱式映射。

有些人建議改變發電機以產生guid。 這在Postgres中不起作用,因為在PostgreSQL94Dialect中不支持它。

為這兩個數據庫制作這個詞最優雅的解決方案是什么? 我正在考慮使用從UUID到二進制的自定義轉換來創建我自己的SQLServer方言,但我不確定是否可行。

我有類似的要求,但我也想使用Hibernate DDL生成並確保SQL Server生成了uniqueidentifier類型,並且還希望支持hsqldb進行單元測試。 要在SQL Server中使用UUID,您肯定希望通過字符串而非二進制,因為SQL Server中的二進制表示是針對與UUID不同的GUID。 請參閱Java Hibernate和SQL Server中的UUID的不同表示形式

您可以使用以下命令創建正確的映射並為SQL Server生成正確的sql:

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

這只是按原樣運行,但不幸的是,Hibernate沒有辦法為不同的數據庫設置不同的columnDefinitions,因此除了SQL Server之外,這將失敗。

所以,你必須走很遠的路。 這就是Hibernate 4.3的全部內容:

  1. 在重寫的SQLServerDialect中注冊新的GUID sql類型,並使用此方言而不是基礎方言

    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. 創建一個包裝器UUIDCustomType,其行為類似於內置的UUIDCharType,但是根據數據庫類型進行委托。 您需要 Hibernate配置之前調用init(databaseType)。 也許自定義方言可以做到,但我在我的Spring應用程序啟動時稱之為。

DatabaseType是我已經根據系統配置設置的枚舉,使用方言類或字符串或其他來修改它。

這是對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. 在Hibernate將獲取的位置注冊自定義類型(我有一個適用於所有實體的公共基類)。 我使用defaultForType = UUID.class注冊它,以便所有UUID都使用它,這意味着我根本不需要注釋UUID屬性。

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

警告:實際上並沒有使用postgres進行測試,但是對於Hibernate 4.3上的hsqldb和sql server非常有用。

我最后編寫了自己的Dialect和BasicType,默認情況下將Java類型java.util.UUID鏈接到uuid-char。

本課程的靈感來自PostgreSQLDialect

public class MySQLServerDialect extends SQLServer2012Dialect {

    public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
        super.contributeTypes( typeContributions, serviceRegistry );

        typeContributions.contributeType( SQLServerUUIDType.INSTANCE );
    }
}

本課程的靈感來自UUIDCharType

public class SQLServerUUIDType extends AbstractSingleColumnStandardBasicType<UUID> implements LiteralType<UUID> {
    public static final SQLServerUUIDType INSTANCE = new SQLServerUUIDType();

    public SQLServerUUIDType() {
        super( VarcharTypeDescriptor.INSTANCE, UUIDTypeDescriptor.INSTANCE );
    }

    @Override
    protected boolean registerUnderJavaType() {
        return true;
    }

    public String getName() {
        return "my-uuid";
    }

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

我不認為這是最優雅的解決方案,但它現在有效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM