使用PostgreSQL和SQL Server Hibernate UUID

我有一个应用程序,我想在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之前将其转换为二进制forms。 不幸的是,二进制格式略有不同 ,导致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进行unit testing。 要在SQL Server中使用UUID,您肯定希望通过字符串而不是二进制,因为SQL Server中的二进制表示是针对与UUID不同的GUID。 请参阅Java Hibernate和SQL Server中的UUID的不同表示forms

您可以使用以下命令创建正确的映射并为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 implements LiteralType { private static final long serialVersionUID = 1L; private static SqlTypeDescriptor SQL_DESCRIPTOR; private static JavaTypeDescriptor 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 implements LiteralType { 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 ); } } 

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