• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

Go语言基础之16--Mysql基本操作

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

1.1 Golang中的Mysql驱动

A. https://github.com/go-sql-driver/mysql

B. Go本身不提供具体数据库驱动,只提供驱动接口和管理。(官方只是封装了接口并没有提供具体的实现)

C. 各个数据库驱动需要第三方实现,并且注册到Go中的驱动管理中。

1.2 Mysql驱动,注册示例

 

解释:

其实就是把mysql驱动注册到golang官方的接口中,然后在通过golang的接口去操作mysql具体实例了。驱动的名字叫mysql。

1.3 导入Mysql驱动

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)

 解释:

可能会好奇为什么导入"database/sql"后,为什么导入mysql驱动却要加"_"来忽略掉呢?其实真正的原因是因为init函数的原因,下面通过一个实例来了解一下:

写一个自定义的包:

目录结构如下:

custom_pkg:

custom.go:

package custom_pkg

import (
    "fmt"
)

var Count int

func init() {  
    fmt.Printf("hello world, i'm in init function\n")
}

custom2.go:

package custom_pkg

import (
    "fmt"
)

func init() {
    fmt.Printf("hello world, i'm in init function custom2\n")
}

custom_example:

main.go:

package main

import (
    "6/After_class/mysql/custom_pkg"
    "fmt"
)

func main() {
    fmt.Printf("count=%d\n", custom_pkg.Count)
}

执行结果如下:

可以发现执行的结果是将导入的包的init函数执行了。

总结:葵花宝典

init函数名字是固定的,没有参数也没有返回值,其是包用来做初始化的,会自动执行(在包导入的时候执行),在一个包中这个函数可以有多个。所以之后我们就可以利用init函数做一些包初始化的逻辑。

了解:init函数执行顺序,先执行导入的包的init函数,在执行当前包的init函数。

所以说在倒入mysql驱动时为什么要加"_"来忽略掉呢?就是因为想要执行init函数。

1.4 连接数据库

先安装mysql驱动:

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

连接实例如下:

package main

import (
    "database/sql"
    "fmt"

    _ "github.com/go-sql-driver/mysql"
)

func main() {
    dsn := "root:123456@tcp(127.0.0.1:3306)/golang" //连接数据库的配置
    Db, err := sql.Open("mysql", dsn)               //Open函数第一个参数就是驱动的名字(不能随意写的)
    if err != nil {                                 //有一个坑,如果连接数据库配置写错了,不会在此处报错
        fmt.Printf("open mysql is failed,err:%v\n", err)
        return
    }

    err = Db.Ping() //ping一下没报错证明连接数据库成功
    if err != nil {
        fmt.Printf("ping is failed,err:%v\n", err)
        return
    }

    fmt.Printf("connect to db successful\n")
}

执行结果:

二、Mysql数据基本操作

首先准备环境先创建一张表

建表语句如下:
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;

2.1 sql查询

A. 单行查询, Db.QueryRow

B 多行查询, Db.Query

 

实例1:单行查询  Db.QueryRow

package main

import (
    "database/sql"
    "fmt"

    _ "github.com/go-sql-driver/mysql"
)

type User struct { //为要查询的表定义一个结构体(数据库定义的表要有1个结构体来封装,否则会十分混乱)
    Id   int64  `db:"id"` //数据库一般都是小写,所以我们需要借助tag来映射(结构体中字段名写什么就无所谓了,反正要做映射)
    Name string `db:"name"`
    Age  int    `db:"age"`
}

func QueryRow(Db *sql.DB) {
    id := 1
    row := Db.QueryRow("select id,name,age from user where id=?", id) //?是占位符,可以传入数值拼接成完整的sql语句发给mysql服务器,然后mysql返回数据集。

    var user User
    err := row.Scan(&user.Id, &user.Name, &user.Age) //Scan这里查询有2种错误 1、查询不到数据失败(网络原因等);2、查询过程中查询失败(和因为网络原因造成的错误区分开)
    if err == sql.ErrNoRows {  //对应第二种错误 ErrNoRows表示查询过程中是没发生错误的
        fmt.Printf("not found data of id:%d\n", id)
        return
    }

    if err != nil { //对应第一种错误
        fmt.Printf("scan failed, err:%v\n", err)
        return
    }

    fmt.Printf("user:%#v\n", user)

}

