grails 使用 uuid 作为 id 并映射到二进制列

Posted

技术标签:

【中文标题】grails 使用 uuid 作为 id 并映射到二进制列【英文标题】:grails using uuid as id and mapping to to binary column 【发布时间】:2011-07-04 23:51:51 【问题描述】:

我想为我的域对象使用 UUID 作为 id。这个想法是 uuid 可以由客户端提供,如果不是,将生成 UUID。我有这样的定义::

class Person 
        static mapping = 
        id generator:'assigned' 
        

        String id

        def getUUID  =
            return java.util.UUID.randomUUID().toString();
        


          transient beforeInsert = 
            if ( id == null || id.equals(""))
                      id = getUUID();
           

现在假设我去掉了包含在 java UUID 或客户端提供的 UUID 中的破折号,我希望将其存储在我的 mysql 数据库中的二进制字段中。并且在检索时也有正确的格式发回。

我怎样才能做到这一点?想到更好的方法来做到这一点?

【问题讨论】:

【参考方案1】:

Grails 和 hibernate 通常以字符串形式处理 UUID。使用二进制 UUID 需要做更多的工作。将id 声明为UUID 类型并提供休眠用户类型以将其序列化为字节数组。您还需要告诉 grails 用于 UUID 的 SQL 类型。例如:

class Person 
    static mapping = 
        id generator:'assigned', type: UUIDUserType, sqlType: 'varbinary(16)'
    

    UUID id

    def beforeInsert = 
        if (!id) 
            id = UUID.randomUUID()
        
    

UUID 的用户类型是:

import java.nio.ByteBuffer
import java.nio.LongBuffer
import java.sql.ResultSet
import java.sql.PreparedStatement
import java.sql.Types
import org.hibernate.usertype.UserType

public class UUIDUserType implements UserType 

    int[] sqlTypes()  [Types.VARBINARY] as int [] 
    Class returnedClass()  UUID 

    Object nullSafeGet(ResultSet resultSet, String[] names, owner) 
        byte[] value = resultSet.getBytes(names[0])
        return value ? bytesToUuid(value) : null
    

    void nullSafeSet(PreparedStatement statement, value, int index) 
        if (value == null) 
                statement.setNull(index, Types.VARBINARY)
         else 
                statement.setBytes(index, uuidToBytes(value))
        
    

    boolean equals(x, y)  x == y 
    int hashCode(x)  x.hashCode() 
    Object deepCopy(value)  value 
    boolean isMutable()  false 
    Serializable disassemble(value)  value 
    Object assemble(Serializable cached, owner)  cached 
    def replace(original, target, owner)  original 

    static byte[] uuidToBytes(uuid) 
        def bytes = new byte[16];
        ByteBuffer.wrap(bytes).asLongBuffer().with 
            put(0, uuid.mostSignificantBits)
            put(1, uuid.leastSignificantBits)
        
        bytes
    

    static UUID bytesToUuid(bytes) 
        ByteBuffer.wrap(bytes).asLongBuffer().with 
            new UUID(get(0), get(1))
        
    

【讨论】:

这在 MySQL 上不起作用:2011-02-27 10:18:33,818 [main] 错误 hbm2ddl.SchemaExport - 不成功:创建表人(id tinyblob 不为空,版本 bigint 不为空,主键(id)) 2011-02-27 10:18:33,819 [main] 错误 hbm2ddl.SchemaExport - BLOB/TEXT 列“id”在没有密钥长度的密钥规范中使用 2011-02-27 10:18:36,702 [main]错误 util.JDBCExceptionReporter - 表 'testuuid.person' 不存在 哎呀,你是对的。我假设 grails 会自动处理非字符串 UUID,但事实证明并非如此。我已经通过修复更新了答案。 啊是的自定义用户类型。我原以为 grails 或 hibernate 会为我们解决这个问题。总体而言,该解决方案有效..但是当使用 varbinary(16) 作为实体的主键时,grails/hibernate 的 belongsTo 关系存在问题。唯一的解决方案似乎是先手动创建数据库方案。

以上是关于grails 使用 uuid 作为 id 并映射到二进制列的主要内容,如果未能解决你的问题,请参考以下文章

GORM:使用字符串作为键的一对一表映射

Grails:相同的URL映射到不同HTTP方法的不同操作

如何将 Grails 域类映射到 DTO?

站点资源在 GetMapping 中泄漏到 UUID

我应该使用 UUID 还是常规 auto_increment 作为用户 ID?

Grails + RESTful URL 映射 + 过滤器 + 路由