Files
rikako-note/Golang/go-sqlx.md
2025-06-16 01:09:29 +08:00

5.1 KiB
Raw Blame History

SQLX

Getting Start with SQLite

为了安装sqlx和database driver可以通过如下命令

$ go get github.com/jmoiron/sqlx
$ go get github.com/mattn/go-sqlite3

Handle Types

sqlxdatabase/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实例
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
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

ExecMustExec方法会从connection pool中获取连接并且执行提供的sql方法。

并且在result返回之前连接将会被归还到连接池。因为Exec是非事务的故而连接无需等待result返回可以直接被其他协程使用

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语句的结构。例如如下语句都是不被允许的

// doesn't work
db.Query("SELECT * FROM ?", "mytable")
 
// also doesn't work
db.Query("SELECT ?, ? FROM people", "name", "location")