是否需要同时实现 Scanner 接口和 Valuer 接口

我正在处理一个要求,其中我收到一个 JSON 对象,其中包含一个字符串形式的日期值。我的任务是将 Date 对象存储在数据库中。


类似这样的事情:


{"start_date": "2019-05-29", "end_date": "2019-08-30"}

{"start_date": "2019-05-29", "end_date": null}

我已经实现了自定义Date类型


type Date struct {

    time.Time

}

我已经实现了 UnmarshalJSON 接口


func (d *Date) UnmarshalJSON(b []byte) (err error) {

    if b[0] == '"' && b[len(b)-1] == '"' {

        b = b[1 : len(b)-1]

    }


    // take care of null..

    if len(b) == 0 || string(b) == "null" {

        d.Time = time.Time{}

        return

    }


    d.Time, err = time.Parse("2006-01-02", string(b))

    return

}

还实现了 Valuer 接口以将 Value 返回给 sql.Driver。


func (d Date) Value() (driver.Value, error) {

    // check if the date was not set..

    if d.Time.IsZero() {

        return nil, nil

    }

    return d.Time.Format("2006-01-02"), nil

}

但由于某种原因,直到Date 实现 Scanner 接口,就像这样:


func (d Date) Scan(b interface{}) (err error) {

    ...

    return

}

问题:

ORM 适配器 (GORM) 不会将记录存储在数据库中。有什么线索吗?

如果我运行上面的代码 2 两次,我会看到不同的行为,具体取决于 Scan() 函数是否存在。


一只萌萌小番薯
浏览 128回答 2
2回答

素胚勾勒不出你

