Go数据库操作

本人编写了一个根据数据库表单生成表单的工具,欢迎自取:gitee->gorm结构体自动生成工具

前期操作

下载驱动包:

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

最后导入包即可:

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

连接到数据库:

db, err := sql.Open("mysql", "username:password@tcp(localhost:3306)/database")
if nil != err {
    fmt.Println("connect db error: ", err)
}
defer db.Close()
db.SetMaxOpenConns(10) // 设置最大连接数
db.SetMaxIdleConns(5) // 设置闲置情况最大连接数

CRUD操作

查询操作:

// 指定条件,指定参数查询
rows, err := db.Query("select * from book where id=?", 12312)
if nil != err {
    fmt.Println("query db error: ", err.Error())
    return
}
defer rows.Close()
fmt.Println(rows)
for rows.Next() {
    var book dao.Book\
    err = rows.Scan(&book.Id, &book.Title)
    if err != nil {
        panic(err.Error())
    }
    fmt.Println(book)
}

增、删、改操作:

// 增加
insert := "insert into book(title) values(?)"
ret, err := db.Exec(insert, "name")
if err != nil {
    println(err.Error())
    return
}
id, err := ret.LastInsertId()
if err != nil {
    fmt.Println("获取id失败")
    return
}
fmt.Print("插入的id为:", id)

// 修改
update := "update book set title = ? where id = ?"
uret, err := db.Exec(update, "hello", id)
if err != nil {
    fmt.Println(err.Error())
    return
}
unum, err := ret.RowsAffected()
if err != nil {
    fmt.Println(err.Error())
    return
}
fmt.Printf("影响了 %d 行数据\n", unum)

// 删除
del := "delete from book where title = ?"
dret, err  := db.Exec(del, "hello")
if err != nil {
    fmt.Println(err.Error())
    return
}
dnum, err = dret.RowsAffected()
if err != nil {
    fmt.Println(err.Error())
    return
}
fmt.Printf("影响了 %d 行数据\n", dnum)

预编译

sql := "select * from book where id = ?"
stmt, err := db.Prepare(sql)
defer stmt.Close()

rows, err := stmt.Query(1)
fmt.Println(rows)
for rows.Next() {
    var book dao.Book
    //将值存入变量book中
    err = rows.Scan(&book.Id, &book.Title)
    if err != nil {
        //panic(err.Error())
    }
    fmt.Println(book)
}

Gorm框架应用

建议学习官方文档更详细:gorm.io官方

连接数据库:

核心代码(需要导入包):

package database

