Eli's Blog

1. Getting Started

1
2
go get github.com/jmoiron/sqlx
github.com/go-sql-driver/mysql

2. Handle Types

4 handle types:

sqlx database/sql
sqlx.DB sql.DB
sqlx.Tx sql.Tx
sqlx.Stmt sql.Stmt
sqlx.NamedStmt

2 cursor types:

sqlx database/sql from
sqlx.Rows sql.Rows Queryx
sqlx.Row sql.Row QueryRowx

3. Connecting to Database

1
2
3
4
5
6
7
8
9
10
11
12
13
var dsn = "root:123456@tcp(127.0.0.1:3306)/mydb?parseTime=true&&charset=utf8mb4"

var db *sqlx.DB

// 1. same as sql.Open()
db, err = sqlx.Open("mysql", dsn)
err = db.Ping() // force a connection and test that is worked

// 2. open and connect at the same time
db, err = sqlx.Connect("mysql", dsn)

// 3. same as 2, but panic on error
db = sqlx.MustConnect("mysql", dsn)

4. Querying

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 1. unchanged from database/sql
Exec(query string, args ...interface{}) (sql.Result, error)
Query(query string, args ...interface{}) (*sql.Rows, error)
QueryRow(query string, args ...interface{}) *sql.Row

// 2. extensions
MustExec(query string, args ...interface{}) sql.Result
Queryx(query string, args ...interface{}) (*sqlx.Rows, error)
QueryRowx(query string, args ...interface{}) *sqlx.Row

// 3. new semantics: 结构体struct与数据库schema绑定
Select(dest interface{}, query string, args ...interface{}) error
Get(dest interface{}, query string, args ...interface{}) error // An error is returned if the result set is empty

// 4. sqlx.Row
type Rows struct {
*sql.Rows
unsafe bool
Mapper *reflectx.Mapper
// these fields cache memory use for a rows during iteration w/ structScan
started bool
fields [][]int
values []interface{}
}

// 5. sql.Result
LastInsertId() (int64, error)
RowsAffected() (int64, error)

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
func querying(db *sqlx.DB) {
// 1. Exec & MustExec
schema := `CREATE TABLE IF NOT EXISTS person (
id INT(10) AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
age TINYINT,
address VARCHAR(100)
)`
db.MustExec(schema)

sqlStr := "insert into person(name, age) values(?, ?)"
db.MustExec(sqlStr, "jack", 21)
db.MustExec(sqlStr, "maxin", 30)

sqlStr = "insert into person(name, age, address) values(?, ?, ?)"
result, err := db.Exec(sqlStr, "lucy", 39, "London, UK")
if err != nil {
panic(err)
}
id, _ := result.LastInsertId()
fmt.Printf("last insert id is %d\n", id)

// 2. Query & Queryx
sqlStr = "select * from person"
rows1, err := db.Query(sqlStr)
if err != nil {
panic(err)
}
for rows1.Next() {
var id int
var name string
var age uint8
var address sql.NullString
err = rows1.Scan(&id, &name, &age, &address)
if err != nil {
panic(err)
}
fmt.Printf("id: %d, name: %s, age: %d, address: %v\n", id, name, age, address)
}

type person struct {
Id int
Name string
Age uint8
Address sql.NullString
}
rows2, err := db.Queryx(sqlStr)
if err != nil {
panic(err)
}
for rows2.Next() {
var p person
rows2.Scan(&p)
fmt.Printf("%#v\n", p)
}

// 3. Get & Select
var p person
var pp []person

err = db.Get(&p, "select * from person limit 1")
if err != nil {
panic(err)
}
fmt.Printf("%#v\n", p)

err = db.Select(&pp, "select * from person where id > 2")
if err != nil {
panic(err)
}
fmt.Printf("%#v\n", pp)

var count int
db.Get(&count, "select count(*) from person")
fmt.Println(count)

var names []string
db.Select(&names, "select name from person")
fmt.Println(names)
}

5. Transactions

1
2
3
4
5
6
// 1. sql.Tx
Begin() (*sql.Tx, error)

// 2. sqlx.Tx
Beginx() (*sqlx.Tx, error)
MustBegin() (*sql.Tx)

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func transaction(db *sqlx.DB) {
tx := db.MustBegin()

defer func() {
if err := recover(); err != nil {
tx.Rollback()
}
}()

tx.MustExec("delete from person where id=4")
tx.MustExec("insert into person values(2, 'abc', 22, 'LA')")
tx.MustExec("insert into person values(100, 'abc', 22, 'LA')")

err := tx.Commit()
if err != nil {
panic(err)
}
}

6. Prepared Statements

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func prepared(db *sqlx.DB) {
stmt, _ := db.Prepare("select * from person where id=?")
row := stmt.QueryRow(5)

var id int
var name string
var age uint8
var address sql.NullString
row.Scan(&id, &name, &age, &address)
fmt.Printf("id: %d, name: %s, age: %d, address: %v\n", id, name, age, address)

stmtx, _ := db.Preparex("select * from person where id=?")
rowx := stmtx.QueryRowx(5)

var p person
rowx.Scan(&p)
fmt.Printf("%#v\n", p)
}

7. Query Helpers

7.1 “In” Queries

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
func inQuery(db *sqlx.DB) {
ids := []int{1, 2, 3, 4, 5}

/* // converting argument $1 type: unsupported type []int, a slice of int
rows, err := db.Query("select name from person where id in (?)", ids)
if err != nil {
panic(err)
}

for rows.Next() {
var name string
rows.Scan(&name)
fmt.Println(name)
}*/

// convert to (?, ?, ...)
query, args, err := sqlx.In("select name from person where id in (?)", ids)
if err != nil {
panic(err)
}

query = db.Rebind(query)
fmt.Println(query)
rows, err := db.Query(query, args...)
if err != nil {
panic(err)
}
for rows.Next() {
var name string
rows.Scan(&name)
fmt.Println(name)
}
}

7.2 Named Queries

1
2
3
NamedQuery(query string, arg interface{}) (*sqlx.Rows, error)
NamedExec(query string, arg interface{}) (sql.Result, error)
PrepareNamed(query string) (*NamedStmt, error)

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func namedQuery(db *sqlx.DB) {
// named query with a struct
p := person{Name: "jack"}
rows, _ := db.NamedQuery("select count(*) from person where name=:name", p)
for rows.Next() {
var count int
rows.Scan(&count)
fmt.Println(count)
}

// named query with a map
m := map[string]interface{}{"address": "LA"}
stmt, _ := db.PrepareNamed("select * from person where address=:address limit 1")
row := stmt.QueryRowx(m)
row.Scan(&p)
fmt.Printf("%#v\n", p)
}

8. Alternate Scan Types

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func alternateScan(db *sqlx.DB) {
rows, _ := db.Queryx("select * from person")
for rows.Next() {
cols, _ := rows.SliceScan()
fmt.Println(cols)
}

rows, _ = db.Queryx("select * from person")
for rows.Next() {
cols := make(map[string]interface{})
rows.MapScan(cols)
fmt.Println(cols)
}
}

9. Connection Pool

1
2
DB.SetMaxIdleConns(n int)
DB.SetMaxOpenConns(n int)