Go语言Mysql

1.MySql常用引擎介绍

1.MyIasm引擎

  • 1.不支持事物
  • 2.不支持行锁
  • 3.读写性能较好

2.Innodb引擎

  • 1.支持事物
  • 2.支持行锁
  • 3.整体性能比较好

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事务
  • 1.同时更新多个表
  • 2.同时更新多行数据
2.事务的ACID
  • 1.原子性
  • 2.一致性
  • 3.隔离性
  • 4.持久性
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.Getsqlx.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}
/*
posted @ 2022-09-30 17:25  fatpuffer  阅读(79)  评论(0)    收藏  举报