Hibernate的UUID与PostgreSQL和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查看它们时)是不同的,这至少是令人困惑的。

这可以在SQL Server中通过将列的类型更改为uuid-char来弥补

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

然而,它不再适用于PostgreSQL,因为在Postgres中没有从varchar到uuid的隐式映射。

有人建议改变发生器来生成指导。 这在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以外的任何其他环境中都会失败。

所以,你必须走很长的路:

  • 在重写的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)" );
            }
         }
    
  • 创建一个包装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;
            }
        }
    }
    
  • 将自定义类型注册到Hibernate将会拾取的位置(我为所有实体都有一个公共基类)。 我使用defaultForType = UUID.class注册它,以便所有的UUID使用它,这意味着我根本不需要注释UUID属性。
  • 
        @TypeDefs( {
                @TypeDef( name = "uuid-custom", typeClass = UUIDCustomType.class, defaultForType = UUID.class )
        } )
        public class BaseEntityWithId { 
    

    警告:实际上没有用postgres进行测试,但对于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 );
        }
    }
    

    我不认为这是最优雅的解决方案,但它现在起作用。

    链接地址: http://www.djcxy.com/p/91453.html

    上一篇: Hibernate UUID with PostgreSQL and SQL Server

    下一篇: Is UUID really unique?