func main() {
    dsn := "root:123456@tcp(127.0.0.1:3306)/golang"
    Db, err := sql.Open("mysql", dsn)
    if err != nil {
        fmt.Printf("open mysql is failed,err:%v\n", err)
        return
    }

    err = Db.Ping()
    if err != nil {
        fmt.Printf("ping is failed,err:%v\n", err)
        return
    }

    fmt.Printf("connect to db successful\n")

    QueryRow(Db)
}

 执行结果:

 

实例2:多行查询, Db.Query

package main

import (
    "database/sql"
    "fmt"

    _ "github.com/go-sql-driver/mysql"
)

type User struct {
    Id   int64  `db:"id"`
    Name string `db:"name"`
    Age  int    `db:"age"`
}

func Query(Db *sql.DB) {
    id := 0
    rows, err := Db.Query("select id, name, age from user where id>?", id)
    //rows返回的是1个结果集,这个rows结果集,用完之后,一定释放!rows.Close() 这个是消耗数据库连接资源的。
    defer func() {
        if rows != nil {
            rows.Close()
        }
    }()

    if err == sql.ErrNoRows {
        fmt.Printf("not found data of id:%d\n", id)
        return
    }

    if err != nil {
        fmt.Printf("scan failed, err:%v\n", err)
        return
    }

    for rows.Next() { //多行数据通过写一个for循环来进行处理,有数据返回true接着执行,没数据返回false就退出 Next就如同一个游标,查到最后就会返回一个false,就查询结束退出循环了。
        var user User
        err := rows.Scan(&user.Id, &user.Name, &user.Age)
        if err != nil {
            fmt.Printf("scan failed, err:%v\n", err)
            return
        }

        fmt.Printf("user:%#v\n", user)
    }

}

func main() {
    dsn := "root:123456@tcp(127.0.0.1:3306)/golang"
    Db, err := sql.Open("mysql", dsn)
    if err != nil {
        fmt.Printf("open mysql is failed,err:%v\n", err)
        return
    }

    err = Db.Ping()
    if err != nil {
        fmt.Printf("ping is failed,err:%v\n", err)
        return
    }

    fmt.Printf("connect to db successful\n")

    Query(Db)
}

执行结果如下:

2.2 Mysql 插入、更新和删除

 A. Db.Exec(query, args…interface{})(Result, error)

解释:插入、更新、删除都是Exec方法(sql语句,传入占位符对应的值)(返回结果集,错误)

B. 插入的主键id获取, Result. LastInsertId()

 

插入实例:

package main

import (
    "database/sql"
    "fmt"

    _ "github.com/go-sql-driver/mysql"
)

type User struct {
    Id   int64  `db:"id"`
    Name string `db:"name"`
    Age  int    `db:"age"`
}

func Insert(DB *sql.DB) {
    username := "user01"
    age := 18

    result, err := DB.Exec("insert into user(name, age)values(?, ?)", username, age)
    if err != nil {
        fmt.Printf("exec failed, err:%v\n", err)
        return
    }

    id, err := result.LastInsertId()  //获取最后一次插入的id
    if err != nil {
        fmt.Printf("last insert id failed, err:%v\n", err)
        return
    }

    affectRows, err := result.RowsAffected()  //此次操作影响的行数
    if err != nil {
        fmt.Printf("affectRows failed, err:%v\n", err)
        return
    }

    fmt.Printf("last insert id:%d affect rows:%d\n", id, affectRows)
}

