基本概念open() – creates a db
close() - closes the db
query() - 查询
queryrow() -查询行
exec() -执行操作,update,insert,delete
row - a row is not a hash map, but an abstraction of a cursor
next()
scan()注意:db并不是指的一个connection
连接到数据库我们以mysql为例,使用github.com/go-sql-driver/mysql,首先我们需要导入我们需要的包
import ( database/sql _ github.com/go-sql-driver/mysql)
注意我们导入github.com/go-sql-driver/mysql 前面用了一个,操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数,import的时候其实是执行了该包里面的init函数,初始化了里面的变量,_操作只是说该包引入了,我只初始化里面的 init函数和一些变量,但是往往这些init函数里面是注册自己包里面的引擎,让外部可以方便的使用,就很多实现database/sql的包,在 init函数里面都是调用了sql.register(name string, driver driver.driver)注册自己,然后外部就可以使用了。
我们用open()函数来打开一个database handle
db, err := sql.open(mysql, user:password@tcp(ip:port)/database)
写一个完整的:
import ( database/sql _ github.com/go-sql-driver/mysql log)func main() { db, err := sql.open(mysql, user:password@tcp(ip:port)/database) if err != nil { log.println(err) } //在这里进行一些数据库操作 defer db.close()}
我们在执行open函数的时候,并不会去获得数据库连接有效性,当执行数据库操作的时候才会去连接,当我们需要在open之后就知道连接的有效性的时候,可以通过ping()来进行
err = db.ping()if err != nil { log.println(err)}
我们通常习惯使用close来关闭数据库连接,但是sql.db是被设计成长期有效的类型,我们不应该频繁的open和close,相反,我们应该建立一个sql.db,在程序需要进行数据库操作的时候一直使用它,不要在一个方法里面进行open和close,应该把sql.db作为参数传递给方法
进行数据库操作增删改操作exec()方法一般用于增删改操作,这里以增加为例:
stmt, err := db.prepare(insert into user(name,age)values(?,?))if err != nil { log.println(err)}rs, err := stmt.exec(go-test, 12)if err != nil { log.println(err)}//我们可以获得插入的idid, err := rs.lastinsertid()//可以获得影响行数affect, err := rs.rowsaffected()
查询操作一般的查询 var name string var age int rows, err := db.query(select name,age from user where id = ? , 1) if err != nil { fmt.println(err) } defer rows.close() for rows.next() { err := rows.scan(&name, &age) if err != nil { fmt.println(err) } } err = rows.err() if err != nil { fmt.println(err) } fmt.println(name:, url, age:, description)
我们应该养成关闭rows的习惯,在任何时候,都不要忘记rows.close().哪怕这个rows在确实循环完之后,已经自动关闭掉了,我们定义rows.close()也是对我们没有坏处的,因为我们无法保证,rows是否会正常的循环完。
查询单条记录,我们使用db.queryrow()
var name string err = db.queryrow(select name from user where id = ?, 222).scan(&name)
没有结果的时候会返回err
处理空值我们用一个name字段为空的记录来举例
var name nullstringerr := db.queryrow(select name from names where id = ?, id).scan(&name)...if name.valid { // use name.string} else { // value is null}
在这种情况下我们通常使用nullstring,但是有时候我们并不关心值是不是null,我们只需要吧他当一个空字符串来对待就行。这时候我们可以使用[]byte(null byte[]可以转化为空string) 或者 sql.rawbytes,
var col1, col2 []bytefor rows.next() { // scan the value to []byte err = rows.scan(&col1, &col2) if err != nil { panic(err.error()) // just for example purpose. you should use proper error handling instead of panic } // use the string value fmt.println(string(col1), string(col2))}
使用*sql.rawbytes
package mainimport ( database/sql fmt _ github.com/go-sql-driver/mysql)func main() { // open database connection db, err := sql.open(mysql, user:password@/dbname) if err != nil { panic(err.error()) // just for example purpose. you should use proper error handling instead of panic } defer db.close() // execute the query rows, err := db.query(select * from table) if err != nil { panic(err.error()) // proper error handling instead of panic in your app } // get column names columns, err := rows.columns() if err != nil { panic(err.error()) // proper error handling instead of panic in your app } // make a slice for the values values := make([]sql.rawbytes, len(columns)) // rows.scan wants '[]interface{}' as an argument, so we must copy the // references into such a slice // see http://code.google.com/p/go-wiki/wiki/interfaceslice for details scanargs := make([]interface{}, len(values)) for i := range values { scanargs[i] = &values[i] } // fetch rows for rows.next() { // get rawbytes from data err = rows.scan(scanargs...) if err != nil { panic(err.error()) // proper error handling instead of panic in your app } // now do something with the data. // here we just print each column as a string. var value string for i, col := range values { // here we can check if the value is nil (null value) if col == nil { value = null } else { value = string(col) } fmt.println(columns[i], : , value) } fmt.println(-----------------------------------) } if err = rows.err(); err != nil { panic(err.error()) // proper error handling instead of panic in your app }}
事务使用db.begin()来开启一个事务, 通过commit()和rollback()方法来关闭。
tx := db.begin() tx.rollback() tx.commit()
exec, query, queryrow and prepare 方法已经全部可以在tx上面使用。使用方法和在*sql.db是一样的,事务必须以commit()或者rollback()结束
the connection pool在database/sql中有一个很基本的连接池,你并没有多大的控制权,仅仅可以设置setmaxidleconns和setmaxopenconns,也就是最大空闲连接和最大连接数。
db.setmaxidleconns(n) db.setmaxopenconns(n)
