所有的我已经看到了使用实施例sql.Row,获得的返回值,从查询由 位置 :sql.Rows.scan()需要正确地正确类型的变量 定位 在scan()对应于相应的列,来检索每个列的值的参数返回,如在下面的例子:
sql.Row
sql.Rows.scan()
scan()
基于GoDocs的示例(带有小型mod):
rows, err := db.Query("SELECT name,age FROM users WHERE age>=50") if err != nil { log.Fatal(err) } for rows.Next() { var name string var age int if err := rows.Scan(&name,&age); err != nil { log.Fatal(err) } fmt.Printf("%s is %d\n", name, age) } if err := rows.Err(); err != nil { log.Fatal(err) }
&name并且&age必须正确放置Rows.Scan()的位置(0和1列),以检索具有正确类型的正确值。
&name
&age
在生产系统的多年开发中,我一直在避免这种做法,因为这种做法不够可靠:如果数据库的布局基于列位置,则更改列布局会很容易破坏您的代码。
使用 列名 检索值要更健壮- 这样可以使您免受对数据库的更改的影响,这些更改添加或删除了会损坏基于位置的代码的列。例如,在Delphi和C#中,所有的数据集,包括返回从查询,支持值的列FieldByName('age').asInteger或fields['age'].value,等
FieldByName('age').asInteger
fields['age'].value,
有什么办法可以在Go中做到这一点?如果不是这样,这将是Go数据库支持中的一个很大缺点,并且会令人严重失望-完全不安全,如上所述。
编辑:
另外 (也许这是一个新问题) :我所看到的示例似乎要求您检索查询返回的所有列,否则列的位置将倾斜。
假设在锁定的数据库中存在无法修改或添加的实用程序查询,并且它检索了几列,但是对于当前任务,我只需要其中一列。基于当前sql.Rows.Scan()模型,即使我不需要它们,我也必须从应用程序代码中的查询中检索所有值,而如果我可以查询"columnByName"不必要的值,则可以将我的应用程序代码带入我需要的数据。有任何解决方法吗?
sql.Rows.Scan()
"columnByName"
是的,可以这样做,而不必手动匹配列位置。您可以使用一些第三方库来执行此操作,例如sqlx或gorp。我建议坚持使用其中之一,而不要自己动手。
命名匹配确实会有轻微的损失。命名匹配与自己匹配列位置没有什么不同。它只是在运行时为您完成这项工作-可能会在每次查询执行时执行。在任何其他语言中都是如此。
为什么在运行时?查询被写为字符串。必须对其进行分析以确定位置。
如果要创建自己的库,该如何自己做呢?
将切片的指针传递[]interface{}给Rows.Scan以获取值。
[]interface{}
reflect.Value和Value.Addr获得指向目标值的指针。
Value如果要映射到结构字段,则使用Value.FieldByName获取结构字段的。
Value
好的,让我们看看它是如何工作的。
type Person struct { Id int Name string } rows, err := db.Query("SELECT id, name FROM person;") if err != nil { // handle err log.Fatal(err) } columnNames, err := rows.Columns() // []string{"id", "name"} if err != nil { // handle err log.Fatal(err) } people = make([]Person, 0, 2) for rows.Next() { person := Person{} // person == Person{0, ""} pointers := make([]interface{}, len(columnNames)) // pointers == `[]interface{}{nil, nil}` structVal := reflect.ValueOf(person) for i, colName := range columnNames { fieldVal := structVal.FieldByName(strings.Title(colName)) if !fieldVal.IsValid() { log.Fatal("field not valid") } pointers[i] = fieldVal.Addr().Interface() } // pointers == `[]interface{}{&int, &string}` err := rows.Scan(pointers...) if err != nil { // handle err log.Fatal(err) } // person == Person{1, "John Doe"} people = append(people, person) }