Hibernate UUID with PostgreSQL and SQL Server
我有一个要在PostgreSQL和SQL Server上运行的应用程序。我想使用java.util.uuid作为ID。
我在SQL Server中将列定义为
1 | id UNIQUEIDENTIFIER ROWGUIDCOL NOT NULL UNIQUE |
我在PostgreSQL中将我的列定义为
1 | id UUID NOT NULL |
列在我的JPA实体中定义为
1 2 3 4 5 | @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中纠正此问题。
1 2 3 4 5 6 | @Id @Type(type ="uuid-char") @Column(name ="id") public UUID getId() { return id; } |
然而,由于postgres中没有从varchar到uuid的隐式映射,所以它不再在postgresql中工作。
有些人建议更改生成器以生成guid。这在Postgres中不起作用,因为Postgresql94方言中不支持这一点。
对于这两个数据库来说,最优雅的解决方案是什么?我在考虑为sqlserver创建自己的方言,用一个从uuid到binary的自定义转换,但我不确定这是怎么做的。
我有类似的要求,但我也希望使用HibernateDDL生成,并确保SQL Server生成了唯一标识符类型,还希望支持hsqldb进行单元测试。要在SQL Server中使用UUID,您肯定希望遍历字符串而不是二进制字符串,因为SQL Server中的二进制表示是针对不同于UUID的GUID的。例如,在Java Hibernate和SQL Server中UUID的不同表示
您可以创建正确的映射,并使用以下命令为SQL Server生成正确的SQL:
1 2 3 | @Type(type ="uuid-char") @Column(columnDefinition="uniqueidentifier") public UUID getUuid() |
但不幸的是,在Hibernate中,无法为不同的数据库使用不同的列定义,所以这在除SQL Server之外的任何数据库上都将失败。
所以,你必须走很远的路。这一切都是为了休眠4.3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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)" ); } } |
databasetype是一个枚举,我已经根据系统配置设置了这个枚举,使用方言类或字符串或其他任何东西修改它来品尝它。
这是在https://zorq.net/b/2012/04/21/switching-hibernates-uuid-type-mapping-per-database中描述的变化。/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | 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 2 3 4 | @TypeDefs( { @TypeDef( name ="uuid-custom", typeClass = UUIDCustomType.class, defaultForType = UUID.class ) } ) public class BaseEntityWithId { |
警告:实际上不是用Postgres测试的,但是在Hibernate4.3上对hsqldb和SQL Server非常有用。
最后,我编写了自己的方言和Basic类型,将Java类型java. UTIL uuID默认链接到uuID char。
这门课的灵感来源于PostgresqlDialect
1 2 3 4 5 6 7 8 | public class MySQLServerDialect extends SQLServer2012Dialect { public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { super.contributeTypes( typeContributions, serviceRegistry ); typeContributions.contributeType( SQLServerUUIDType.INSTANCE ); } } |
这个类的灵感来自于UuidCharType
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 ); } } |
我不认为这是最优雅的解决方案,但它目前有效。