将枚举值保存到数据库

Posted

技术标签:

【中文标题】将枚举值保存到数据库【英文标题】:Saving enumerated values to a database 【发布时间】:2014-08-07 12:08:01 【问题描述】:

我是 Go 新手,我正在尝试编写一个小程序来中。 我声明我的价值观的方式如下:

type FileType int64
const (
    movie FileType = iota
    music
    book
    etc
)

我在结构中使用这些值,如下所示:

type File struct 
    Name     string
    Type     FileType
    Size     int64

我将 gorp 用于我的数据库内容,但我想 gorp 的使用与我的问题无关。我把东西放在我的数据库中是这样的:

dbmap.Insert(&File"MyBook.pdf",movie,1000)

但是当我尝试检索东西时……

dbmap.Select(&dbFiles, "select * from Files")

我收到以下错误:

panic: reflect.Set: value of type int64 is not assignable to type main.FileType

当我使用 int64 作为 const(...)File.Type 字段的类型时,一切正常,但我是 Go 新手,想了解问题所在。 在我看来,我有两个问题:

    为什么 Go 不能成功转换这些东西?我查看了 Go 反射和 sql 包的源代码,并且有这种转换的方法,但它们似乎失败了。这是一个错误吗?有什么问题?

    我发现,可以通过实现以下方法来实现sql.Scanner接口:

    Scan(src interface) error
    

    我尝试实现该方法,我什至能够从src 获得正确的值并将其转换为FileType,但我很困惑是否应该为“(f *FileType) 或@987654334 实现该方法@. 无论哪种方式,方法都会被调用,但是我无法覆盖 f(或者至少更新稍后会丢失),并且从数据库读取的 File 实例始终具有“0”作为 @ 的值987654337@.

你对这两点有什么想法吗?

【问题讨论】:

你真的不应该使用 Go 范围之外的 iota 值,例如数据库。如果您曾经对常量重新排序,或者在中间添加一个新常量,iota 值将发生变化,与数据库中的现有记录不匹配。 【参考方案1】:

我最近也有同样的需求,解决方法是实现两个接口:

    sql/driver.Valuer sql.Scanner

这是一个工作示例:

type FileType int64

func (u *FileType) Scan(value interface) error  *u = FileType(value.(int64)); return nil 
func (u FileType) Value() (driver.Value, error)   return int64(u), nil 

【讨论】:

这是正确的答案,应该被接受。干得好。 出于好奇,我完全是新手。有充分的理由将其设为 int64 吗?似乎您只需要一个 int16 甚至 int8,因为您可能不会有数十亿个可能的值。 @bigblind 这是原始问题使用的内容。我通常不会开始过多担心值大小,直到它成为一个问题,或者问题空间需要它。【参考方案2】:

有点离题,但可能对其他人有用,因为在使用 golang 中的 postgres 枚举字段(以字节形式返回)时,我在解决类似问题时不断重新审视这个问题/答案。

 // Status values
 const ( 
     incomplete Status = "incomplete"
     complete   Status = "complete" 
     reject     Status = "reject"
 )

 type Status string

 func (s *Status) Scan(value interface) error 
     asBytes, ok := value.([]byte)
     if !ok 
         return errors.New("Scan source is not []byte")
     
     *s = Status(string(asBytes))
     return nil
 

 func (s SubjectStatus) Value() (driver.Value, error) 
     // validation would go here
     return string(s), nil
 

【讨论】:

Go 没有enums。您的常量是string 类型。如果您希望它们的类型为 Status(您可以),那么您应该使用 Incomplete Status = "incomplete"Complete Status = "complete" 等(此外,全大写标识符不是惯用的 Go)。 @DaveC 谢谢,更新了!为了清楚起见,我指的是 postgres 的实际 enum 列类型,但很好的更正。【参考方案3】:
    Go 需要特定类型,这有时会很痛苦。 对于“原生”类型,(f FileType)(f *FileType) 便宜,除非你有一个复杂的类型,否则最好不要使用指针。 什么意思它不会覆盖它?修改后是否重新保存了结构?

【讨论】:

关于 2:好的,但是当我在 Scan 方法中编写类似“f = book”的内容时,如何更改“f”以防“(f FileType)”,它没有效果。 您只能修改您的值,如果它在指针接收器上运行。看看stdlib中sql.NullInt64的实现。

以上是关于将枚举值保存到数据库的主要内容,如果未能解决你的问题,请参考以下文章

将枚举值保存到字典中

如何在数据库室中保存枚举字段?

在核心数据中保存没有原始值的快速枚举

hibernate的枚举注解@Enumerated

10. 结构和枚举

@Enumerated-转