自定义用于Go中结构体的扫描器

huangapple go评论68阅读模式
英文:

Custom scanner for structs in Go

问题

我正在使用Go编写一种类似于REST的API。我有一个遗留的数据库,所以我无法控制字段名、结构或其他任何内容。我正在一个名为datastore的单独包中抽象数据库访问方法。我的代码如下所示(省略了所有的错误处理等):

type Datastore struct {}

type Object struct {
  id uint
  name string
  ... 这里还有很多其他字段
}

func (Datastore) ObjectList() {
  var objects *[]Object
  db, _ := sqlx.Open("postgres", conn_info)
  rows, _ := sqlx.Queryx("SELECT * FROM object_table")
  defer rows.Close()
  for rows.Next() {
    var obj Object
    rows.Scan(&obj.id, &obj.name)
    objects = append(objects, obj)
  }
  return objects
}

我目前遇到的问题是对象表有很多字段,有些我关心,有些我不关心。有些字段与Object的字段同名,有些则不是。最终,我需要支持大部分字段,但我现在只是在进行概念验证。如果在行中发现的字段多于Scan()函数的参数数量,Scan代码会失败。我可以在查询中列出我要扫描的字段,例如select id, name from object_table,但是:

  1. 这会使代码非常丑陋(SQL语句无法正确格式化)
  2. 这会增加另一个需要编辑的地方,当我想要支持另一个字段时

有没有办法实现一个自定义的扫描器接口,它可以接收rows对象,将其中的一些数据加载到结构体中,并忽略其余的数据?

英文:

I am writing a sort of a REST-like API in Go. The database I have is legacy, so I don't control the field names or the structure or for that matter anything. I am abstracting database access methods in a separate package called datastore. The code I have looks like this (skipping all the error handling etc.)
<pre>
type Datastore struct {}

type Object struct {
id uint
name string
... zillion more fields here
}

func (Datastore) ObjectList() {
var objects *[]Object
db, _ := sqlx.Open("postgres", conn_info)
rows, _ := sqlx.Queryx("SELECT * FROM object_table")
defer rows.Close()
for rows.Next() {
var obj Object
rows.Scan(&obj.id, &obj.name)
objects = append(objects, obj)
}
return objects
}
</pre>

The problem I am currently having is that the object table has dozens and dozens of fields. Some I care about, but some I do not. Some are named the same as Object and some are not. Eventually, I will need to support most of them, but I am working on a proof of concept first. It seems that the Scan code fails if it finds more fields in the row than are present in the arguments for Scan(). I could list the fields I scan for in the query select id, name from object_table, but

  1. it makes the code extremely ugly (SQL doesn't get formatted right by gofmt)
  2. it adds another place I need to edit when I want to support another field

Is there any way to implement a custom scanner interface that would take the rows object, load some data out of it into a struct and ignore the rest?

答案1

得分: 0

你已经在使用sqlx,所以只需要在查询之前使用*DB.Unsafe():

var db *sqlx.DB

// 与内置的方式完全相同
db = sqlx.Open("sqlite3", ":memory:")
var p Person
udb := db.Unsafe()
err = udb.Get(&p, "SELECT * FROM person, place LIMIT 1;")
英文:

You are already using sqlx so just use *DB.Unsafe() before querying :

var db *sqlx.DB
 
// exactly the same as the built-in
db = sqlx.Open(&quot;sqlite3&quot;, &quot;:memory:&quot;)
var p Person
udb := db.Unsafe()
err = udb.Get(&amp;p, &quot;SELECT * FROM person, place LIMIT 1;&quot;)

huangapple
  • 本文由 发表于 2016年12月22日 06:29:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/41272985.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定