1. SQL预处理的使用

使用场景:批量执行sql语句,可以提高查询速度

为什么要预处理 ?

1)优化MySQL服务器重复执行SQL的方法,可以提升服务器性能,提前让服务器编译,一次编译多次执行,节省后续编译的成本

2)避免SQL注入问题

package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"time"
)
var db *sql.DB

func initMySQL()(err error){
	dsn := "root:root@tcp(127.0.0.1:13306)/go_test"
	//去初始化全局的db对象,而不是新声明一个变量
	db,err = sql.Open("mysql",dsn)
	if err != nil {
		panic(err)
	}
	//做完错误检查后,确保db不为nil

	//尝试与数据库建立连接(校验dsn是否正确)
	err = db.Ping()
	if err != nil {
		fmt.Printf("connect to db failed,err: %v\n",err)
		return err
	}
	//根据实际业务设置数值
	db.SetConnMaxLifetime(time.Second*10) // 设置连接可以重复使用的最长时间
	db.SetMaxOpenConns(500) // 最大连接数
	db.SetMaxIdleConns(200) // 最大空闲连接数
	return
}
type user struct {
	id int
	age int
	name string
}

//预处理查询示例
func prepareQueryDemo() {
	// Prepare创建一个准备好的用于之后的查询和命令。返回值可以同时执行多个查询和命令
	sqlStr := "SELECT id,name,age FROM user WHERE id>?"
	stmt, err := db.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("prepare failed, err:%v\n",err)
		return
	}
	defer stmt.Close()
	rows, err := stmt.Query(0)
	if err != nil {
		fmt.Printf("query failed, err:%v\n",err)
		return
	}
	defer rows.Close()
	// 循环读取结果集中的数据
	for rows.Next() {
		var u user
		//需要调用Scan去释放数据库链接
		err := rows.Scan(&u.id,&u.name,&u.age)
		if err != nil {
			fmt.Printf("scan failed,err:%v\n",err)
			return
		}
		fmt.Printf("id:%d,name:%s,age:%d\n",u.id,u.name,u.age)
	}
}
func main() {
	if err := initMySQL();err != nil {
		fmt.Printf("connect to db failed,err:%v\n",err)
	} else {
		fmt.Println("db connect to db success")
	}
	// Close() 用来释放数据库连接的相关资源
	defer db.Close()
	prepareQueryDemo()
}

2.SQL注入问题

注意:我们任何时候都不应该自己拼接SQL语句

#符号为注释符,忽略后面的sql语句,#为mysql特有的注释符

应该用 -- 

package main
import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"time"
)
var db *sql.DB

func initMySQL()(err error){
	dsn := "root:root@tcp(127.0.0.1:13306)/go_test"
	//去初始化全局的db对象,而不是新声明一个变量
	db,err = sql.Open("mysql",dsn)
	if err != nil {
		panic(err)
	}
	//做完错误检查后,确保db不为nil

	//尝试与数据库建立连接(校验dsn是否正确)
	err = db.Ping()
	if err != nil {
		fmt.Printf("connect to db failed,err: %v\n",err)
		return err
	}
	//根据实际业务设置数值
	db.SetConnMaxLifetime(time.Second*10) // 设置连接可以重复使用的最长时间
	db.SetMaxOpenConns(500) // 最大连接数
	db.SetMaxIdleConns(200) // 最大空闲连接数
	return
}
type user struct {
	id int
	age int
	name string
}
// sql注入示例
func sqlInjectDemo(name string) {
	sqlStr := fmt.Sprintf("select id, name, age from user where name='%s'", name)
	fmt.Printf("SQL:%s\n", sqlStr)
	var u user
	err := db.QueryRow(sqlStr).Scan(&u.id, &u.name, &u.age)
	if err != nil {
		fmt.Printf("exec failed, err:%v\n", err)
		return
	}
	fmt.Printf("user:%#v\n", u)
}
func main() {
	if err := initMySQL();err != nil {
		fmt.Printf("connect to db failed,err:%v\n",err)
	} else {
		fmt.Println("db connect to db success")
	}
	// Close() 用来释放数据库连接的相关资源
	defer db.Close()
	sqlInjectDemo("xxxx' or 1=2#")
}