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

标签 sql go protocol-buffers grpc

谁能解释一下如何正确使用Scan()Value()在下面的例子中?

我正在尝试使用以下示例:

  • https://github.com/jinzhu/gorm/issues/2017#issuecomment-537627961
  • https://github.com/travisjeffery/proto-go-sql/blob/master/_example/person_sql.go

  • 我的原型(prototype):
    message Timestamp {
      google.protobuf.Timestamp timestamp = 1;
    }
    message User {
      uint32 ID = 1;
      Timestamp createdAt = 2;
    }
    

    代码(需要修复 time.Now() ):
    package v1
    
    import (
            "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)
    }
    

    最佳答案

    扫描器和评估器接口(interface)并不是您自己会真正使用的东西,至少在将自定义类型存储在数据库中时不会。我将首先介绍 Scan() 的使用。和 Value()功能,然后我会解决你的问题。

    当您收到 sql.Row结果,并希望将结果集中的值分配(扫描)到自定义类型的变量中。文档显示 sql.Row.Scan()函数接受 0 个或多个 interface{} 类型的参数,基本上什么都有。 (check docs here)。

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

    any type implementing Scanner (see Scanner docs)



    使用功能func (ts *Timestamp) Scan(value interface{}) error { , Timestamp类型现在实现 Scanner接口(interface),从而允许 sql.Row为这种类型赋值。 Scanner 的文档界面位于 Scan() 文档的正下方,我在上面链接。

    当然,这有助于您从数据库中读取值,但在存储这些类型时却一无所获。为此,您需要 Valuer界面。如果您还没有猜到,func (ts Timestamp) Value() (driver.Value, error)功能确实使您的Timestamp type 实现了这个接口(interface)。 driver.Valuer 的文档接口(interface)可以找到here ,一直在底部。
    Valuer的要点|接口(interface)是允许将任何类型转换为 driver.Value 的方法。 ,驱动程序可以使用并存储在数据库中(再次:docs here)。

    修复问题

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

    违规行确实是您标记的行:
    u.CreatedAt = time.Now()
    

    首先,User.CreatedAtTimestamp 类型,它本身就是一条包含单个时间戳的消息。设置CreatedAt时间到time.Now() , 你需要初始化 CreatedAt正确字段:
    u.CreatedAt = &Timestamp{
        Timestamp: ptypes.TimestampNow(), // this returns a PROTOBUF TYPE!
    }
    

    您在 Scan 中执行此操作和 Value功能已经,所以我真的不明白你为什么不在这里做......

    建议

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

    关于sql - 使用 SQL 和 gRPC 时如何使用 Scan 和 Value?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59079565/

    相关文章:

    sql - 如何将ORM迁移文件压缩为一个

    mysql - 动态查询MySQL

    sql - 从一列嵌套表中选择 SQL

    java - Protocol Buffer Java,序列化具有字段的枚举

    java - 解决由于 C++ 导致的 Google protobuf 中枚举字段命名限制的解决方案

    mysql - 有没有办法获取变量并将它们添加为 SQL 中左连接后的列?

    go - 将 firestore "integer_value"转换为整数

    go - 如何按顺序将字符串输入输入 bufio.Scanner 和 fmt.Scanln?

    testing - echo c.Get ("user") 在测试环境中不起作用

    c++ - 如何将必需的 protobuf 字段设置为其默认值?