上一篇:我跑了go get -u github.com/jinzhu/gorm。我将使用sqlite而不是pgsql.&nbsp;供参考,go version go1.12.7 linux/amd64您的注释未使用正确的语法。gorm-属性-值对的分隔符不是comma,而是semi-colon。https://github.com/jinzhu/gorm/blob/master/model_struct.go#L644的实现Date.Scan()必须使用指针接收器,否则您会丢失更新的值。https://github.com/jinzhu/gorm/blob/0fd395ab37aefd2d50854f0556a4311dccc6f45a/scaner_test.go#L57在我的测试期间,Scan方法收到一个time.Time,因此不需要nil大小写,也不string需要大小写(可能是数据库驱动程序的问题)。然而,更重要的是,我添加了一个default案例来捕获不受支持的类型。该Scan方法涉及将从数据库读取的数据分配给您提供的对象的过程。评论它与您在上次 sql 结果复制粘贴中演示的缺失日期问题无关。我认为这在某种程度上与我之前提到的问题有关。我听取了所有错误以确保一切按预期工作。这是我的测试代码,package mainimport (&nbsp; &nbsp; "database/sql/driver"&nbsp; &nbsp; "encoding/json"&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "log"&nbsp; &nbsp; "time"&nbsp; &nbsp; "github.com/jinzhu/gorm"&nbsp; &nbsp; _ "github.com/jinzhu/gorm/dialects/sqlite")var DB *gorm.DBtype CustomerBroker struct {&nbsp; &nbsp; gorm.Model&nbsp; &nbsp; StartDate Date `gorm:"type:date;column:start_date" json:"start_date"`&nbsp; &nbsp; EndDate&nbsp; &nbsp;Date `gorm:"type:date;column:end_date" json:"end_date"`}type Date struct {&nbsp; &nbsp; time.Time}func (d *Date) UnmarshalJSON(b []byte) (err error) {&nbsp; &nbsp; if b[0] == '"' && b[len(b)-1] == '"' {&nbsp; &nbsp; &nbsp; &nbsp; b = b[1 : len(b)-1]&nbsp; &nbsp; }&nbsp; &nbsp; // take care of null..&nbsp; &nbsp; if len(b) == 0 || string(b) == "null" {&nbsp; &nbsp; &nbsp; &nbsp; d.Time = time.Time{}&nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; }&nbsp; &nbsp; d.Time, err = time.Parse("2006-01-02", string(b))&nbsp; &nbsp; return}func (d *Date) Scan(b interface{}) (err error) {&nbsp; &nbsp; switch x := b.(type) {&nbsp; &nbsp; case time.Time:&nbsp; &nbsp; &nbsp; &nbsp; d.Time = x&nbsp; default:&nbsp; &nbsp; err = fmt.Errorf("unsupported scan type %T", b)&nbsp; &nbsp; }&nbsp; &nbsp; return}func (d Date) Value() (driver.Value, error) {&nbsp; &nbsp; // check if the date was not set..&nbsp; &nbsp; if d.Time.IsZero() {&nbsp; &nbsp; &nbsp; &nbsp; return nil, nil&nbsp; &nbsp; }&nbsp; &nbsp; return d.Time.Format("2006-01-02"), nil}func main() {&nbsp; &nbsp; var dberr error&nbsp; &nbsp; DB, dberr = gorm.Open("sqlite3", ":memory:")&nbsp; &nbsp; if dberr != nil {&nbsp; &nbsp; &nbsp; &nbsp; panic(dberr)&nbsp; &nbsp; }&nbsp; &nbsp; defer DB.Close()&nbsp; &nbsp; record := CustomerBroker{}&nbsp; &nbsp; errs := DB.CreateTable(record).GetErrors()&nbsp; &nbsp; fmt.Println("create error ", errs)&nbsp; &nbsp; data := []byte(`{"start_date": "2019-05-29", "end_date": null}`)&nbsp; &nbsp; err := json.Unmarshal(data, &record)&nbsp; &nbsp; fmt.Println("unmarshal error ", err)&nbsp; &nbsp; log.Printf("record start %v end %v\n", record.StartDate, record.EndDate)&nbsp; &nbsp; errs = DB.Create(&record).GetErrors()&nbsp; &nbsp; fmt.Println("insert error ", errs)&nbsp; &nbsp; all := []CustomerBroker{}&nbsp; &nbsp; errs = DB.Find(&all).GetErrors()&nbsp; &nbsp; fmt.Println("find error ", errs)&nbsp; &nbsp; log.Printf("records count %v\n", len(all))&nbsp; &nbsp; for _, a := range all {&nbsp; &nbsp; &nbsp; &nbsp; log.Printf("found start %v end %v\n", a.StartDate, a.EndDate)&nbsp; &nbsp; }}输出:$ go run main.go&nbsp;create error&nbsp; []unmarshal error&nbsp; <nil>2019/10/11 17:22:51 record start 2019-05-29 00:00:00 +0000 UTC end 0001-01-01 00:00:00 +0000 UTCinsert error&nbsp; []find error&nbsp; []2019/10/11 17:22:51 records count 12019/10/11 17:22:51 found start 2019-05-29 00:00:00 +0000 UTC end 0001-01-01 00:00:00 +0000 UTC如果您在运行此命令时发现不同的行为pqsql,请共享相应的 docker 映像,以便将来可以针对同一版本进行尝试。

慕容708150

启用DB.LogMode(true)显示这两个查询与扫描:[2019-10-16 14:03:34]  [0.97ms]  INSERT  INTO "custom_brokers" ("created_at","updated_at","deleted_at","start_date","end_date") VALUES ('2019-10-16 14:03:34','2019-10-16 14:03:34',NULL,'2019-05-29',NULL) RETURNING "custom_brokers"."id"无需扫描:[2019-10-16 14:02:53]  [0.76ms]  INSERT  INTO "custom_brokers" ("created_at","updated_at","deleted_at") VALUES ('2019-10-16 14:02:53','2019-10-16 14:02:53',NULL) RETURNING "custom_brokers"."id"第二个显示 gorm 完全忽略模型中的另一列(嵌入式 gorm 模型除外)
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go