go操作数据库

go操作数据库

Go官方没有提供数据库驱动,而是为开发数据库驱动定义了一些标准接口database/sql,开发者可以根据定义的接口来开发相应的数据库驱动

接口 说明
sql.Register(name string, driver driver.Driver) 注册驱动,通过map来保存多个驱动
Driver.Open(name string) (Conn, error) 返回一个数据库的连接,只能在一个goroutine中使用
Conn.Prepare(query string) (Stmt, error) 返回与当前连接相关的执行Sql语句的准备状态,用于增删改查
Conn.Close() error 关闭连接
Conn.Begin() (Tx, error) 返回事务,用于提交、回滚事务
Stmt.Exec(args []Value) (Result, error) 执行增删改语句
Stmt.Query(args []Value) (Rows, error) 执行查询语句

。。。

sqlx

操作Mysql

  1. 导包

    1
    2
    3
    4
    5
    // mysql驱动
    go get github.com/go-sql-driver/mysql

    // go操作数据库
    go get github.com/jmoiron/sqlx
  2. 注册驱动

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package main

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

    func main() {
    // 用户名:密码@tcp(ip:port)/数据库名
    database, err := sqlx.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/test")
    if err != nil {
    fmt.Println("open mysql failed,", err)
    return
    }
    fmt.Println(database)
    }
  3. 插入操作db.Exec

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    package main

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

    type User struct {
    UserId int `db:"user_id"`
    Username string `db:"username"`
    Sex string `db:"sex"`
    Email string `db:"email"`
    }

    var db *sqlx.DB

    // 注册驱动,创建操作对象
    func init() {
    database, err := sqlx.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/test")
    if err != nil {
    fmt.Println("open mysql failed,", err)
    return
    }
    db = database
    }

    func main() {
    // sql语句
    sql := "INSERT INTO test_go_user(`username`, `sex`, `email`) VALUES (?, ?, ?)"
    value := [3]string{"user01", "man", "user01@qq.com"}

    // 执行sql
    res, err := db.Exec(sql, value[0], value[1], value[2])
    if err != nil {
    fmt.Println("exec failed, ", err)
    return
    }

    // 查看最后一个初入的id
    id, err := res.LastInsertId()
    if err != nil {
    fmt.Println("exec failed", err)
    return
    }
    fmt.Println("insert success", id)
    }
  4. 查询操作db.Select

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    func main() {
    sql := "SELECT * FROM test_go_user"

    var users []User

    err := db.Select(&users, sql)
    if err != nil {
    fmt.Println("select failed, ", err)
    }
    fmt.Println(users)
    }
  5. 修改操作db.Exec

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    func main() {
    sql := "UPDATE test_go_user SET username=? WHERE user_id = ?"
    value := []string{"user02", "2"}

    res, err := db.Exec(sql, value[0], value[1])
    if err != nil {
    fmt.Println("exec failed, ", err)
    return
    }

    row, err := res.RowsAffected()
    if err != nil {
    fmt.Println("exec failed, ", err)
    return
    }
    fmt.Println("影响的行数:", row)
    }
  6. 删除操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    func main() {

    sql := "delete from test_go_user where user_id=?"

    res, err := db.Exec(sql, 2)
    if err != nil {
    fmt.Println("exce failed,", err)
    return
    }

    row, err := res.RowsAffected()
    if err != nil {
    fmt.Println("row failed, ", err)
    }
    fmt.Println("delete succ: ", row)
    }

操作Mysql事务Begin()/Rollback()/Commit()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
func main() {
//开启事务
conn, err := db.Begin()
if err != nil {
fmt.Println("begin failed :", err)
return
}

//执行插入语句
err = insert("user01", "man", "usre01@163.com", conn)
if err != nil {
fmt.Println("exec failed, ", err)
conn.Rollback() //出现异常,进行回滚操作
return
}

//执行插入语句
err = insert("user02", "man", "user02@163.com", conn)
err = errors.New("测试回滚")
if err != nil {
fmt.Println("exec failed, ", err)
conn.Rollback()
return
}

// 提交事务
conn.Commit()
fmt.Println("insert success")
}

gorm

参考