func main() {
    dsn := "root:123456@tcp(127.0.0.1:3306)/golang"
    Db, err := sql.Open("mysql", dsn)
    if err != nil {
        fmt.Printf("open mysql is failed,err:%v\n", err)
        return
    }

    err = Db.Ping()
    if err != nil {
        fmt.Printf("ping is failed,err:%v\n", err)
        return
    }

    fmt.Printf("connect to db successful\n")

    Insert(Db)
}

执行结果:

 

 

更新实例:

package main

import (
    "database/sql"
    "fmt"

    _ "github.com/go-sql-driver/mysql"
)

type User struct {
    Id   int64  `db:"id"`
    Name string `db:"name"`
    Age  int    `db:"age"`
}

func Update(DB *sql.DB) {
    username := "user02"
    age := 108
    id := 3
    result, err := DB.Exec("update user set name=?, age=? where id=?", username, age, id)
    if err != nil {
        fmt.Printf("exec failed, err:%v\n", err)
        return
    }

    affectRows, err := result.RowsAffected()
    if err != nil {
        fmt.Printf("affectRows failed, err:%v\n", err)
        return
    }

    fmt.Printf("last update id:%d affect rows:%d\n", id, affectRows)
}

func main() {
    dsn := "root:123456@tcp(127.0.0.1:3306)/golang"
    Db, err := sql.Open("mysql", dsn)
    if err != nil {
        fmt.Printf("open mysql is failed,err:%v\n", err)
        return
    }

    err = Db.Ping()
    if err != nil {
        fmt.Printf("ping is failed,err:%v\n", err)
        return
    }

    fmt.Printf("connect to db successful\n")

    Update(Db)
}

执行结果:

 

数据库结果:

 

删除实例:

package main

import (
    "database/sql"
    "fmt"

    _ "github.com/go-sql-driver/mysql"
)

type User struct {
    Id   int64  `db:"id"`
    Name string `db:"name"`
    Age  int    `db:"age"`
}

func Delete(DB *sql.DB) {
    id := 3
    result, err := DB.Exec("delete from user where id=?", id)
    if err != nil {
        fmt.Printf("exec failed, err:%v\n", err)
        return
    }

    affectRows, err := result.RowsAffected()
    if err != nil {
        fmt.Printf("affectRows failed, err:%v\n", err)
        return
    }

    fmt.Printf("last delete id:%d affect rows:%d\n", id, affectRows)
}

func main() {
    dsn := "root:123456@tcp(127.0.0.1:3306)/golang"
    Db, err := sql.Open("mysql", dsn)
    if err != nil {
        fmt.Printf("open mysql is failed,err:%v\n", err)
        return
    }

    err = Db.Ping()
    if err != nil {
        fmt.Printf("ping is failed,err:%v\n", err)
        return
    }

    fmt.Printf("connect to db successful\n")

    Delete(Db)
}

执行结果如下:

 

数据库结果:

三、Mysql预处理

3.1 一般sql处理流程

1. 客户端拼接好sql语句

2. 客户端发送sql语句到mysql服务器

3. mysql服务器解析sql语句并执行, 把执行结果发送给客户端

3.2 预处理处理流程

1. 把sql分为两部分,命令部分和数据部分。

2. 首先把命令部分发送给mysql服务器, mysql进行sql预处理

3. 然后把数据部分发送给mysql服务器, mysql进行占位符替换

4. mysql服务器执行sql语句并返回结果给客户端

3.3 Mysql预处理优势

A. 同一条sql反复执行,性能会很高。

B. 避免sql注入问题

3.4 Mysql预处理实例

A. 查询操作,

1. Db.Prepare(sql string) (*sql.Stmt, error)   stmt可以暂且理解为语句

2. Stmt.Query()

B. 更新操作(插入、更新、 delete),

1. Db.Prepare(sql string) (*sql.Stmt, error)

2. Stmt.Exec()

 

查询操作预处理实例:

package main

import (
    "database/sql"
    "fmt"

    _ "github.com/go-sql-driver/mysql"
)

