diff --git a/Golang/go-sqlx.md b/Golang/go-sqlx.md index 572a620..ce0bedb 100644 --- a/Golang/go-sqlx.md +++ b/Golang/go-sqlx.md @@ -147,6 +147,93 @@ err = rows.Err() > 在使用`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`: `Exec`方法在`server返回执行结果给client之后`,`client根据返回结果构建并返回sql.Result之前`,将会将连接返回给连接池