前言

学了很多Golang的基础语法和零碎知识,总感觉无法把Golang像Python一样灵活运用到实际项目开发之中。

 

 

Gin框架源码解析

Gin框架路由详解

 Gin web实战

 

 

Gin框架中间详解 

 

 

 

 

Go操作MySQL数据库

1.database/sql操作MySQL

在golang内置了1个database/sql包,database/sql提供了连接数据库的泛用的接口,但并不提供具体数据库驱动。

所以使用database/sqx包时必须注入(至少)一个数据库驱动。

如果我们需要连接不同的数据就需要基于database/sqlx的数据库驱动,下载不同的数据驱动。

我们常用的数据库基本上都有完整的第三方实现。例如:MySQL驱动

公共的接口+不同的实现插件,这就是1个接口思想。

 

2.mysql数据库和表创建

MariaDB [(none)]> create database web default charset=utf8;

创建user表

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;

 

3.下载MySQL驱动

go get -u github.com/go-sql-driver/mysql

 

3.sql.Open()使用驱动

sql.Open会返回1个db对象,db对象维护了1个数据库连接池,并发安全。

func UserDriver()(err error){
    rds:="zhanggen:123.com@tcp(192.168.56.18:3306)/web"
    db,err= sql.Open("mysql", rds)
    //做完错误检查之后再进行defer db.close()
    if err!=nil{
        return
    }
    //确保sql.Open()返回的不是空指针!
    defer db.Close()
    return
}

 

4.db.Ping()测试数据库连接

sql.Open()不会去真正连接数据库,我们可以使用db.ping尝试与数据建立连接。

//全局连接池对象
var db *sql.DB

func InitDB() (err error) {
    dns := "zhanggen:123.com@tcp(192.168.56.18:3306)/web"
    //Open函数只是校验其参数格式是否正确?实际并不会创建数据库连接!
    db, err = sql.Open("mysql", dns)
    if err != nil {
        return
    }
    //db有可能返回Nil
    //做完了错误检查之后,再defer,db.close释放掉数据库连接资源,确保db不为nill
    //defer db.Close()
    //尝试与数据库建立连接
    err = db.Ping()
    if err!=nil{
        fmt.Printf("连接数据库失败%v\n",err)
        return
    }
    //连接最长的时间
    db.SetConnMaxLifetime(time.Second*10)
    //最大连接数
    db.SetMaxOpenConns(200)
    //最大闲置连接
    db.SetMaxIdleConns(10)
    return

}

 

5.db.QueryRow查询单条记录

注意在我查询的数据的使用了QueryRow就相当于在连接池中获取了1个数据库连接,要使用Scan方法对当前进行释放

package main

import (
    "database/sql"
    "fmt"
    _ "github.com/go-sql-driver/mysql" //自动初始化执行mysql包中的init()
    "time"
)
//全局连接池对象
var db *sql.DB

func InitDB() (err error) {
    dns := "zhanggen:123.com@tcp(192.168.56.18:3306)/web"
    //Open函数只是校验其参数格式是否正确?实际并不会创建数据库连接!
    db, err = sql.Open("mysql", dns)
    if err != nil {
        return
    }
    //db有可能返回Nil
    //做完了错误检查之后,再defer,db.close释放掉数据库连接资源,确保db不为nill
    //defer db.Close()
    //尝试与数据库建立连接
    err = db.Ping()
    if err!=nil{
        fmt.Printf("连接数据库失败%v\n",err)
        return
    }
    //连接最长的时间
    db.SetConnMaxLifetime(time.Second*10)
    //最大连接数
    db.SetMaxOpenConns(200)
    //最大闲置连接
    db.SetMaxIdleConns(10)
    return

}
type user struct {
    id int
    name string
    age  int
}

