go里面有个database/sql的包,里面定义所以连接操作数据库的方法,
并且原生就是支持连接池的,是并发安全的。
这个标准库没有具体的实现,只是列出了需要第三方库实现的具体内容。
下载驱动
go get github.com/go-sql-driver/mysql
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func main() {
// 数据库信息
dsn := "root:root@tcp(127.0.0.1:3306)/community"
// 连接数据库
_, err := sql.Open("mysql", dsn)
if err != nil {
fmt.Printf("open %s failed,err:%v\n", dsn, err)
return
}
fmt.Println("数据库连接成功")
}
我们可以将这个代码进行抽取成一个初始化的功能,然后后面查询和插入
var db *sql.DB // 是一个连接池对象
func initDB() (err error) {
// 数据库信息 用户名:密码@(本机:3306)/具体的数据库
dsn := "root:root@tcp(127.0.0.1:3306)/junmu"
// 连接数据库 ,这个db变量注意使用全局的变量,不要使用自己进行定义的。
db, err = sql.Open("mysql", dsn)
if err != nil {
return
}
err = db.Ping() // 尝试连接数据库
if err != nil {
return
}
return
}
type user struct {
id int
age int
name string
}
func queryRow(id int){
sqlStr := "select id,name,age from user where id = ?"
var u user
err := db.QueryRow(sqlStr,id).Scan(&u.id,&u.name,&u.age)
if err != nil {
fmt.Printf("queryRow failed! err:%v",err)
}
fmt.Println("id:",u.id," name:",u.name, " age:",u.age)
}
// 根据id进行多行数据范围查询
func query(id int) {
sqlStr := "select id,name,age from user where id < ?"
rows,err := db.Query(sqlStr,id)
if err != nil {
fmt.Printf("query rows failed! err:%v\n",err)
return
}
defer rows.Close() // 关闭并且释放数据库的资源
// 循环遍历的进行数据的查询
for rows.Next() {
var u user
err := rows.Scan(&u.id,&u.name,&u.age)
if err != nil {
fmt.Printf("rowsNext query failed! err:%v\n",err)
return
}
fmt.Println("id:",u.id," name:",u.name, " age:",u.age)
}
}
插入数据
func (db *DB) Exec(query string, args ...interface{}) (Result, error)
func insertRow(name string,age int){
sqlStr := "insert into user(name,age) values(?,?)"
ans ,err := db.Exec(sqlStr,name,age)
if err != nil {
fmt.Printf("insertRow failed! err:%v\n",err)
return
}
theID,err := ans.LastInsertId() // 获取新插入数据的id
if err != err {
fmt.Printf("id query failed! err:%v\n",err)
return
}
fmt.Printf("insert success! the id is %d.\n",theID)
}
更新操作(修改)
func updataRow(id,age int){
sqlStr := "update user set age = ? where id = ?"
ret,err := db.Exec(sqlStr,age,id)
if err != nil {
fmt.Printf("updateRow failed! err:%v",err)
return
}
// 获取影响的函数
total,err := ret.RowsAffected()
if err != nil {
fmt.Printf("get RowsAffected failed! err:%v\n",err)
return
}
fmt.Println("update success! Affected rows:",total)
}
删除操作
// 删除数据操作
func deleteRow(id int){
sqlStr := "delete from user where id = ?"
ret, err := db.Exec(sqlStr,id)
if err != nil {
fmt.Printf("delete failed! err:%v\n",err)
return
}
ans,err := ret.RowsAffected()
if err != nil {
fmt.Printf("get RowsAffected failed! err:%v\n",err)
return
}
fmt.Println("delete success!! RowsAffected: ",ans)
}
数据库预处理
什么是预处理?
普通SQL语句执行过程:
预处理执行过程:
那么我们为什么要预处理呢?
Go实现预处理
// 预处理实现mysql插入
func prepareInsert() {
sqlStr := `insert into user(name,age) values(?,?)`
stmt,err := db.Prepare(sqlStr)
if err != nil {
fmt.Printf("Prepare failed! err :%v\n",err)
return
}
defer stmt.Close()
var m = map[string]int {
"科科儿子":30,
"涛子哥":20,
"啊鸡巴":88,
"水儿子":16,
"张飞":56,
"关羽":45,
"刘备":44,
}
// 数据进行遍历的插入
for k,v := range m {
stmt.Exec(k,v)
}
}
SQL注入问题
我们任何时候都不应该自己拼接SQL语句!
// 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)
}
sqlInjectDemo("xxx' or 1=1#")
sqlInjectDemo("xxx' union select * from user #")
sqlInjectDemo("xxx' and (select count(*) from user) <10 #")
总结