我看到的所有使用的示例sql.Row,按位置访问查询的返回值:sql.Rows.scan()需要在对应于相应列的参数中正确定位的正确类型变量scan(),以检索返回的每个列值,例如在以下示例中:
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)
}
Run Code Online (Sandbox Code Playgroud)
&name并且&age必须正确定位(第0列和第1列)以便Rows.Scan()使用正确的类型检索正确的值.
在我多年的生产系统开发中,我已经刻意避免这种做法,因为它不健壮:如果基于列位置,列的布局中的数据库更改将很容易破坏您的代码.
使用列名来检索值会更加健壮- 这可以使您免受对添加或删除会破坏基于位置的代码的列的数据库的更改.例如,在Delphi和C#,所有数据集,包括从查询返回值的列,支持FieldByName('age').asInteger或fields['age'].value,等
在Go中有什么方法可以做到这一点?如果没有,这是Go数据库支持的一个很大的缺点,并且非常令人失望 - 如上所述,根本不安全.
编辑:
另外(也许这是一个新问题):我看到的示例似乎要求您检索查询返回的所有列,否则列的位置将会偏斜.
假设在锁定的数据库中存在我无法修改或添加的实用程序查询,并且它检索了多个列,但我只需要其中一个用于当前任务.基于当前sql.Rows.Scan()模型,我必须在我的应用程序代码中检索查询中的所有值,即使我不需要它们,而如果我可以查询"columnByName"那些没有必要 - 我可以将我的应用程序代码引入我需要的数据.为此做任何解决方法?
Luk*_*uke 15
是的,可以在不必手动匹配列位置的情况下执行此操作.您可以使用一些第三方库来执行此操作,例如sqlx或gorp.我建议坚持使用其中一种而不是自己动手.
命名匹配确实有轻微的惩罚.命名匹配与自己匹配列位置没有什么不同.它只是在运行时为您工作 - 可能在每次查询执行时.在任何其他语言中都是如此.
为什么在运行时?查询被写为字符串.必须对其进行解析以确定位置.
如果你要建立自己的图书馆,你如何自己做?
将一片指针传递[]interface{}给Rows.Scan以获取值.
reflect.Value和Value.Addr获取指向目标值的指针.
Value如果要映射到struct字段,则使用Value.FieldByName来获取struct字段.好的,让我们看看它是如何工作的.
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)
}
Run Code Online (Sandbox Code Playgroud)