连接 go语言中的database/sql
包提供了Sql数据库的泛用接口,但是不提供具体的驱动。使用这个包时必须注入数据库驱动。
下载依赖 1 go get -u github.com/go-sql-driver/mysql
使用MySql驱动 1 func Open (driverName, dataSourceName string ) (*DB, error )
打开一个指定driverName的数据库,dataSourceName指定数据源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) func Open () { dsn := "root:123@tcp(127.0.0.1:3306)/t1231est" db, err := sql.Open("mysql" , dsn) if err != nil { fmt.Printf("打开失败,db:%#v" , db) panic (err) } defer db.Close() fmt.Printf("成功打开数据库,db:%#v" , db) }
初始化连接 Open函数只有检查参数格式的作用,并不创建与数据库的连接,如需检查数据源是否有效应该使用Ping方法。
Open得到的DB对象可以被多个goroutine并发使用,并且维护一个空闲的连接池。所以Open函数应该只被调用一次,很少关闭DB对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var db *sql.DB func initDB () (err error ) { dsn := "root:123@tcp(127.0.0.1:3306)/tes22t" db, err = sql.Open("mysql" , dsn) if err != nil { fmt.Printf("打开失败,db:%#v" , db) return err } err = db.Ping() if err != nil { return err } return nil }
SetMaxOpenConns 1 func (db *DB) SetMaxOpenConns(n int )
设置与数据库建立连接的最大数目。n≤0时连接数无限制,0<n<最大闲置连接数时,最大闲置连接数会减小到最大连接数。
SetMaxIdleConns 1 func (db *DB) SetMaxIdleConns(n int )
设置连接池中的最大闲置连接数。n ≤ 0时不会保留闲置连接,n > 最大开启连接数时,最大闲置连接数减小到最大开启连接数。
CRUD 创建表 选定或新建一个数据库,使用以下SQL创建一张表
1 2 3 4 5 CREATE TABLE `user ` ( `id` int (4 ) NOT NULL , `name` varchar (32 ) NOT NULL , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8;
查询 首先封装结构体代表表中的每一行记录
1 2 3 4 5 / / 创建结构体封装数据库查询对象type user struct { id int name string }
单行查询 通过db.QueryRow()
来查询单行数据,期望返回最多一行结果。
返回值的Scan
方法被调用时才会返回被延时的错误。
1 func (db *DB) QueryRow(query string , args ...interface {}) *Row
示例:
1 2 3 4 5 6 7 8 9 10 11 func queryRowDemo () { sqlStr := "select id, name from user where id = ?" var u user err := db.QueryRow(sqlStr, 1 ).Scan(&u.id, &u.name) if err != nil { fmt.Printf("scan failed, err:%v\n" , err) return } fmt.Printf("id:%d name:%s\n" , u.id, u.name) }
多行查询 db.Query()执行一次查询返回多行数据
1 func (db *DB) Query(query string , args ...interface {}) (*Rows, error )
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func queryMultiRowDemo () { sqlStr := "select id, name from user where id > 0" rows, err := db.Query(sqlStr) if err != nil { fmt.Printf("scan failed, err:%v\n" , err) return } defer rows.Close() for rows.Next() { var u user err := rows.Scan(&u.id, &u.name) if err != nil { fmt.Printf("Scan failed ,err: %v\n" , err) return } fmt.Printf("id:%d name:%s\n" , u.id, u.name) } }
插入 插入、更新和删除操作都使用Exec
方法。
1 func (db *DB) Exec(query string , args ...interface {}) (Result, error )
Result是对已执行的SQL命令的总结, 参数args表示query中的占位参数。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func insertRowDemo () { sqlstr := "insert into user (id, name) values (?, ?)" ret, err := db.Exec(sqlstr, 223 , "王五" ) if err != nil { fmt.Printf("insert failed, err:%v\n" , err) return } theID, err := ret.LastInsertId() if err != nil { fmt.Printf("get lastinsert ID failed, err:%v\n" , err) return } fmt.Printf("insert success, the id is %d.\n" , theID) }
更新 1 2 3 4 5 6 7 8 9 10 11 12 13 14 func updateRowDemo () { sqlStr := "update user set name = ? where id = ?" ret, err := db.Exec(sqlStr, "张s" , 222 ) if err != nil { fmt.Printf("update failed, err:%v\n" , err) return } n, err := ret.RowsAffected() if err != nil { fmt.Printf("get RowsAffected failed, err:%v\n" , err) return } fmt.Printf("update success, affected rows:%d\n" , n) }
删除 1 2 3 4 5 6 7 8 9 10 11 12 13 14 func deleteRowDemo () { sqlstr := "delete from user where id = ?" ret, err := db.Exec(sqlstr, 2 ) if err != nil { fmt.Printf("delete failed, err:%v\n" , err) return } n, err := ret.RowsAffected() if err != nil { fmt.Printf("get RowsAffected failed, err:%v\n" , err) return } fmt.Printf("delete success, affected rows:%d\n" , n) }
预处理 概念 不使用预处理时完整的SQL语句会发送给客户端,会有SQL注入的风险。使用预处理可以将SQL与数据拆分,先将要执行的SQL“模板”发送给数据库预处理,之后再将占位符参数发送给数据库,这样就可以防止SQL注入。
普通SQL语句执行过程:
客户端对SQL语句进行占位符替换得到完整的SQL语句。
客户端发送完整SQL语句到MySQL服务端
MySQL服务端执行完整的SQL语句并将结果返回给客户端。
预处理执行过程:
把SQL语句分成两部分,命令部分与数据部分。
先把命令部分发送给MySQL服务端,MySQL服务端进行SQL预处理。
然后把数据部分发送给MySQL服务端,MySQL服务端对SQL语句进行占位符替换。
MySQL服务端执行完整的SQL语句并将结果返回给客户端。
优点
优化MySQL服务器重复执行SQL的方法,可以提升服务器性能,提前让服务器编译,一次编译多次执行,节省后续编译的成本。
避免SQL注入问题。
Go实现MySql预处理语句 database/sql
中使用下面的Prepare
方法来实现预处理操作。
1 func (db *DB) Prepare(query string ) (*Stmt, 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 func prepareQueryDemo () { sqlstr := "select id, name from user where id > ?" s, err := db.Prepare(sqlstr) if err != nil { fmt.Printf("prepare failed, err:%v" , err) return } defer s.Close() rows, err := s.Query(0 ) if err != nil { fmt.Printf("query failed, err:%v\n" , err) return } defer rows.Close() for rows.Next() { var u user err := rows.Scan(&u.id, &u.name) if err != nil { fmt.Printf("scan failed, err:%v\n" , err) return } fmt.Printf("id:%d name:%s\n" , u.id, u.name) } }
插入、删除、更新示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func prepareInsertDemo () { sqlStr := "insert into user (id, name) values (?, ?)" s, err := db.Prepare(sqlStr) if err != nil { fmt.Printf("prepare failed err:%v" , err) return } defer s.Close() _, err = s.Exec(123 , "大王子" ) if err != nil { fmt.Printf("insert failed err:%v" , err) return } _, err = s.Exec(124 , "小公主" ) if err != nil { fmt.Printf("insert failed err:%v" , err) return } fmt.Printf("insert succeed" ) }
Go实现MySql事务 开启事务
1 func (db *DB) Begin() (*Tx, error )
提交事务
1 func (tx *Tx) Commit() error
回滚事务
1 func (tx *Tx) Rollback() 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 func transactionDemo () { tx, err := db.Begin() if err != nil { if tx != nil { tx.Rollback() } fmt.Printf("begin trans failed , err:%v\n" , err) return } sqlStr1 := "Update user set name = ? where id = ?" ret1, err := tx.Exec(sqlStr1, "大公主" , 124 ) if err != nil { tx.Rollback() fmt.Printf("exec sql1 failed, err:%v\n" , err) return } affRow1, err := ret1.RowsAffected() if err != nil { tx.Rollback() fmt.Printf("exec ret1.RowsAffected() failed, err:%v\n" , err) return } sqlStr2 := "Update user set name='王大锤' where id=?" ret2, err := tx.Exec(sqlStr2, 3 ) if err != nil { tx.Rollback() fmt.Printf("exec sql2 failed, err:%v\n" , err) return } affRow2, err := ret2.RowsAffected() if err != nil { tx.Rollback() fmt.Printf("exec ret1.RowsAffected() failed, err:%v\n" , err) return } fmt.Println(affRow1, affRow2) if affRow1 == 1 && affRow2 == 1 { fmt.Println("事务提交啦..." ) tx.Commit() } else { tx.Rollback() fmt.Println("事务回滚啦..." ) } fmt.Println("exec trans success!" ) }