Go Object Relational Mapping

导包

1
2
3
4
// 安装MySQL驱动
go get -u gorm.io/driver/mysql
// 安装gorm包
go get -u gorm.io/gorm

创建连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var DB *gorm.DB

// 连接数据库
func init() {
username := "root"
password := "123456"
host := "127.0.0.1"
port := 3306
dbName := "test"

// data source name
// root:123456@tcp(127.0.0.1:3306)/test?charset=utf8&parseTime=True&loc=Local
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", username, password, host, port, dbName)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
checkErr(err)
DB = db
}

CRUD

实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
// 默认字段映射为数据库:create_time -> 实体类:createTime
// 如果有CreatedAt和UpdateAt会自动填充
type User struct {
ID int64
Username string `gorm:"column:username"`
Password string `gorm:"column:password"`
CreateTime int64 `gorm:"column:createtime"`
}

// TableName 返回表名
func (u *User) TableName() string {
return "test_go_user"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 新增一条数据
func saveUser() {
user := &User{
Username: "zhangsan",
Password: "123456",
CreateTime: time.Now().UnixMilli(),
}
res := DB.Create(user)
checkErr(res.Error)
}

// 查一条
func getById() {
id := 1
user := User{}
// First默认主键排序后取一条,Take不考虑排序
res := DB.Where("id=?", id).First(&user)
checkErr(res.Error)
fmt.Println(user)
}

// 查所有
func getAll() {
var users []User
res := DB.Find(&users)
checkErr(res.Error)
for i := 0; i < len(users); i++ {
fmt.Println(users[i])
}
}

// 改
func updateById() {
id := 1
res := DB.Model(&User{}).Where("id = ?", id).Update("username", "lisi")
checkErr(res.Error)
}

// 删
func deleteById() {
id := 2
res := DB.Where("id=?", id).Delete(&User{})
checkErr(res.Error)
}

// 原生sql执行增删改
func sql() {
db.Exec("insert into users (username,password,createtime) values (?,?,?)", user.Username, user.Password, user.CreateTime)
}

// 原生sql执行查
func sql2() {
sql := "SELECT type, count(*) as total FROM `goods` where create_time > ? GROUP BY type HAVING (total > 0)"
db.Raw(sql, "2022-11-06 00:00:00").Scan(&results)
fmt.Println(results)
}

事务

自动事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func autoTransaction() {
id := 1
err := DB.Transaction(func(tx *gorm.DB) error {
res := tx.Model(&User{}).Where("id = ?", id).Update("username", "2")
// 返回error就会回滚
if res.Error != nil {
return res.Error
}

res = tx.Model(&User{}).Where("id = ?", id).Update("username", "3")
res.Error = errors.New("测试回滚")
if res.Error != nil {
return res.Error
}
return nil
})
checkErr(err)
}

手动提交事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func transaction() {
tx := DB.Begin()

id := 1
res := tx.Model(&User{}).Where("id = ?", id).Update("username", "1")

res.Error = errors.New("测试回滚")
if res.Error != nil {
fmt.Println(res.Error)
// 回滚
tx.Rollback()
}
// 提交
tx.Commit()
}

通过保存点来回滚部分事务

SavePoint(“保存点名”); RollBackTo(“保存点名”)

回调

在回调中如果返回error则会回滚事务,可以用来做入参、出参检查等操作

结构体实现对应的回调,

增:BeforeCreate、AfterCreate

删:BeforeDelete、AfterDelete

改:BeforeUpdate、AfterUpdate

查:AfterFind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 在新插入数据前会调用
func (u *User) BeforeCreate(tx *gorm.DB) error {
// 判断是否有相同username的回调
username := u.Username

var count int64
DB.Model(&User{}).Where("username = ?", username).Count(&count)
if count > 0 {
return errors.New("存在相同username:" + username + ",回滚")
}
return nil
}

// 在新插入数据后会调用
func (u *User) AfterCreate(tx *gorm.DB) error {
fmt.Println("插入数据成功, id: ", u.ID)
return nil
}

go操作数据库
http://xwww12.github.io/2023/11/01/go/go操作数据库/
作者
xw
发布于
2023年11月1日
许可协议