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