doc: 阅读golang-sqlx文档

This commit is contained in:
asahi
2025-06-17 01:14:30 +08:00
parent 65a711ad96
commit 48a3e7614b

View File

@@ -147,6 +147,93 @@ err = rows.Err()
> 在使用`Rows`时,如果并不迭代完整个结果集,请确保调用`rows.Close()`方法将连接返回到连接池中。 > 在使用`Rows`时,如果并不迭代完整个结果集,请确保调用`rows.Close()`方法将连接返回到连接池中。
#### Query errors
其中,`Query`方返回的error可能是`在服务端进行prepare操作或execute操作时发生的任何异常`,该异常的可能场景如下:
- 从连接池中获取了bad connection
- 因sql语法、类型不匹配、不正确的field name或table name导致的错误
在大多数场景下,`Rows.Scan`会复制其从driver获取的数据因为`Rows.Scan`并无法感知driver对缓冲区进行reuse的方式。类型`sql.RawBytes`可以被用于获取` zero-copy slice of bytes from the actual data returned by the driver`。在下一次调用`Next`方法时该值将会无效因为该bytes的内存空间将会被driver重写。
#### Connection Closed Scenes
在调用完`Query`方法后connection将会等到如下两种场景才会关闭
- Rows中所有的行都通过`Next`方法调用被迭代
- rows中所有行未被完全迭代但是`rows.Close()`方法被调用
#### Queryx
sqlx拓展的`Queryx`方法,其行为和`Query`方法一致,但是实际返回的是`sqlx.Rows`类型,`sqlx.Rows`类型对scan行为进行了拓展
```go
type Place struct {
Country string
City sql.NullString
TelephoneCode int `db:"telcode"`
}
rows, err := db.Queryx("SELECT * FROM place")
for rows.Next() {
var p Place
err = rows.StructScan(&p)
}
```
`sqlx.Rows`的主要拓展是支持`StructScan()`其会自动将结果扫描到struct fields中。
> 注意在使用struct scan时struct field必须被exported首字母大写
可以使用`db` struct tag指定field映射到哪个column。默认情况下会对field name使用`strings.Lower`并和column name相匹配。
#### QueryRow
QueryRow会从server端拉取一行数据。其从connection pool中获取一个连接并且通过Query执行该查询并返回一个`Row` object`Row object`内部包含了`Rows`对象
```go
row := db.QueryRow("SELECT * FROM place WHERE telcode=?", 852)
var telcode int
err = row.Scan(&telcode)
```
和Query不同的是QueryRow并不会返回error故而可以对返回结果链式嵌套其他方法调用例如`Scan`
如果在执行`QueryRow`查询时报错那么该error将会被`Scan`方法返回如果并没有查询到rows那么Scan方法将会返回`sql.ErrNoRows`
如果Scan操作失败了例如类型不匹配error也会被Scan方法返回。
Row对象内部的`Rows`结构在Scan后就会关闭`即代表QueryRow使用的连接持续打开直到Result被扫描后才会关闭`
#### QueryRowx
`QueryRowx`拓展将会返回一个sqlx.Row`,其实现了和`sqlx.Rows`相同的拓展,示例如下
```go
var p Place
err := db.QueryRowx("SELECT city, telcode FROM place LIMIT 1").StructScan(&p)
```
#### Get & Select
Get/Select和`QueryRow`/`Query`类似,但是其能够节省代码编写,并能提供灵活的扫描语义。
##### scannable
scannable定义如下
- 如果value并不是struct那么该value是scannable的
- 如果value实现了sql.Scanner那么该value是scannable的
- 如果struct没有exported field那么其是scannble的
Get和Select对于scannable类型使用了`rows.Scan`方法对non-scannable类型使用`rows.StructScan`方法,使用示例如下:
```go
p := Place{}
pp := []Place{}
// this will pull the first place directly into p
err = db.Get(&p, "SELECT * FROM place LIMIT 1")
// this will pull places with telcode > 50 into the slice pp
err = db.Select(&pp, "SELECT * FROM place WHERE telcode > ?", 50)
// they work with regular types as well
var id int
err = db.Get(&id, "SELECT count(*) FROM place")
// fetch at most 10 place names
var names []string
err = db.Select(&names, "SELECT name FROM place LIMIT 10")
```
`Get`和`Select`都会对Rows进行关闭并且在执行遇到错误时会返回error。
> 但是需要注意的是Select会将整个result set都导入到内存中。如果结果集较大最好使用传统的`Queryx`/`StructScan`迭代方式。
### Exec和Query在归还连接池上的差异 ### Exec和Query在归还连接池上的差异
Exec操作和Query操作在归还连接到连接池的时机有所不同 Exec操作和Query操作在归还连接到连接池的时机有所不同
- `Exec` `Exec`方法在`server返回执行结果给client之后``client根据返回结果构建并返回sql.Result之前`,将会将连接返回给连接池 - `Exec` `Exec`方法在`server返回执行结果给client之后``client根据返回结果构建并返回sql.Result之前`,将会将连接返回给连接池