Go语言数据库编程
该包提供了对SQL或者类SQL数据库提供通用访问接口。
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 |
import "database/sql" import ( // 引入驱动包 _ "github.com/go-sql-driver/mysql" "log" ) func main() { // 打开数据库连接 db, _ := sql.Open(driver, dataSourceName) // 直到发起第一个查询,并不会真正打开连接 // 下面的命令可以验证连接是否正常 if err := db.Ping(); err != nil { log.Fatal(err) } // 执行SQL插入 result, _ := db.Exec( "INSERT INTO users (name, age) VALUES (?, ?)", "Alex", 31, ) // 上一次插入的ID、影响的行数 id, _ := result.LastInsertId() rowCount, _ := result.RowsAffected() // 执行SQL查询 rows, _ := db.Query("SELECT name FROM users WHERE age = ?", 31) // 遍历结果集 for rows.Next() { var name string rows.Scan(&name) } // 执行SQL单行查询 var age int row := db.QueryRow("SELECT age FROM users WHERE name = ?", "Alex") row.Scan(&age) // 预编译语句 stmt, err := db.Prepare("SELECT name FROM users WHERE age = ?") if err != nil { log.Fatal(err) } rows, _ := stmt.Query(31) // 事务支持 tx, _ := db.Begin() tx.Commit() tx.Rollback() // Context支持,可以进行查询取消或者超时 db.QueryContext(ctx,sql,arg0,arg1) } |
如果数据库的列可空,则你必须使用支持空值的Go类型来接收它。sql包中实现的类型包括NullBool、NullFloat64、NullInt64、NullString。示例代码:
1 2 3 4 5 6 7 |
var name NullString db.QueryRow("SELECT name FROM names WHERE id = ?", id).Scan(&name) if name.Valid { } else { // 数据库空值 } |
标准库内置了数据库连接池:
1 2 3 4 5 6 7 |
// 连接池中连接的最大数量 db.SetMaxOpenConns(100) // 连接池中最大空闲连接数量 db.SetMaxIdleConns(50) mlt, _ := time.ParseDuration("600s") // 连接可以被重用的最大时长 db.SetConnMaxLifetime(mlt) |
该包对Go标准库database/sql进行了扩展,支持:
- 将结果集行反序列化为结构(支持嵌入式结构)、映射、切片
- 支持命名参数
- 通过Get/Query便捷的把查询结果转换为结构或切片
执行下面的命令安装此包:
1 |
go get github.com/jmoiron/sqlx |
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 |
package main import ( _ "github.com/mattn/go-sqlite3" "github.com/jmoiron/sqlx" "github.com/jinzhu/now" "time" "fmt" "log" ) const DDL = ` CREATE TABLE USERS ( USER_NAME VARCHAR(64), USER_AGE INTEGER, BIRTHDAY DATE ) ` type User struct { // sqlx使用db标签 Name string `db:"USER_NAME"` Age uint8 `db:"USER_AGE"` DOB time.Time `db:"BIRTHDAY"` // 注意,如果数据库字段可为空,则结构的字段类型必须用sql.Null* } func main() { db, _ := sqlx.Connect("sqlite3", ":memory:") // 执行语句,如果失败则终止程序 db.MustExec(DDL) // 在事务中执行 tx := db.MustBegin() tx.MustExec( "INSERT INTO USERS (USER_NAME, USER_AGE ,BIRTHDAY) VALUES ($1, $2, $3)", "Alex", 31, "1986-09-12", // UTC时间 ) tx.MustExec("INSERT INTO USERS (USER_NAME, USER_AGE ,BIRTHDAY) VALUES ($1, $2, $3)", "Meng", 28, "1989-11-06") tx.NamedExec( "INSERT INTO USERS (USER_NAME, USER_AGE ,BIRTHDAY) VALUES (:USER_NAME, :USER_AGE, :BIRTHDAY)", &User{"Cai", 3, now.MustParse("2014-11-27")}, // 本地时间 ) tx.Commit() // 根据Tag自动把行装配到结构 var users []User err := db.Select(&users, "SELECT * FROM USERS ORDER BY USER_NAME ASC") // 不能使用$1必须使用?,否则报错 err := db.Select(&users, "SELECT * FROM USERS WHERE USER_TYPE = ?", type) if err != nil { log.Fatal(err) } alex, cai, meng := users[0], users[1], users[2] fmt.Printf("%v\n%v\n%v\n", alex, cai, meng) // 类似,单仅仅处理第一行 alex = User{} db.Get(&alex, "SELECT * FROM USERS WHERE USER_NAME=$1", "Alex") fmt.Printf("%v\n", alex) // 使用单个结构示例来遍历所有行 user := User{} // rows是一个游标 rows, _ := db.Queryx("SELECT * FROM USERS") for rows.Next() { rows.StructScan(&user) fmt.Println(user.Name) } // 命名查询 rows, _ = db.NamedQuery(`SELECT * FROM USERS WHERE USER_NAME=:un`, map[string]interface{}{"un": "Alex"}) // 命名查询可以直接传入结构,就像上面的NamedExec一样 // 支持Context db.QueryxContext(ctx,sql,arg0,arg1) } |
该包支持MySQL、MariaDB、Percona、Sphinx等数据库。执行下面的命令安装:
1 |
go get -u github.com/go-sql-driver/mysql |
打开数据库连接:
1 2 3 4 5 6 |
import ( // 引入驱动 _ "github.com/go-sql-driver/mysql" ) //驱动名 数据源名称(DSN) db, err := sql.Open("mysql", "user:password@/dbname") |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN] # address 为TCP或UDP地址,格式host[:port],如果host为IPv6则必须用 []包围 # 支持的参数: # allowCleartextPasswords 是否允许明文密码,true/false # charset 字符集,示例charset=utf8mb4,utf8 多个逗号分隔,优先使用前面。最好使用collation参数代替 # collation 客户端服务器通信时使用的编码方式,默认utf8_general_ci # maxAllowedPacket 最大允许的包大小 # parseTime 如果设置为true则DATE/DATETIME被输出为time.Time而非[]byte / string # readTimeout IO读超时 # timeout 连接超时 # writeTimeout IO写超时 # 示例 user:password@tcp(localhost:3306)/dbname?timeout=90s&collation=utf8mb4_unicode_ci |
该驱动支持Go 1.8的ColumnType接口,但是不支持ColumnType.Length()方法。
从Go 1.8开始,database/sql包支持context.Context。该驱动支持基于Context的查询超时和取消。
MySQL的DATE、DATETIME内部默认输出类型为[]byte,你可以将其Scan到[]byte、string、sql.RawBytes等类型的字段。
如果需要将DATE、DATETIME输出为time.Time,设置DSN参数parseTime=true。
报错信息:sql: Scan error on column index 8: unsupported Scan, storing driver.Value type []uint8 into type time.Time
解决方案:设置DSN参数parseTime=true即可
报错信息:sql: Scan error on column index 3: converting driver.Value type \u003cnil\u003e (\"\u003cnil\u003e\") to a uint64: invalid syntax
解决方案: \u003cnil\u003e就是<nil>,意味着取到了空的列值。使用Null安全的字段类型。上面的index 3表示是SQL中第4个列
Leave a Reply