diff --git a/Golang/go-sqlx.md b/Golang/go-sqlx.md new file mode 100644 index 0000000..2f980a5 --- /dev/null +++ b/Golang/go-sqlx.md @@ -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") +``` + + +