type User struct {
    Id   int64  `db:"id"`
    Name string `db:"name"`
    Age  int    `db:"age"`
}

func PrepareQuery(DB *sql.DB) {
    //预处理第1步:发送sql语句命令部分
    stmt, err := DB.Prepare("select id, name, age from user where id>?")
    if err != nil {
        fmt.Printf("prepare failed, err:%v\n", err)
        return
    }

    //预处理第2步:发送sql语句数据部分
    id := 1
    rows, err := stmt.Query(id)
    //这个rows结果集,用完之后,一定释放!rows.Close()
    defer func() {
        if rows != nil {
            rows.Close()
        }
        if stmt != nil { //stmt记住也要释放
            stmt.Close()
        }
    }()
    if err == sql.ErrNoRows {
        fmt.Printf("not found data of id:%d\n", id)
        return
    }
    if err != nil {
        fmt.Printf("scan failed, err:%v\n", err)
        return
    }
    for rows.Next() {
        var user User
        err := rows.Scan(&user.Id, &user.Name, &user.Age)
        if err != nil {
            fmt.Printf("scan failed, err:%v\n", err)
            return
        }

        fmt.Printf("user:%#v\n", user)
    }
}

func main() {
    dsn := "root:123456@tcp(127.0.0.1:3306)/golang"
    Db, err := sql.Open("mysql", dsn)
    if err != nil {
        fmt.Printf("open mysql is failed,err:%v\n", err)
        return
    }

    err = Db.Ping()
    if err != nil {
        fmt.Printf("ping is failed,err:%v\n", err)
        return
    }

    fmt.Printf("connect to db successful\n")

    PrepareQuery(Db)
}

 执行结果如下:

 

 

更新操作预处理实例:(这里以insert举例)

package main

import (
    "database/sql"
    "fmt"

    _ "github.com/go-sql-driver/mysql"
)

type User struct {
    Id   int64  `db:"id"`
    Name string `db:"name"`
    Age  int    `db:"age"`
}

func PrepareInsert(DB *sql.DB) {
    stmt, err := DB.Prepare("insert into user(name, age)values(?, ?)")
    if err != nil {
        fmt.Printf("prepare failed, err:%v\n", err)
        return
    }

    result, err := stmt.Exec("user10111", 108)
    if err != nil {
        fmt.Printf("exec failed, err:%v\n", err)
        return
    }

    id, err := result.LastInsertId()
    if err != nil {
        fmt.Printf("last insert id failed, err:%v\n", err)
        return
    }

    affectRows, err := result.RowsAffected()
    if err != nil {
        fmt.Printf("affectRows failed, err:%v\n", err)
        return
    }

    fmt.Printf("last insert id:%d affect rows:%d\n", id, affectRows)
}

func main() {
    dsn := "root:123456@tcp(127.0.0.1:3306)/golang"
    Db, err := sql.Open("mysql", dsn)
    if err != nil {
        fmt.Printf("open mysql is failed,err:%v\n", err)
        return
    }

    err = Db.Ping()
    if err != nil {
        fmt.Printf("ping is failed,err:%v\n", err)
        return
    }

    fmt.Printf("connect to db successful\n")

    PrepareInsert(Db)
}

 执行结果:

数据库结果:

四、Mysql事务

4.1 应用场景

1. 同时更新,多个表。

2. 同时更新多行数据。

4.2 事务的ACID

1. 原子性

2. 一致性

3. 隔离性

4. 持久性

4.3 Mysql的事务实例

A. Db.Begin(),开启一个事务

B. Db.Commit(),提交一个事务

C. Db.Rollback(),回滚一个事务

 

关于事务的实例如下:

package main

import (
    "database/sql"
    "fmt"

    _ "github.com/go-sql-driver/mysql"
)

type User struct {
    Id   int64  `db:"id"`
    Name string `db:"name"`
    Age  int    `db:"age"`
}