func main() {
    if err := InitDB(); err != nil {
        fmt.Printf("connect to db faild,err:%v\n", err)

    }
    defer db.Close()
    sqlStr:="select id,name,age from user where id=?"
    var u user
    //注意:每1次QueryRow之后都要scan把当前连接释放掉!否则会一直占用单前连接!
    row:=db.QueryRow(sqlStr,1)
    //相当于直接传了3个变量,如果不直接传1个结构体字段名无需大写!
    err := row.Scan(&u.id, &u.name, &u.age)

    if err!=nil{
        fmt.Println(err)
        return
    }
    fmt.Printf("id:%d name:%s age:%d\n",u.id,u.name,u.age)

}

 

5.db.Query()查询多条记录

//查询多条记录
func querymultiple() {
    sqlString := "select id,name,age from user limit 10;"
    rows, err := db.Query(sqlString)
    if err != nil {
        fmt.Println(err)
        return
    }
    var userList []user
    //防止for循环过程出错!提前注册关闭!
    defer rows.Close()
    //循环获取结果集中的数据,知道Rows.Next()=flase
    for rows.Next() {
        var u user
        err := rows.Scan(&u.id, &u.name, &u.age)
        if err != nil {
            return
        }
        userList = append(userList, u)

    }
    fmt.Println(userList)
}

 

7.db.Exec()

db.Exec()可以对数据库进行insert、update、delete操作。

 

insert

插入操作可以通过db.Exec()执行之后返回的结果描述中获取LastInsertId

func insertRow() {
    sqlString := "insert into user(name,age)values(?,?)"
    ret, err := db.Exec(sqlString, "张根", 25)
    if err != nil {
        fmt.Println(err)
        return
    }
    id, err := ret.LastInsertId()
    if err != nil {
        fmt.Println("获取最后一条插入记录的ID失败!", err)
    }
    fmt.Printf("插入成功,最新记录的id为%d\n", id)
}

 

update

我们在使用db.Exec()执行update操作时,会返回1个RowsAffected,也就是受影响的行数。

func updateRow(){
    sqlSring:="update user set age=? where id=?;"
    ret,err := db.Exec(sqlSring,27,3)
    if err!=nil{
        fmt.Println("更新数据失败",err)
        return
    }
    //受影响的行数
    n,err:=ret.RowsAffected()
    if err!=nil{
        fmt.Println(err)
    }
    if n==0{
        fmt.Println("未更新!")
        return
    }
    fmt.Printf("更新%d条数据\n",n)

}

 

delete

我们在使用db.Exec()执行delete操作时,会返回RowsAffected(受影响的行数),仅SQL语句不同。

func deleteRow() {
    sqlString := "delete from user where id=?;"
    ret, err := db.Exec(sqlString, 4)
    if err != nil {
        fmt.Println("执行删除失败", err)
        return
    }
    //受影响的行数!!
    var affectedRow int64
    affectedRow, err = ret.RowsAffected()
    if err != nil {
        fmt.Println(err)
        return
    }
    if affectedRow == 0 {
        fmt.Println("删除未成功!", affectedRow)
        return
    }
    fmt.Printf("删除%d数据\n", affectedRow)

}

 

8.db.Prepare(sql)SQL预处理

SQL预处理有2大好处

1.避免SQL注入问题。

func prepareQuery(){
    sql:="select id,name,age from user where name=?"
    //1.把SQL先发送到MySQL-server端去预处理
    prepared,err := db.Prepare(sql)
    if err!=nil{
        fmt.Printf("SQL预处理失败%v",err)
    }
    //3.最后记得关闭连接
    defer prepared.Close()
    //2.把sql参数发送到MySQL-server端进行拼接
    row:= prepared.QueryRow("Tom")
    u:=new(user)
    row.Scan(&u.id,&u.name,&u.age)
    fmt.Println(u)
}

2.SQL预处理是优化MySQL服务器频繁执行重复SQL的方法,可以提升服务器性能,提前让服务器编译,服务端一次编译多次执行,节省后续编译的成本

我们在做监控系统时,数据的写入不能延迟,所以无法使用批量插入,可以使用SQL预编译来进行优化。

