sql和连接池

参考

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()  可以创建一个连接

 

  • db.Ping() 调用完毕后会马上把连接返回给连接池。
  • db.Exec() 调用完毕后会马上把连接返回给连接池,但是它返回的Result对象还保留这连接的引用,当后面的代码需要处理结果集的时候连接将会被重用。
  • db.Query() 调用完毕后会将连接传递给sql.Rows类型,当然后者迭代完毕或者显示的调用.Clonse()方法后,连接将会被释放回到连接池。
  • db.QueryRow()调用完毕后会将连接传递给sql.Row类型,当.Scan()方法调用之后把连接释放回到连接池。
  • db.Begin() 调用完毕后将连接传递给sql.Tx类型对象,当.Commit()或.Rollback()方法调用后释放连接。

 

 

 

数据库查询的一般步骤如下:

  1. 调用 db.Query 执行 SQL 语句, 此方法会返回一个 Rows 作为查询的结果
  2. 通过 rows.Next() 迭代查询数据.
  3. 通过 rows.Scan() 读取每一行的值
  4. 调用 db.Close() 关闭查询
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/

你可能感兴趣的:(go)