dongpaocuan7498 2016-09-07 22:27
浏览 273
已采纳

golang-将枚举类型保存到SQL数据库中“紧急:sql:转换Exec参数1的类型:从Value返回的非Value类型的int”

In my current go project (~5K LOC), I am using sqlite3 as my underlying database layer, and I am using gorm as my ORM engine. One of the models is a Platform with a field of PlatformType enum type. Here's a code snippet to demonstrate my problem.

package main

import (
    _ "github.com/jinzhu/gorm/dialects/sqlite"
    "github.com/jinzhu/gorm"
    "database/sql/driver"
    "fmt"
)

/****************************\
    Object Layer
\****************************/

// Platform ID 
type PlatformID string

func (u *PlatformID) Scan(value interface{}) error { *u = PlatformID(value.([]byte)); return nil }
func (u PlatformID) Value() (driver.Value, error)  { return string(u), nil }

// Platform Type enumeration
type PlatformType int
const (
    PLATFORM_TYPE_NOT_A_VALUE PlatformType = iota
    PLATFORM_TYPE_TYPE1
    PLATFORM_TYPE_TYPE2 
)

var types = [...]string {
    "Not a type",
    "Type1",
    "Type2",
}

func (platform_type PlatformType) String() string {
    return types[platform_type]
}

func (u *PlatformType) Scan(value interface{}) error { *u = PlatformType(value.(int)); return nil }
func (u PlatformType) Value() (driver.Value, error)  { return int(u), nil }

// Platform descriptor.
type Platform struct {
    ID              PlatformID          `json:"ID" gorm:"type:varchar(100);unique;not null"`                // Assigned by LCBO.
    Type            PlatformType        `json:"Type" gorm:"type:integer"`   
}

type PlatformStore interface {
    Init() error
    Save(platform *Platform) error
}

/****************************\
    Persist Layer
\****************************/
func NewSqlite3Store(dbname string) *gorm.DB {
    db, err := gorm.Open("sqlite3", dbname)
    if err != nil {
        panic("failed to connect database")
    }
    return db
}

type DBPlatformStore struct {
    db *gorm.DB
}

func NewDBPlatformStore(db *gorm.DB) PlatformStore {
    return &DBPlatformStore{
        db: db,
    }
}

func (store *DBPlatformStore) Init() error {
    err := store.db.AutoMigrate(&Platform{}).Error
    if err != nil {
        panic(err)
    }
    return err
}

func (store *DBPlatformStore) Save(platform *Platform) error {
    err := store.db.Create(platform).Error
    if err != nil {
        panic(err)
    }
    return err
}

/****************************\
    Application Layer
\****************************/
func main() {
    db := NewSqlite3Store("enum_test.db")
    platformStore := NewDBPlatformStore(db)

    fmt.Println("Initialize Database")
    err := platformStore.Init()
    if err != nil {
        panic(err)
    }

    platform := new(Platform)
    platform.ID = "12345"
    platform.Type = PLATFORM_TYPE_TYPE1

    platformStore.Save(platform)
}

After running the code above, I got a runtime error "sql: converting Exec argument #1's type: non-Value type int returned from Value"

]# go run enumtest.go
Initialize Database
panic: sql: converting Exec argument #1's type: non-Value type int returned from Value

goroutine 1 [running]:
panic(0x66d380, 0xc8203ae350)
        /*/panic.go:481 +0x3e6
main.(*DBPlatformStore).Save(0xc820020b20, 0xc820304500, 0x0, 0x0)
        /*/enumtest.go:84 +0x9f
main.main()
        /*/enumtest.go:106 +0x247
exit status 2

And I checked my database, the platforms table has been created successfully.

]# sqlite3 enum_test.db
sqlite> .schema platforms
CREATE TABLE "platforms" ("id" varchar(100) NOT NULL UNIQUE,"type" integer , PRIMARY KEY ("id"));

The (not-so) trivial question is how do I modify my code so that I can correctly save the entry to database.

My bigger question is: How to save a customized GO enum type to a sql database?(with a ORM engine hopefully)

  • 写回答

1条回答 默认 最新

  • doumaoao0182 2016-09-08 16:29
    关注

    According to current database/sql docs, the sql has four builtin functions that returns driver.Value, and the underlying types are int64, float64, string and bool. So I guess that's the only four types supported.

    I just changed the underlying type of my enum from int to int64 and things are working.

    The problematic section is updated to the following snippet:

    // Platform Type enumeration
    type PlatformType int64
    const (
        PLATFORM_TYPE_NOT_A_VALUE PlatformType = iota
        PLATFORM_TYPE_TYPE1
        PLATFORM_TYPE_TYPE2 
    )
    
    var types = [...]string {
        "Not a type",
        "Type1",
        "Type2",
    }
    
    func (platform_type PlatformType) String() string {
        return types[platform_type]
    }
    
    func (u *PlatformType) Scan(value interface{}) error { *u = PlatformType(value.(int64)); return nil }
    func (u PlatformType) Value() (driver.Value, error)  { return int64(u), nil }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 DIFY API Endpoint 问题。
  • ¥20 sub地址DHCP问题
  • ¥15 delta降尺度计算的一些细节,有偿
  • ¥15 Arduino红外遥控代码有问题
  • ¥15 数值计算离散正交多项式
  • ¥30 数值计算均差系数编程
  • ¥15 redis-full-check比较 两个集群的数据出错
  • ¥15 Matlab编程问题
  • ¥15 训练的多模态特征融合模型准确度很低怎么办
  • ¥15 kylin启动报错log4j类冲突