参考
https://www.jianshu.com/p/340eb943be2e
https://www.jianshu.com/p/50c9fbf4046c
首先是导包
import "database/sql"
import _ "github.com/go-sql-driver/mysql"
_的作用是只执行init函数
值得注意的是他内部自己已经实现了连接池
db.SetMaxOpenConns(2) 设置最大连接数 该函数的默认设置是0,表示无限制
db.SetMaxIdleConns(n int) 设置连接池中的保持连接的最大连接数。默认也是0,表示连接池不会保持释放会连接池中的连接的连接状态:即当连接释放回到连接池的时候,连接将会被关闭。这会导致连接再连接池中频繁的关闭和创建。
链接数据库
db,err := sql.Open("mysql","root:密码@tcp(www.wsj0819.cn:3306)/test")
采用的是延迟连接。真正调用时才会创建连接
通过 err = db.Ping() 可以创建一个连接
数据库查询的一般步骤如下:
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"log"
)
func getConn() (*sql.DB,error) {
db,err := sql.Open("mysql","root:×××××@tcp(www.wsj0819.cn:3306)/test")
//db.SetMaxOpenConns(2)
if err != nil {
log.Fatalln(err)
return nil,err
}
err = db.Ping()
if err != nil {
log.Fatalln(err)
return nil,err
}
log.Println("connection success")
return db,nil
}
func main() {
db,err := getConn()
if err != nil {
fmt.Println(err)
return
}
defer db.Close()
rows,err := db.Query("select * from user")
type user struct {
Id int
name string
}
users := []user{}
for rows.Next(){
u := user{}
err := rows.Scan(&u.Id,&u.name)
if err != nil {
log.Fatalln(err)
}
users = append(users,u)
}
rows.Close()
fmt.Println(users)
}
当数据库有null的字段时,类型可采用sql.NullString
name sql.NullString
输出 [{1 {q true}} {2 {w true}} {3 { false}}]
u.name.String可以获取到他的值
name []byte 如果采用字节数组来接受的话,则可以兼容null
还可以自定义一个方法,将查询结果存入一个map切片,key是字段名
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"log"
)
func getConn() (*sql.DB,error) {
db,err := sql.Open("mysql","root:×××××@tcp(www.wsj0819.cn:3306)/test")
//db.SetMaxOpenConns(2)
if err != nil {
log.Fatalln(err)
return nil,err
}
err = db.Ping()
if err != nil {
log.Fatalln(err)
return nil,err
}
log.Println("connection success")
return db,nil
}
func main() {
db,err := getConn()
if err != nil {
fmt.Println(err)
return
}
defer db.Close()
rows,err := db.Query("select * from user")
columns,_ := rows.Columns()
values := make([]sql.RawBytes,len(columns))
valuesAddr := make([]interface{},len(columns))
results := []map[string]string{}
//false
//for i,addr := range values {
// valuesAddr[i] = &addr
//}
for i,_ := range values {
valuesAddr[i] = &values[i]
}
for rows.Next(){
err := rows.Scan(valuesAddr...)
if err != nil {
log.Fatalln(err)
}
result := make(map[string]string)
for i,v := range values{
result[columns[i]] = string(v)
}
results = append(results,result)
}
rows.Close()
for k,v := range results{
fmt.Println(k,v)
}
}
查询数量的封装
//查询数量
func (conn *MysqlConn)QueryCount(sql string, args ...interface{}) (int32,error) {
row := conn.db.QueryRow(sql,args...)
var count int
err := row.Scan(&count)
if err != nil {
return -1,err
}
return int32(count),nil
}
事物的操作
package main
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"log"
)
func main() {
db,err := sql.Open("mysql","root:123456@tcp(www.wsj0819.cn:3306)/test")
if err != nil {
log.Fatalln(err)
}
defer db.Close()
tx,_ := db.Begin()
//db.Exec("insert into num (num) values (?)",777) //不能用db调用 应该需要tx调用
//Tx建立的时候就和一个连接绑定了,所以这些操作内部共用一个TX内部的连接。
tx.Exec("insert into num (num) values (?)",777)
tx.Rollback()
}
特别注意,开启事物后,不能再通过db去操作,而是应该通过tx
通过db执行,他会每次都从连接池去获取链接,二tx则是有一个链接绑定在上面。
由此还可以得到一点,就是当执行大量的插入更新操作时,最好通过tx去操作,而不是db,这样就避免了每次都去获取连接
for i := 1001;i<=1100;i++{
//每次循环内部都会去连接池获取一个新的连接,效率低下
db.Exec("INSERT INTO user(uid,username,age) values(?,?,?)",i,"user"+strconv.Itoa(i),i-1000)
}
tx,_ := db.Begin()
for i := 1301;i<=1400;i++{
//每次循环用的都是tx内部的连接,没有新建连接,效率高
tx.Exec("INSERT INTO user(uid,username,age) values(?,?,?)",i,"user"+strconv.Itoa(i),i-1000)
}
//最后释放tx内部的连接
tx.Commit()
关于sql注入的问题 可以参考
https://michaelyou.github.io/2018/03/30/database-sql-%E4%B8%80%E7%82%B9%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3/