func prepareInsert() {
    sqlString := "insert into user(name,age)values(?,?);"
    preparedStatement, err := db.Prepare(sqlString)
    if err != nil {
        fmt.Println("sql预处理失败", err)
        return
    }
    defer preparedStatement.Close()
    //实时插入
    preparedStatement.Exec("Amy", 19)
    preparedStatement.Exec("Jack", 89)
    preparedStatement.Exec("Foina", 29)
}

 

9.事务操作

Go语言中使用以下三个方法实现MySQL中的事务操作。

开始事务

begin, err := db.Begin()

提交事务

begin.Commit()

 

回滚事务

begin.Rollback()

代码

func transactionDemo() {
    //1.开启事务
    begin, err := db.Begin()
    if err != nil {
        if begin != nil {
            //事务回滚
            begin.Rollback()
        }
        fmt.Println("事务操作开始失败", err)
        return
    }
    sql1 := "update user set age=59 where id=?"
    _, err = begin.Exec(sql1, 1)
    if err != nil {
        //事务回滚
        begin.Rollback()
        fmt.Println("事务操作时执行SQL1失败!", err)
        return
    }
    sql2 := "update user set age=60 where id=?"
    _, err = begin.Exec(sql2, 2)
    if err != nil {
        //事务回滚
        begin.Rollback()
        fmt.Println("事务操作时执行SQL2失败", err)
        return
    }
    //2.提交事务
    err = begin.Commit()
    if err != nil {
        //事务回滚
        begin.Rollback()
        fmt.Println("执行事务操作提交事务时失败", err)
        return
    }

}

 

使用sqlx操作数据

sqlx是基于内置database/sql包扩展出来的超集,它的功能更加强大,更加易用。官网

 

1.sqlx.Connect()

连接数据库:sqlx是基于database/sql进行的扩展,它的connect()就是对sql.Open()和sql.Ping()的封装。

func initDB() (err error) {
    //parseTime=True解析时间类型
    dsn := "zhanggen:123.com@tcp(192.168.56.18:3306)/web?charset=utf8mb4&parseTime=True"
    //Connect()=Open()+Ping()
    db, err = sqlx.Connect("mysql", dsn)
    if err != nil {
        fmt.Println("数据库连接失败", err)
        return
    }
    //设置连接池参数
    db.SetConnMaxLifetime(time.Second)
    db.SetMaxIdleConns(10)
    return

}

 

2.db.Get(&u, sqlString,参数)

查询单条数据

func queryOne(){
    sqlString:="select id,name,age from user where id=?;"
    var u user
    //&u:查询结果要赋值到的结构体,sql语句,sql的参数
    err := db.Get(&u, sqlString,1)
    if err!=nil{
        fmt.Println("查询数据失败",err)
        return
    }
    fmt.Println(u)
}

 

3.db.Select(&users, sqlString, 10)

查询多条数据

func queryMutil(){
    sqlString:="select id,name,age from user limit ?"
    var users []user
    //不需要for rows.Next()逐一进行Scan获取了!
    err := db.Select(&users, sqlString, 10)
    if err!=nil{
        fmt.Println(err)
        return
    }
    fmt.Printf("%#v\n",users)

}

 

4.db.Exec()

我们可以使用db.Exec()完成对数据库的insert/update/delete操作。

func InsertOne() {
    sqlStr := "insert into user(name,age)values(?,?);"
    ret, err := db.Exec(sqlStr, "郑爽", 28)
    if err != nil {
        fmt.Printf("数据插入失败%v\n", err)
        return
    }
    var id int64
    id, err = ret.LastInsertId()
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(id)
}
insert

相关文章:

  • 2021-12-21
  • 2021-09-02
  • 2021-08-16
  • 2022-12-23
  • 2021-06-27
  • 2022-12-23
猜你喜欢
  • 1970-01-01
  • 2022-12-23
  • 2021-09-24
  • 2021-07-06
  • 2022-12-23
  • 2019-08-18
  • 2022-12-23
相关资源
相似解决方案