func Transaction(Db *sql.DB) {

    //开启事务
    tx, err := Db.Begin() //tx就是事务这次用来操作数据库的1个连接
    if err != nil {
        fmt.Printf("begin failed, err:%v\n", err)
        return
    }

    _, err = tx.Exec("insert into user(name, age)values(?, ?)", "user0101", 108)  //插入一条数据
    if err != nil {
        tx.Rollback()
        return
    }

    _, err = tx.Exec("update user set name=?, age=?", "user0101", 108) 
    if err != nil {
        tx.Rollback() //如果有错误,则回滚
        return
    }

    err = tx.Commit() //提交事务
    if err != nil {
        tx.Rollback()
        return
    }
}

func main() {
    dsn := "root:123456@tcp(127.0.0.1:3306)/golang"
    Db, err := sql.Open("mysql", dsn)
    if err != nil {
        fmt.Printf("open mysql is failed,err:%v\n", err)
        return
    }

    err = Db.Ping()
    if err != nil {
        fmt.Printf("ping is failed,err:%v\n", err)
        return
    }

    fmt.Printf("connect to db successful\n")

    Transaction(Db)
}

执行结果如下:

 

数据库结果:

 

五、sqlx库介绍与使用

5.1 介绍

 

sqlx是在数据库层面的驱动进行封装的。主要做的是对查询的优化

github地址:

https://github.com/jmoiron/sqlx

安装:

go get github.com/jmoiron/sqlx

 

5.2 好处

A. 使用更简单

B. 支持多数据库, mysql、 postgresql、 oracle、 sqlite

5.3 开发与使用

A. 查询, sqlx.DB.Get(查单行)和sqlx.DB.Select(查多行)

B. 更新、插入和删除, sqlx.DB.Exec

C. 事务, sqlx.DB.Begin()、 sqlx.DB.Commit、 sqlx.DB.rollback

5.3.1 查询

查询这里对go语言原生sql方法做了优化,封装了接口,所以用sqlx做查询较为方便,不需要像原生一样去考虑结果集close问题、for循环遍历问题、append问题,sqlx帮我们把上述问题都做了(其底层借助反射机制封装了接口)。

单行查询:sqlx.DB.Get

实例:

package main

import (
    "database/sql"
    "fmt"

    _ "github.com/go-sql-driver/mysql"
    "github.com/jmoiron/sqlx"
)

type User struct {
    Id   int64  `db:"id"`
    Name string `db:"name"`
    Age  int    `db:"age"`
}

func QueryRow(Db *sqlx.DB) { //查询单行数据
    id := 2
    var user User
    err := Db.Get(&user, "select id, name, age from user where id=?", id)
    if err == sql.ErrNoRows { //无数据报错
        fmt.Printf("not record found\n")
        return
    }
    if err != nil { //查询失败报错
        fmt.Printf("get failed, err:%v\n", err)
        return
    }

    fmt.Printf("get user succ, user:%#v\n", user)
}

func main() {
    dns := "root:123456@tcp(127.0.0.1:3306)/golang"
    Db, err := sqlx.Connect("mysql", dns) //sqlx.connect连接数据库
    if err != nil {
        fmt.Printf("open mysql failed, err:%v\n", err)
        return
    }

    err = Db.Ping()
    if err != nil {
        fmt.Printf("ping failed, err:%v\n", err)
        return
    }

    fmt.Printf("connect to db succ\n")
    QueryRow(Db)
}

执行结果:

 

多行查询:sqlx.DB.Select

实例: 

package main

import (
    "fmt"

    _ "github.com/go-sql-driver/mysql"
    "github.com/jmoiron/sqlx"
)

type User struct {
    Id   int64  `db:"id"`
    Name string `db:"name"`
    Age  int    `db:"age"`
}

func Query(Db *sqlx.DB) {
    var user []*User //多行数据存 

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
GO语言练习:channel工程实例发布时间:2022-07-10
下一篇:
使用 Go 语言徒手撸一个负载均衡器发布时间:2022-07-10
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap