如何防止 gorm 将自定义整数类型转换为字符串?

Posted

技术标签:

【中文标题】如何防止 gorm 将自定义整数类型转换为字符串?【英文标题】:How to prevent gorm from converting custom integer types into strings? 【发布时间】:2021-05-01 23:20:38 【问题描述】:

我在将 gorm 与具有整数属性的自定义类型一起使用时遇到了麻烦。 Gorm 倾向于为我将整数值转换为字符串。有两个不同的问题:

问题 1:

我有一个自定义类型定义如下:

type ClientType uint8

const (
    USER ClientType = iota
    SERVER
)

func (t ClientType) String() string 
    return [...]string"User", "Service"[t]

然后我有一个 Client 的 ORM 结构,定义如下:

type Client struct 
    ClientID uint64 `gorm:"primaryKey"`
    UserID uint64
    ClientType ClientType
    CreatedAt time.Time

调用db.Create(&client) 时,gorm 会自动调用ClientType 上的String() 方法,并导致mysql 中的数据类型不匹配,我打算将ClientType 存储在名为TINYINTTINYINT 列中。

问题 2:

所以我想如果我无法弄清楚如何覆盖String() 方法的自动调用,我只是将方法重命名为ToString() 并在需要时调用它。现在 gorm 不能再调用它,而是将整数值转换为数字字符串。所以USER,即0,现在将在生成的SQL语句中变为'0',而SERVER将变为'1',尽管MySQL能够将字符串转换回整数。

我仍然想知道我做错了什么让 gorm 认为我想要这些转换。

    有没有办法覆盖String()的调用,所以我仍然可以按常规命名方法? 是使用uint8 使gorm 将int 转换为字符串,因为使用uint64 的其他数值(ClientID 和UserID)不受此问题的影响?是不是某种数据库模式缓存让 gorm 记住了 client_type 曾经是 ENUM('User', 'Service') 列的旧模式?

【问题讨论】:

【参考方案1】:

根据 gorm 的自定义类型 Value and Scan 需要实现。

您也可以尝试在 struct 中指定类型(将 替换为列类型)。

type Client struct 
    ClientID   uint64 `gorm:"primaryKey"`
    UserID     uint8
    ClientType ClientType `gorm:"type:<TYPE>"`
    CreatedAt  time.Time

String() 似乎不是问题,因为它不会在创建期间调用,而仅在查找期间调用。

使用db.LogMode(true),它也在创建期间被调用,但这似乎是用于 sql 查询日志记录而不是发送到数据库。

关闭日志模式

    db.LogMode(false)
    fmt.Println("Creating")
    db.Create(&ClientClientID: 9, UserID: 8, ClientType: USER, CreatedAt: time.Now())
    fmt.Println("Created")

    fmt.Println("Finding")
    db.First(&client)
    fmt.Printf("Found %v\n", client)

输出

Creating
Created
Finding
String() called with 0
Found 9 8 User 2021-01-28 11:41:51.355633057 +0530 +0530

开启日志模式

    db.LogMode(true)
    fmt.Println("Creating")
    db.Create(&ClientClientID: 9, UserID: 8, ClientType: USER, CreatedAt: time.Now())
    fmt.Println("Created")

    fmt.Println("Finding")
    db.First(&client)
    fmt.Printf("Found %v\n", client)

输出:

Creating
String() called with 0
[2021-01-28 11:39:30]  [0.95ms]  INSERT INTO "clients" ("client_id","user_id","client_type","created_at") VALUES (9,8,'User','2021-01-28 11:39:30')  
[1 rows affected or returned ] 
Created
Finding
[2021-01-28 11:39:30]  [1.49ms]  SELECT * FROM "clients"   LIMIT 1  
[1 rows affected or returned ] 
String() called with 0
Found 9 8 User 2021-01-28 11:42:31.245628274 +0530 +0530

【讨论】:

在自定义类型上实现 Value() 方法似乎已经成功了。我基本上写了:func (t ClientType) Value() (driver.Value, error) return int64(t), nil 问题解决了。但我不明白为什么它似乎只接受 int64 作为转换类型。我尝试了 uint8、int 等,但没有任何效果,错误消息如下:sql: converting argument $2 type: non-Value type int returned from Value 另一件事是 String() 仍然会以某种方式被调用,并且在 gorm v1.20.11 中,没有更多的 LogMode() . Value 已接受指定为 int64、float64、bool、[]byte、string 和 time.Time 的类型。因此错误。 我尝试使用 gorm 1.9。看来是时候升级部门了 :)

以上是关于如何防止 gorm 将自定义整数类型转换为字符串?的主要内容,如果未能解决你的问题,请参考以下文章

无法将自定义数据类型转换为字符串?

如何将自定义类型转换为原始类型?

如何将自定义属性转换为 Json 字符串

如何将自定义 json 转换为自适应卡片 json 格式

如何支持 any_cast 将自定义类转换为字符串?

将自定义类型转换为类型 [][]float64