doc: 阅读golang sqlx文档

This commit is contained in:
asahi
2025-06-16 01:09:29 +08:00
parent 89f8bd9db4
commit 3ddc976a66

127
Golang/go-sqlx.md Normal file
View File

@@ -0,0 +1,127 @@
# SQLX
## Getting Start with SQLite
为了安装sqlx和database driver可以通过如下命令
```bash
$ go get github.com/jmoiron/sqlx
$ go get github.com/mattn/go-sqlite3
```
## Handle Types
`sqlx``database/sql`等价,主要有四种类型:
- `sqlx.DB`: 和`sql.DB`等价用于表示database
- `sqlx.Tx`: 和`sql.Tx`等价,用于表示事务
- `sqlx.Stmt`: 和`sql.Stmt`等价用于表示Prepared Statement
- `sqlx.NamedStmt` 用于表示`Prepared Statement with support for named parameters`
Handle Types中都嵌入了其`database/sql`中等价的对象,在调用`sqlx.DB.query`时,其实际调用的代码和`sql.DB.Query`
> 例如,`sqlx.DB`实现中包含了`sql.DB`的对象引用,在调用`sqlx.DB`的方法时,实际会调用内部嵌套的`sql.DB`中的方法。
sqlx中除了上述类型外还引入了两个cursor类型
- `sqlx.Rows` 等价于`sql.Rows`,为`Queryx`返回的cursor
- `sqlx.Row` 等价于`sql.Row`:,由`QueryRowx`返回的结果
在上述两个cursor类型中`sqlx.Rows`嵌入了`sql.Rows``但是由于底层实现不可访问sqlx.Row对sql.Row的部分内容进行了重新实现标准接口保持一致`
### Connecting to database
一个`DB`实例代表的只是一个数据库的抽象,其并不代表实际连接。`故而对DB实例进行创建并不会返回error或是抛出panic`
DB实例在内部维护了一个连接池并且`初次需要获取连接时`去尝试建立连接。
创建`sqlx.DB`有两种方式,
- 通过`sqlx.DB.Open`方法进行创建
- 通过`sqlx.DB.NewDb`方法进行创建,该方法接收一个已经存在的`sql.DB`实例
```golang
var db *sqlx.DB
// exactly the same as the built-in
db = sqlx.Open("sqlite3", ":memory:")
// from a pre-existing sql.DB; note the required driverName
db = sqlx.NewDb(sql.Open("sqlite3", ":memory:"), "sqlite3")
// force a connection and test that it worked
err = db.Ping()
```
#### Open Db and connect in same time
在某些场景下,可能希望在创建数据库实例时就连接数据库,可以使用如下方法,它们会在创建数据库的同时调用`Ping`
- `sqlx.Connect`如果在遇到错误时会返回error
- `sqlx.MustConnect`在遇到错误时会发生panic
```go
var err error
// open and connect at the same time:
db, err = sqlx.Connect("sqlite3", ":memory:")
// open and connect at the same time, panicing on error
db = sqlx.MustConnect("sqlite3", ":memory:")
```
## Query
handle Types中实现了和`database/sql`中同样的database方法
- `Exec(...) (sql.Result, error)``database/sql`中一致
- `Query(...) (*sql.Rows, error)`:和`database/sql`中一致
- `QueryRow(...) *sqlRow`:和`database/sql`中一致
如下是内置方法的拓展:
- `MustExec() sql.Result ` Exec并且在错误时发生panic
- `Queryx(...) (*sqlx.Rows, error)` Query但是会返回`sqlx.Rows`
- `QueryRowx(...) *sqlx.Row` QueryRow但是会返回`sqlx.Row`
如下是新语意的方法:
- `Get(dest interface{}, ...) error`
- `Select(dest interface{}, ...) error`
### Exec
`Exec``MustExec`方法会从connection pool中获取连接并且执行提供的sql方法。
> 并且在result返回之前连接将会被归还到连接池。因为Exec是非事务的故而连接无需等待result返回可以直接被其他协程使用
```go
schema := `CREATE TABLE place (
country text,
city text NULL,
telcode integer);`
// execute a query on the server
result, err := db.Exec(schema)
// or, you can use MustExec, which panics on error
cityState := `INSERT INTO place (country, telcode) VALUES (?, ?)`
countryCity := `INSERT INTO place (country, city, telcode) VALUES (?, ?, ?)`
db.MustExec(cityState, "Hong Kong", 852)
db.MustExec(cityState, "Singapore", 65)
db.MustExec(countryCity, "South Africa", "Johannesburg", 27)
```
`db.Exec`返回的结果中包含两部分信息:
- `LastInsertedId`: 在mysql中该字段在使用auto_increment key时会返回最后插入的id
- `RowsAffected`
#### bindvars
上述示例中,`?`占位符在内部调用了`bindvars`使用占位符能够避免sql注入。
`database/sql`并不会对query text做任何验证其将query text和encoded params原封不动的发送给server。
除非底层driver做了实现否则query语句会在server端运行sql语句之前执行prepare操作bindvars每个database可能语法都不相同
- mysql会使用`?`来作为bindvars的占位符
- postgresql会使用`$1, $2`来作为bindvars的占位符
- sqlite既接收`?`又接收`$1`
- oracle接收`:name`语法
##### Rebind
可以通过`sqlx.DB.Rebind(string) string`方法,将使用`?`的query sql转化为当前数据库类型的query sql。
##### bindvars is only used for parameterization
`bindvars机制`只能够被用于参数化并不允许通过bindvars改变sql语句的结构。例如如下语句都是不被允许的
```go
// doesn't work
db.Query("SELECT * FROM ?", "mytable")
// also doesn't work
db.Query("SELECT ?, ? FROM people", "name", "location")
```