使用PostgreSQL和SQL Server Hibernate UUID

时间:2017-03-02 15:50:33

标签: java sql-server postgresql 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之前将其转换为二进制形式。 Unfortunately the binary format is slightly different,导致GUID的字符串表示(例如,当使用SSMS查看它们时)不同,这至少令人困惑。

可以通过将列的类型更改为uuid-char

来在SQL Server中解决此问题
@Id
@Type(type = "uuid-char")
@Column(name = "id")
public UUID getId() {
    return id;
}

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

Some people suggest生成器中的更改以生成guid。这在Postgres中不起作用,因为在PostgreSQL94Dialect中不支持它。

为两个数据库创建这个词最优雅的解决方案是什么?我正在考虑使用从UUID到二进制的自定义转换来创建我自己的SQLServer方言,但我不确定是否可行。

2 个答案:

答案 0 :(得分:3)

我有类似的要求,但我也想使用Hibernate DDL生成并确保SQL Server生成了uniqueidentifier类型,并且还希望支持hsqldb进行单元测试。 要在SQL Server中使用UUID,您肯定希望通过字符串而非二进制,因为SQL Server中的二进制表示是针对与UUID不同的GUID。请参阅示例Different representation of UUID in Java Hibernate and SQL Server

您可以使用以下命令创建正确的映射并为SQL Server生成正确的sql:

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

这只是按原样运行,但不幸的是,Hibernate没有办法为不同的数据库使用不同的columnDefinitions,因此除了SQL Server之外,这将失败。

所以,你必须走很远的路。这完全适用于Hibernate 4.3:

  1. 在重写的SQLServerDialect中注册新的GUID sql类型,并使用此方言而不是基础方法
  2. 
        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应用程序启动时称之为。
    2. 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属性。
      2. 
            @TypeDefs( {
                    @TypeDef( name = "uuid-custom", typeClass = UUIDCustomType.class, defaultForType = UUID.class )
            } )
            public class BaseEntityWithId { 
        

        警告:实际上并没有使用postgres进行测试,但是对于hibernate 4.3上的hsqldb和sql server非常有用。

答案 1 :(得分:0)

我最终编写了自己的Dialect和BasicType,默认情况下将Java类型java.util.UUID链接到uuid-char。

本课程的灵感来自PostgreSQLDialect

function createLabelFromTimestamp(timestamp) {
    var hms = timestamp % (1800000 * 48);
    var period = hms / 1800000;

    // if 0-th period, show date instead
    if(period == 0) {
        var date = new Date(timestamp);
        return date.toDateString();
    }
    // otherwise show period number
    else {
        return period;
    }
}

本课程的灵感来自UUIDCharType

public class MySQLServerDialect extends SQLServer2012Dialect {

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

        typeContributions.contributeType( SQLServerUUIDType.INSTANCE );
    }
}

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