为了账号安全,请及时绑定邮箱和手机立即绑定

使用 SQL 和 gRPC 时如何使用 Scan 和 Value?

使用 SQL 和 gRPC 时如何使用 Scan 和 Value?

Go
FFIVE 2023-08-07 15:01:50
有人可以解释如何在下面的示例中正确使用Scan()and吗?Value()我正在尝试使用以下示例:https://github.com/jinzhu/gorm/issues/2017#issuecomment-537627961https://github.com/travisjeffery/proto-go-sql/blob/master/_example/person_sql.go我的原型:message Timestamp {  google.protobuf.Timestamp timestamp = 1;}message User {  uint32 ID = 1;  Timestamp createdAt = 2;}代码(需要修复time.Now()):package v1import (        "database/sql/driver"        "fmt"        "time"        "github.com/golang/protobuf/ptypes")func (u *User) Create() {  u.CreatedAt = time.Now() // FIXME: ERROR. How do I make use of Scan() and Value() here?  // saving to SQL database}func (ts *Timestamp) Scan(value interface{}) error {    switch t := value.(type) {    case time.Time:            var err error            ts.Timestamp, err = ptypes.TimestampProto(t)            if err != nil {                    return err            }    default:            return fmt.Errorf("Not a protobuf Timestamp")    }    return nil}func (ts Timestamp) Value() (driver.Value, error) {        return ptypes.Timestamp(ts.Timestamp)}
查看完整描述

1 回答

?
智慧大石

TA贡献1946条经验 获得超3个赞

扫描器和评估器接口实际上并不是您自己使用的东西,至少在数据库中存储自定义类型时不是这样。Scan()我将首先介绍和函数的使用Value(),然后解决您的问题。

当您获得sql.Row结果并想要将结果集中的值分配(扫描)到自定义类型的变量中时。文档显示该sql.Row.Scan()函数接受 0 个或多个类型的参数interface{},基本上是任何东西。

在可以扫描值的支持类型列表中,最后一行是重要的一行:

任何实现扫描仪的类型(请参阅扫描仪文档)

通过 function func (ts *Timestamp) Scan(value interface{}) error {,该Timestamp类型现在实现了该Scanner接口,从而允许sql.Row向该类型分配值。该Scanner接口的文档位于Scan()我上面链接的 文档的正下方。

当然,这可以帮助您从数据库读取值,但在存储这些类型时却无济于事。为此,您需要Valuer接口。如果您还没有猜到,该func (ts Timestamp) Value() (driver.Value, error)函数确实使您的Timestamp类型实现了此接口。

Valuer接口的要点是允许一种将任何类型转换为 a 的方法driver.Value,驱动程序可以使用该类型并将其存储在数据库中


解决问题

首先,我必须假设您的协议输出已写入包中v1。如果不是,它对你来说不会有很好的效果。

有问题的行确实是您标记的行:

u.CreatedAt = time.Now()

首先,User.CreatedAt类型为Timestamp,它本身就是一条包含单个时间戳的消息。要将CreatedAt时间设置为time.Now(),您需要CreatedAt正确初始化该字段:

u.CreatedAt = &Timestamp{
    Timestamp: ptypes.TimestampNow(), // this returns a PROTOBUF TYPE!
    }

已经Scan在你的函数中这样做了Value ,所以我真的不明白你为什么不在这里这样做......


建议

如果协议输出确实写入了v1包,我真的真的会删除该User.Create()函数。事实上,我会直接杀死它。您的协议缓冲区用于通信。通过 RPC 公开您的程序。这是一个 API。这些message类型本质上是请求和响应对象(如果您愿意的话,可以美化 DTO)。您正在Create向其中添加此功能,这会将它们变成 AR 类型。它使您的 protobuf 包无法使用。gRPC 的美妙之处在于您可以生成 golang、C++、Python...代码,其他人可以使用这些代码来调用您的程序。如果你让你的 gRPC 包依赖于数据库,就像你所做的那样,我个人永远不会使用它。


查看完整回答
反对 回复 2023-08-07
  • 1 回答
  • 0 关注
  • 97 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信