关于Golang中database/sql包的学习笔记
概述
使用DB导入driver这里使用的是MySQL drivers import ( "database/sql" _ "github.com/go-sql-driver/mysql" ) 连接DBfunc main() { db,err := sql.Open("mysql","user:password@tcp(127.0.0.1:3306)/hello") if err != nil { log.Fatal(err) } defer db.Close() }
err = db.Ping() if err != nil { // do something here } sql.DB的设计就是用来作为长连接使用的。不要频繁Open,Close。比较好的做法是,为每个不同的datastore建一个DB对象,保持这些对象Open。如果需要短连接,那么把DB作为参数传入function,而不要在function中Open,Close。 读取DB如果方法包含 var ( id int name string ) rows,err := db.Query("select id,name from users where id = ?",1) if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { err := rows.Scan(&id,&name) if err != nil { log.Fatal(err) } log.Println(id,name) } err = rows.Err() if err != nil { log.Fatal(err) } 上面代码的过程为:
单行Queryerr在 var name string err = db.QueryRow("select name from users where id = ?",1).Scan(&name) if err != nil { log.Fatal(err) } fmt.Println(name) 修改数据,事务一般用Prepared Statements和 stmt,err := db.Prepare("INSERT INTO users(name) VALUES(?)") if err != nil { log.Fatal(err) } res,err := stmt.Exec("Dolly") if err != nil { log.Fatal(err) } lastId,err := res.LastInsertId() if err != nil { log.Fatal(err) } rowCnt,err := res.RowsAffected() if err != nil { log.Fatal(err) } log.Printf("ID = %d,affected = %dn",lastId,rowCnt) 事务
如果你需要通过多条语句修改连接状态,你必须使用Tx,例如:
Prepared StatementsPrepared Statements and Connection在数据库层面,Prepared Statements是和单个数据库连接绑定的。客户端发送一个有占位符的statement到服务端,服务器返回一个statement ID,然后客户端发送ID和参数来执行statement。 在GO中,连接不直接暴露,你不能为连接绑定statement,而是只能为DB或Tx绑定。
这就导致在高并发的场景,过度使用statement可能导致statement泄漏,statement持续重复prepare和re-prepare的过程,甚至会达到服务器端statement数量上限。 某些操作使用了PS,例如 有些场景不适合用statement:
在Transaction中使用PSPS在Tx中唯一绑定一个连接,不会re-prepare。 Tx和statement不能分离,在DB中创建的statement也不能在Tx中使用,因为他们必定不是使用同一个连接使用Tx必须十分小心,例如下面的代码: tx,err := db.Begin() if err != nil { log.Fatal(err) } defer tx.Rollback() stmt,err := tx.Prepare("INSERT INTO foo VALUES (?)") if err != nil { log.Fatal(err) } defer stmt.Close() // danger! for i := 0; i < 10; i++ { _,err = stmt.Exec(i) if err != nil { log.Fatal(err) } } err = tx.Commit() if err != nil { log.Fatal(err) } // stmt.Close() runs here!
处理Error循环Rows的Error如果循环中发生错误会自动运行 for rows.Next() { // ... } if err = rows.Err(); err != nil { // handle the error here } 关闭Resultsets时的error如果你在rows遍历结束之前退出循环,必须手动关闭Resultset,并且接收error。 for rows.Next() { // ... break; // whoops,rows is not closed! memory leak... } // do the usual "if err = rows.Err()" [omitted here]... // it's always safe to [re?]close here: if err = rows.Close(); err != nil { // but what should we do if there's an error? log.Println(err) } QueryRow()的errorvar name string err = db.QueryRow("select name from users where id = ?",1).Scan(&name) if err != nil { log.Fatal(err) } fmt.Println(name) 如果id为1的不存在,err为sql.ErrNoRows,一般应用中不存在的情况都需要单独处理。此外,Query返回的错误都会延迟到Scan被调用,所以应该写成如下代码: var name string err = db.QueryRow("select name from users where id = ?",1).Scan(&name) if err != nil { if err == sql.ErrNoRows { // there were no rows,but otherwise no error occurred } else { log.Fatal(err) } } fmt.Println(name) 把空结果当做Error处理是为了强行让程序员处理结果为空的情况 分析数据库Error各个数据库处理方式不太一样,mysql为例: if driverErr,ok := err.(*mysql.MySQLError); ok { // Now the error number is accessible directly if driverErr.Number == 1045 { // Handle the permission-denied error } }
连接错误NULL值处理简单说就是设计数据库的时候不要出现null,处理起来非常费力。Null的type很有限,例如没有 for rows.Next() { var s sql.NullString err := rows.Scan(&s) // check err if s.Valid { // use s.String } else { // NULL value } } 未知Column
cols,err := rows.Columns() if err != nil { // handle the error } else { dest := []interface{}{ // Standard MySQL columns new(uint64),// id new(string),// host new(string),// user new(string),// db new(string),// command new(uint32),// time new(string),// state new(string),// info } if len(cols) == 11 { // Percona Server } else if len(cols) > 8 { // Handle this case } err = rows.Scan(dest...) // Work with the values in dest } cols,err := rows.Columns() // Remember to check err afterwards vals := make([]interface{},len(cols)) for i,_ := range cols { vals[i] = new(sql.RawBytes) } for rows.Next() { err = rows.Scan(vals...) // Now you can check each element of vals for nil-ness,// and you can use type introspection and type assertions // to fetch the column into a typed variable. } 关于连接池
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |