1.MySql常用引擎介绍
1.MyIasm引擎
2.Innodb引擎
2.Golang中MYSQL驱动
1.依赖安装
go get github.com/go-sql-driver/mysql
2.导入驱动
import (
"database/sql"
"github.com/go-sql-driver/mysql"
)
3.连接数据库
func main() {
dsn := "user:password@tcp(127.0.0.1:3306)/test"
db, err := sql.Open("mysql", dsn)
if err != nil {
panic(err)
return
}
defer db.Close()
}
4.创建表
CREATE TABLE `user`(
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT '',
`age` int(11) DEFAULT 0, PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
5.sql查询
1.单行查询:Db.QueryRow
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql" // 注册驱动 sql.Register("mysql", &MySQLDriver)
)
var DB *sql.DB
func initDb() error {
var err error
dsn := "admin:password@tcp(139.224.199.152:3306)/test"
DB, err = sql.Open("mysql", dsn)
if err != nil {
return err
}
DB.SetMaxOpenConns(100) // 设置最大连接数(16个连接不够用时,可以扩展到100个)
DB.SetMaxIdleConns(16) // 空闲连接(没有请求或请求数量没达到16个时,会默认开启16个连接)
return nil
}
type User struct {
Id int `db:"id"`
Name sql.NullString `db:"name"` // sql.NullString:mysql数据中该字段没有值时,sting会报错
Age int `db:"age"`
}
func testQueryData() {
sqlStr := "select id, name, age from user where id=?"
row := DB.QueryRow(sqlStr, 1) // 如果不调用Scan方法,则连接永远不会释放
var user User
err := row.Scan(&user.Id, &user.Name, &user.Age)
if err != nil {
fmt.Println("scan failed, err: ", err)
return
}
// user.Name.String
fmt.Println(user)
}
func main() {
err := initDb()
if err != nil {
fmt.Println("init db failed, err: ", err)
return
}
testQueryData()
}
2.多行查询:Db.Query
func testQueryMultiRow() {
sqlSql := "select id, name, age from user where id > ?"
rows, err := DB.Query(sqlSql, 10) // 返回一个结果集,数据处理完后,需要手动关闭
if err != nil {
fmt.Println("query failed, err: ", err)
return
}
// 重点关注,rows对象一定要关闭
defer func() {
if rows != nil {
rows.Close()
}
}()
// rows全部读取出后,Next()方法返回 false
for rows.Next(){ // 必须读取出rows中的所有数据,不然连接不会释放,导致无法关闭,新的连接也进不来,程序就会卡死
var user User
err := rows.Scan(&user.Id, &user.Name, &user.Age)
if err != nil {
fmt.Println("scan failed, err: ", err)
return
}
}
}
3.Mysql插入、更新和删除
Db.Exec(query, args...interfaceP{})(Result, error)
- 插入的主键
id获:Result.LastInsertId()
func testInsertData() {
sqlStr := "insert into user(name, age) values(?, ?)"
result, err := DB.Exec(sqlStr, "tom", 18)
if err != nil {
fmt.Println("insert failed, err: ", err)
return
}
// 返回当前插入数据的id
id, err := result.LastInsertId()
if err != nil {
fmt.Println("get last insert id failed, err: ", err)
return
}
fmt.Println("insert data success, id: ", id)
}
func testUpdateData() {
sqlStr := "update user set name = ? where id = ?"
result, err := DB.Exec(sqlStr, "lucy", 2)
if err != nil {
fmt.Println("update failed, err: ", err)
return
}
affected, err := result.RowsAffected()
if err != nil {
fmt.Println("get affected rows failed, err: ", err)
}
fmt.Println("update success", affected, "rows")
}
func testDeleteData() {
sqlStr := "delete from user where id = ?"
result, err := DB.Exec(sqlStr, 2)
if err != nil {
fmt.Println("delete failed, err: ", err)
return
}
affected, err := result.RowsAffected()
if err != nil {
fmt.Println("get affected rows failed, err: ", err)
}
fmt.Println("delete success", affected, "rows")
}
6.Mysql预处理
1.一般sql处理流程
- 1.客户端拼接好sql语句
- 2.客户端发送sql语句到mysql服务器
- 3.mysql服务器解析sql语句并执行,把执行结果返回给客户端
2.预处理流程
- 1.把sql分为两部分,命令部分和数据部分
- 2.首先把命令部分发送给mysql服务器,mysql服务器进行sql预处理
- 3.然后把数据部分发送给mysql服务器,mysql服务器进行占位符替换
- 4.mysql服务器执行sql语句并返回结果给客户端
3.Mysql预处理优势
- 1.同一条sql反复执行,性能会很高
- 2.避免sql注入问题
4.查询操做
- 1.
DB.Prepare(sql string)(*sql.Stmt, error)
- 2.
Stmt.Query()
func testPrepareQueryData() {
sqlStr := "select id, name, age from user where id > ?"
stmt, err := DB.Prepare(sqlStr)
if err != nil {
fmt.Println("prepare query failed, err: ", err)
}
rows, err := stmt.Query(0)
if err != nil {
fmt.Println("prepare failed, err: ", err)
return
}
// 需要关闭stmt和rows
defer func() {
if stmt != nil {
stmt.Close()
}
}()
defer func() {
if rows != nil {
rows.Close()
}
}()
for rows.Next(){ // 必须读取出rows中的所有数据,不然连接不会释放,导致无法关闭,新的连接也进不来,程序就会卡死
var user User
err := rows.Scan(&user.Id, &user.Name, &user.Age)
if err != nil {
fmt.Println("scan failed, err: ", err)
return
}
fmt.Println(user)
}
}
4.更新操做(插入、更新、删除)
- 1.
DB.Prepare(sql string)(*sql.Stmt, error)
- 2.
Stmt.Exec()
func testPrepareInsertData() {
sqlStr := "insert into user(name, age) values(?, ?)"
stmt, err := DB.Prepare(sqlStr)
if err != nil {
fmt.Println("prepare insert failed, err: ", err)
}
// 需要关闭stmt
defer func() {
if stmt != nil {
stmt.Close()
}
}()
result, err := stmt.Exec("tom", 18)
if err != nil {
fmt.Println("insert failed, err: ", err)
return
}
// 返回当前插入数据的id
id, err := result.LastInsertId()
if err != nil {
fmt.Println("get last insert id failed, err: ", err)
return
}
fmt.Println("insert data success, id: ", id)
}
7.Mysql开发
1.Mysql事务
2.事务的ACID
3.事务实例
- 1.
DB.Begin():开启一个事物
- 2.
DB.Commit():提交一个事物
- 3.
DB.Rollback():回滚一个事物
func testTrans() {
conn, err := DB.Begin()
if err != nil {
if conn != nil {
conn.Rollback()
}
fmt.Println("begin failed, err: ", err)
return
}
sqlStr := "update user set age = ? where id = ?"
_, err = conn.Exec(sqlStr, 23, 1)
if err != nil {
conn.Rollback()
fmt.Println("exec sql: ", sqlStr, "failed, err: ", err)
return
}
sqlStr = "update user set age = ? where id = ?"
_, err = conn.Exec(sqlStr, 24, 2)
if err != nil {
conn.Rollback()
fmt.Println("exec sql: ", sqlStr, "failed, err: ", err)
return
}
err = conn.Commit()
if err != nil {
conn.Rollback()
fmt.Println("commit failed, err: ", err)
return
}
}
8.sqlx库介绍与使用
1.使用简单
go get github.com/jmoiron/sqlx
2.支持多数据库:mysql\postgresql\oracle\sqlite
3.sqlx库介绍与使用
- 查询:
sqlx.DB.Get和sqlx.DB.Select
- 更新、插入、和删除:
sqlx.DB.Exec
- 事务:
sqlx.DB.Begin()、sqlx.DB.Rollback()、sqlx.DB.Commit()
4.单行查询
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql" // 注册驱动 sql.Register("mysql", &MySQLDriver)
"github.com/jmoiron/sqlx"
)
var DB *sqlx.DB
func initDb() error {
var err error
dsn := "admin:password@tcp(139.224.199.152:3306)/test"
DB, err = sqlx.Open("mysql", dsn)
if err != nil {
return err
}
DB.SetMaxOpenConns(100) // 设置最大连接数(16个连接不够用时,可以扩展到100个)
DB.SetMaxIdleConns(16) // 空闲连接(没有请求或请求数量没达到16个时,会默认开启16个连接)
return nil
}
type User struct {
Id int `db:"id"`
Name sql.NullString `db:"name"` // 尽量数据库层面约束字段不能为空
Age int `db:"age"`
}
func testQueryData() {
var user User
sqlStr := "select id, name, age from user where id=?"
err := DB.Get(&user, sqlStr, 1) // 如果不调用Scan方法,则连接永远不会释放
if err != nil {
fmt.Println("scan failed, err: ", err)
return
}
fmt.Println(user)
}
func main() {
err := initDb()
if err != nil {
fmt.Println("init db failed, err: ", err)
return
}
testQueryData()
}
5.多行查询
func testQueryMultiData() {
var users []User
sqlStr := "select id, name, age from user where id > ?"
err := DB.Select(&users, sqlStr, 0) // 如果不调用Scan方法,则连接永远不会释放
if err != nil {
fmt.Println("scan failed, err: ", err)
return
}
fmt.Println(users)
}
6.更新
func testUpdate() {
sqlStr := "update user set name = ? where id = ?"
result, err := DB.Exec(sqlStr, "黄飞鸿", 1)
if err != nil {
fmt.Println("update failed, err: ", err)
return
}
affected, errs := result.RowsAffected()
if errs != nil {
fmt.Println("get affected rows failed, err: ", errs)
return
}
fmt.Println("update success", affected, "rows")
}
9.sql注入
- 1.
select * from user where name='%s',构造:name="1' or 1 = 1 #"
func queryDb(name string) {
sqlStr := fmt.Sprintf("select id, name, age from user where name = '%s' ", name)
var users []User
err := DB.Select(&users, sqlStr)
if err != nil {
fmt.Println("select failed, err: ", err)
return
}
for _, v := range users {
fmt.Printf("user: %#v\n", v)
}
}
func testSqlInject() {
queryDb("tom")
/*
// 查询语句
select id, name, age from user where name = 'tom'
// 查询结果
user: main.User{Id:2, Name:sql.NullString{String:"tom", Valid:true}, Age:0}
*/
queryDb("tom' or 1 = 1 #") // 最后这个#号是为了注释掉后面的闭合单引号 '
/*
// 查询语句
select id, name, age from user where name = 'tom' or 1 = 1 #'
// 查询结果
user: main.User{Id:1, Name:sql.NullString{String:"黄飞鸿", Valid:true}, Age:23}
user: main.User{Id:2, Name:sql.NullString{String:"tom", Valid:true}, Age:0}
*/
}
- 2.构造
name=123' and (select count(*) from user) > 10#
queryDb("name=tom' and (select count(*) from user) < 10#")
- 3.g构造
name=123' union select * from user #
queryDb("123' union select * from user #")
queryDb("name=123' union select * from user #")
/*
select id, name, age from user where name = '123' union select * from user #'
select id, name, age from user where name = 'name=123' union select * from user #'
user: main.User{Id:1, Name:sql.NullString{String:"黄飞鸿", Valid:true}, Age:23}
user: main.User{Id:2, Name:sql.NullString{String:"tom", Valid:true}, Age:0}
/*