import (
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

var Db *gorm.DB

func Init() {
	var err error
	dataConfig := "username:password@tcp(localhost:3306)/gostudy"
	Db, err = gorm.Open("mysql", dataConfig)
	if err != nil {
		fmt.Printf("mysql connect error %v", err)
	}
	defer Db.Close()
	Db.DB().SetMaxOpenConns(10) // 设置最大连接数
	Db.DB().SetMaxIdleConns(5)  // 设置闲置情况最大连接数
}

模型设定

属性首字母应该是大写的,如果是小写无法反射获取,会导致映射失败

默认结构体名字对应着表名复数形式,建议自己实现func TableName() string方法更好

package model

type Data struct {
	DataId   int    `json:"data_id" gorm:"column:data_id;primaryKey"`
	User     string `json:"user" gorm:"column:user"`
	Password string `json:"password" gorm:"column:password"`
}

func (e *Data) TableName() string {
	return "data"
}

本人编写了一个根据数据库表单生成表单的工具,欢迎自取:gitee->gorm结构体自动生成工具

CRUD

查询

GORM 提供了 FirstTakeLast 方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1 条件,且没有找到记录时,它会返回 ErrRecordNotFound 错误

// 获取第一条记录(主键升序)
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;

// 获取一条记录,没有指定排序字段
db.Take(&user)
// SELECT * FROM users LIMIT 1;

// 获取最后一条记录(主键降序)
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;

result := db.First(&user)
result.RowsAffected // 返回找到的记录数
result.Error        // returns error or nil

// 检查 ErrRecordNotFound 错误
errors.Is(result.Error, gorm.ErrRecordNotFound)

如果你想避免ErrRecordNotFound错误,你可以使用Find,比如db.Limit(1).Find(&user)Find方法可以接受struct和slice的数据。

**注意:**如果相关 model 没有定义主键,那么将按 model 的第一个字段进行排序。

常用方法示例:

注意筛选条件必须 放在Find()前面

// 条件搜索
// SELECT * FROM `data`  WHERE (data_id = 1) 
find := Db.Where("data_id = ?", 1).Find(&d)
// find.RowsAffected 可以判断搜索到的数目
fmt.Println(find.RowsAffected)

// select选取特定字段
// SELECT user FROM `data`   LIMIT 1 
Db.Select("user").First(&d)

// orderBY
// SELECT * FROM `data`   ORDER BY data_id
Db.Order("data_id").Find(&d)

// group 和 having
// SELECT * FROM `data`   GROUP BY date(data_id) HAVING (sum(data_id) > 100)  
rows, err := Db.Table("data").Group("date(data_id)").Having("sum(data_id) > ?", 100).Rows()	

// 多条数据,如果不是切片只是一个,那么就只会是最后的
var ds []model.Data
Db.Where("password = ?", "123321").Find(&ds)

// rows方法扫描
var ds model.Data
rows, _ := Db.Model(&model.Data{}).Where("password = ?", "123321").Rows()
defer rows.Close() // 没读完且不关闭就会导致go太多
for rows.Next() {
    Db.ScanRows(rows, &ds)
    fmt.Println(ds)
}

// 如果条件不存在就插入
// SELECT * FROM `data`  WHERE (data_id = 2) LIMIT 1  
// INSERT INTO `data` (`da***ser`,`password`) VALUES (0,'us','2') 
Db.FirstOrCreate(&d, "data_id = ?", 2)

// 自定义语句
ds := make([]model.Data, 0)
database.Db.Raw("select * from data where password = ?", 2).Scan(&ds)

插入

// 基本创建
// INSERT INTO `data` (`da***ser`,`password`) VALUES (3,'uss','123')  
ds := model.Data{3, "uss", "123"}
Db.Create(&ds)

// 选择 某个字段 or 忽略
ds := model.Data{0, time.Now().String(), "1234"}

// Select 选择, 里面填的也可以是 struct的名字,不一定是数据库的(酷,好东西)
Db.Select("da***ser", "password").Create(&ds)

// Omit 忽略
ds := model.Data{0, time.Now().String(), "1234"}
Db.Omit("password").Create(&ds)

更新

// 更新
var d model.Data
Db.Model(&d).Where("data_id = ?", 1).Update("password", "hello")

// 根据 `struct` 更新属性,只会更新非零值的字段
db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;

// UPDATE `data` SET `data_id` = data_id + 2    
exp := gorm.Expr("data_id + ?", 2)
Db.Table("data").Update("data_id", exp)

// 同样支持select 和 Omit,这两个方法不能配合Table用,必须配合Model!!!
d := model.Data{0, "3", "2"}
Db.Model(&model.Data{}).Select("User").Update(&d)
Db.Model(&model.Data{}).Omit("User").Update(&d)

// 使用map更新
Db.Model(&model.Data{}).Update(map[string]interface{}{"user":"s"})

注意 当通过 struct 更新时,GORM 只会更新非零字段。 如果您想确保指定字段被更新,你应该使用 Select 更新选定字段,或使用 map 来完成更新操作

删除

删除的话一定要增加添加,如果本身没有设置ID这个字段的,就算自己搞了primaryKey都无用,还是需要Where才可以防止删除,同时gorm提供软删除,需要有gorm.DeletedAt字段

// Email 的 ID 是 `10`,不建议用这个方式!
db.Delete(&email)
// DELETE from emails where id = 10;

// DELETE FROM `data`  WHERE (user = 's')  
Db.Where("user = ?", "s").Delete(&model.Data{})

CURD四个常用

// 查找,条件需要放在 Find() 前面
var ds []model.Data
Db.Where("password = ?", "value").Find(&ds) // 或者单个First

// 删除
Db.Where("password = ?", "value").Delete(&model.Data{})

// 更新(必须要有Model)
Db.Model(&model.Data{}).Select("User").Update(&d)
Db.Model(&model.Data{}).Where().Update(map[string]interface{}{"password":"value"})
Db.Model(&model.Data{}).Where().Update("password", "value")

// 插入
Db.Create(&d) // 配合 select & Omit
Db.FirstOrCreate(&d, "data_id = ?", 2)

// exec执行指令, 适合: 删除、更新、插入
Db.Exec("update data set password = ? where user = ?", 3, "us")
// raw则适合:查询
Db.Raw("select * from data where password = ?", 2).Scan(&ds) // 自定义语句

tag字段补充

声明 model 时,tag 是可选的,GORM 支持以下 tag: tag 名大小写不敏感,但建议使用 camelCase 风格

标签名 说明
column 指定 db 列名
type 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not nullsize, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT
size 指定列大小,例如:size:256
primaryKey 指定列为主键
unique 指定列为唯一
default 指定列的默认值
precision 指定列的精度
scale 指定列大小
not null 指定列为 NOT NULL
autoIncrement 指定列为自动增长
autoIncrementIncrement 自动步长,控制连续记录之间的间隔
embedded 嵌套字段
embeddedPrefix 嵌入字段的列名前缀
autoCreateTime 创建时追踪当前时间,对于 int 字段,它会追踪秒级时间戳,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nano
autoUpdateTime 创建/更新时追踪当前时间,对于 int 字段,它会追踪秒级时间戳,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milli
index 根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情
uniqueIndex index 相同,但创建的是唯一索引
check 创建检查约束,例如 check:age > 13,查看 约束 获取详情
<- 设置字段写入的权限, <-:create 只创建、<-:update 只更新、<-:false 无写入权限、<- 创建和更新权限
-> 设置字段读的权限,->:false 无读权限
- 忽略该字段,- 无读写权限
comment 迁移时为字段添加注释

常见错误

  1. 时间比较,记得要改格式

    database.Db.Where("send_time = ?", 、sendTime.Format("2006-01-02 15:04:05")).Find(&FriendReqLog{})