[Golang]Gorm使用汇总

Gorm使用汇总

资料

比较好的Gorm中文文档 https://jasperxu.com/gorm-zh/,本文基于该资料进行整理,汇总最基本的Gorm入门使用内容,陆续补充。

安装

go get -u github.com/jinzhu/gorm

数据库配置


func options() {

    DB.SingularTable(true)

    DB.AutoMigrate(&User{})

    DB.LogMode(true)

    DB.DB().SetMaxIdleConns(10)
    DB.DB().SetMaxOpenConns(100)
}

数据库连接

sqlite3

使用 sqlite数据库来快速连接

package main

import (
    "encoding/json"
    "fmt"
    "github.com/jinzhu/gorm"

    _ "github.com/jinzhu/gorm/dialects/sqlite"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

func initSqlite3Db() {

    var err error
    DB, err = gorm.Open("sqlite3", "test.db")

    if err != nil {
        panic(err)
    }
}

mysql

func initMysqlDb() {

    var err error
    DB, err = gorm.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/testdb?charset=utf8")

    if err != nil {
        panic(err)
    }
}

模型定义

tag:gorm

通过 tag标记 gorm来创建在数据库中字段的约束和属性配置

type User struct {
    Id         int64  gorm:"primary_key;AUTO_INCREMENT"
    UserId     string  gorm:"index:idx_user_id;not null
    UserName   string gorm:"type:varchar(64);not null"
    Age        int
    Phone      int       gorm:"unique_index:uk_phone"
    CreateTime time.Time gorm:"not null"
    UpdateTime time.Time gorm:"not null"
    UserRemark string    gorm:"column:remark;default:'默认'"
    IgnoreMe   int       gorm:"-"
}

gorm定义数据库字段约束 gorm:"primary_key"

字段设置为主键 gorm:"AUTO_INCREMENT"

字段设置为自增 gorm:"size:20

字段长度设置为20 gorm:"index:idx_user_id

字段设置普通索引,名称为idx_user_id gorm:"not null

设置字段为非空 gorm:"type:varchar(64)"

设置字段为varchar类型,长度为64 gorm:"column:remark"

设置数据库字段名为remark gorm:"-"

忽略此字段,不在表中创建该字段 gorm:"default:'默认'"

设置字段的默认值

注意:不一定是最全的示例,陆续补充

表名

gorm中,表名创建是以复数存在的,当创建表之前设置以下选项可以禁用表名复数


    DB.SingularTable(true)

或者在每个 model结构体中声明 TableName方法,若存在该方法 gorm优先采用这种方式来配置表名

type User struct {}

func (User) TableName() string {
  return "user"
}

基础操作

新增

NewRecord主键检查 & Create


func CreateUser() {
    user := &User{
        UserId:     "USER_001",
        UserName:   "zhangsan",
        Age:        10,
        Phone:      19901020305,
        CreateTime: time.Now(),
        UpdateTime: time.Now(),
        UserRemark: "备注",
    }

    boolBefore := DB.NewRecord(user)

    fmt.Println("boolBefore => ", boolBefore)

    err := DB.Create(user).Error
    if err != nil {
        fmt.Printf("CreateUser error %v \n", err)
    }

    fmt.Println("user => ", toJson(user))

    boolAfter := DB.NewRecord(user)

    fmt.Println("boolAfter => ", boolAfter)

    fmt.Println("user => ", toJson(user))
}

打印日志如下:

boolBefore =>  true
user =>  {"Id":1,"UserId":"USER_001","UserName":"zhangsan","Age":10,"Phone":19901020301,"CreateTime":"2021-06-01T13:33:02.237956+08:00","UpdateTime":"2021-06-01T13:33:02.237956+08:00","UserRemark":"默认","IgnoreMe":0}
boolAfter =>  false
user =>  {"Id":1,"UserId":"USER_001","UserName":"zhangsan","Age":10,"Phone":19901020301,"CreateTime":"2021-06-01T13:33:02.237956+08:00","UpdateTime":"2021-06-01T13:33:02.237956+08:00","UserRemark":"默认","IgnoreMe":0}

表数据如下:

sqlite> select * from user;
1|USER_001|zhangsan|10|19901020301|2021-06-01 13:33:02.237956+08:00|2021-06-01 13:33:02.237956+08:00|默认

查询

First:查询第一条记录

func FindFirstRecord()  {
    result := &User{}

    err:=DB.First(result).Error

    if err != nil {
        fmt.Printf("FindFirstRecord error %v",err)
        return
    }
    fmt.Printf(toJson(result))
}

日志输出如下:

{"Id":1,"UserId":"USER_001","UserName":"zhangsan","Age":10,"Phone":19901020301,"CreateTime":"2021-06-01T13:33:02.237956+08:00","UpdateTime":"2021-06-01T13:33:02.237956+08:00","UserRemark":"默认","IgnoreMe":0}

Last:查询最后一条记录

func FindLastRecord()  {
    result := &User{}

    err:=DB.Last(result).Error

    if err != nil {
        fmt.Printf("FindLastRecord error %v",err)
        return
    }
    fmt.Printf(toJson(result))
}

日志输出如下:

{"Id":2,"UserId":"USER_001","UserName":"zhangsan","Age":10,"Phone":19901020302,"CreateTime":"2021-06-01T13:57:56.807786+08:00","UpdateTime":"2021-06-01T13:57:56.807786+08:00","UserRemark":"默认","IgnoreMe":0}

First(… , pk):根据主键查询记录

func FindRecordByPK()  {
    result := &User{}

    err:=DB.First(&User{},2).Find(result).Error

    if err != nil {
        fmt.Printf("FindRecordByPK error %v",err)
        return
    }
    fmt.Printf(toJson(result))
}

日志输出如下:

{"Id":2,"UserId":"USER_001","UserName":"zhangsan","Age":10,"Phone":19901020302,"CreateTime":"2021-06-01T13:57:56.807786+08:00","UpdateTime":"2021-06-01T13:57:56.807786+08:00","UserRemark":"默认","IgnoreMe":0}

Where(…) 条件查询条件

关于条件查询相对来说比较简单,这里不赘述可以参考 https://jasperxu.com/gorm-zh/crud.html#q

FirstOrInit() & Attrs() & Assign()

获取第一个匹配的记录,或者使用给定的条件初始化一个新的记录返回。

func FirstOrInit() {
    user := &User{
        UserId:     "USER_001",
        UserName:   "User",
        Age:        10,
        Phone:      19901020305,
        CreateTime: time.Now(),
        UpdateTime: time.Now(),
    }

    err := DB.Attrs(&User{UserName: "attrs"}).

        Assign(&User{UserRemark: "强制修改"}).

        FirstOrInit(user, User{UserName: "guanjian"}).Error

    if err != nil {
        fmt.Printf("FirstOrInit error %v", err)
        return
    }
    fmt.Printf(toJson(user))
}

FirstOrInit方法如果查不到结果 不会落库,只是会初始化 FirstOrInit(obj)中的 obj对象的值,且将Where条件中的值一并初始化出来

FirstOrCreate() & Attrs() & Assign()

FirstOrInit类似,不同的是它会进行 落库操作。获取第一个匹配的记录,或者使用给定的条件进行落库新建。

func FirstOrCreate() {
    user := &User{
        UserId:     "USER_001",
        UserName:   "guanjian",
        Age:        10,
        Phone:      19901020310,
        CreateTime: time.Now(),
        UpdateTime: time.Now(),
    }

    err := DB.FirstOrCreate(user, User{Id: 10}).Error

    if err != nil {
        fmt.Printf("FirstOrInit error %v", err)
        return
    }
    fmt.Printf(toJson(user))
}

FirstOrCreate方法如果查不到结果 会落库,也会初始化 FirstOrCreate(obj)中的 obj对象的值,且将Where条件中的值一并初始化出来

Select查询字段定义

这里只查询 user_name,age字段返回,其余字段不会查询到值返回

func Select() {
    user := &User{}
    err := DB.Select("user_name,age").Find(user).Error

    if err != nil {
        fmt.Printf("Select error %v", err)
        return
    }
    fmt.Println("只查询user_name,age字段", toJson(user))
}

Scan

func Scan() {
    user := &User{}
    err := DB.Table("user").Select([]string{"user_name","age"}).Scan(user).Error
    if err != nil {
        fmt.Printf("Scan error %v", err)
        return
    }
    fmt.Println("user => ", toJson(user))
}

Scopes 动态条件添加

实现方法形如 func(db *gorm.DB) * gorm.DB则可以配合方法 Scopes进行动态条件的添加,编码更清晰友好


func WithCreateTimeLessThanNow(db *gorm.DB) *gorm.DB {
    return db.Where("create_time < ?", time.Now())
}

func WithAgeGreaterThan0(db *gorm.DB) *gorm.DB {
    return db.Where("age > ?", 0)
}

func Scopes() {
    user := &User{}

    err := DB.Scopes(WithAgeGreaterThan0,WithCreateTimeLessThanNow).Find(user).Error
    if err != nil {
        fmt.Printf("Scopes error %v", err)
        return
    }
    fmt.Println("user => ", toJson(user))
}

修改

Save

保存已存在的数据,触发更新逻辑(update);保存不存在的数据,直接插入(insert)


func SaveExist() {
    user := &User{
        Id: 1,
    }

    user.UserName = "saveNewUserName"

    err := DB.Save(user).Error
    if err != nil {
        fmt.Printf("Save error %v", err)
        return
    }
    fmt.Println("save user => ", toJson(user))
}

func SaveNoExist() {
    user := &User{
        Id: 9999,
        Phone: 19902029492,
    }

    user.UserName = "saveNewUserName"

    err := DB.Save(user).Error
    if err != nil {
        fmt.Printf("Save error %v", err)
        return
    }
    fmt.Println("save user => ", toJson(user))
}

Update & Updates & Omit

func Update() {
    user := &User{
        Id: 9999,
    }

    err := DB.Model(user).Update("user_name", "this is 9999").Error
    if err != nil {
        fmt.Printf("Update error %v", err)
        return
    }
}

更新字段效果如下:

[Golang]Gorm使用汇总
使用 Updates方法可以批量更新多个字段
func Updates() {

    whereUser := &User{
        Id: 9999,
    }

    updateUser := &User{
        UserName: "This is Updates",
        Age: 9292,
        UserRemark: "Updates",
    }

    err := DB.Model(whereUser).Updates(updateUser).Error
    if err != nil {
        fmt.Printf("Updates error %v", err)
        return
    }
}

批量更新字段效果如下:

[Golang]Gorm使用汇总
使用 Omit方法可以忽略该字段的更新
func UpdateOmit() {

    whereUser := &User{
        Id: 1,
    }

    updateUser := &User{
        UserName:   "This is Updates",
        Age:        6666,
        UserRemark: "1 -> Update",
    }

    err := DB.Model(whereUser).

        Omit("user_name").
        Updates(updateUser).Error
    if err != nil {
        fmt.Printf("Updates error %v", err)
        return
    }
}

[Golang]Gorm使用汇总

以上更新操作将执行模型的 BeforeUpdate, AfterUpdate方法,更新其UpdatedAt时间戳,在更新时保存它的Associations,如果不想调用它们,可以使用UpdateColumn, UpdateColumns

非零值更新问题 为了避免误操作或脏数据对全表进行更新,需要配置 WHERE必填条件进行限制

使用 RowsAffected可以返回当前更新的行数

func RowsAffected() {

    whereUser := &User{
        Id: 9999,
    }

    updateUser := &User{
        UserName:   "This is Updates",
        Age:        9292,
        UserRemark: "Updates",
    }

    count := DB.Model(whereUser).Updates(updateUser).RowsAffected
    fmt.Printf("Updates count %v", count)
}

删除

一般使用数据库逻辑删除,这部分可参考 https://jasperxu.com/gorm-zh/crud.html#d

回调使用(Callback)

在创建,更新,查询,删除时将被调用,如果任何回调返回错误,gorm将停止未来操作并回滚所有更改。

Callback默认定义

gorm源码中可以看到默认的 callback注册,这里着重关注 callback_create.go&#x3001;callback_delete.go&#x3001;callback_query.go&#x3001;callback_update.go即可,其实整个 gorm的执行流程都是基于 callback来实现整个执行流程的。

[Golang]Gorm使用汇总
下面举例 callback_create.go文件的 init()方法,按照从上到下的顺序,注册了 callback函数,每个 callbackName(如:gorm:create)都可以作为回调的插入点使用,配合 Before&#x3001;After可以实现前置插入、后置插入等,其他 callback类似这里不再赘述

func init() {
    DefaultCallback.Create().Register("gorm:begin_transaction", beginTransactionCallback)
    DefaultCallback.Create().Register("gorm:before_create", beforeCreateCallback)
    DefaultCallback.Create().Register("gorm:save_before_associations", saveBeforeAssociationsCallback)
    DefaultCallback.Create().Register("gorm:update_time_stamp", updateTimeStampForCreateCallback)
    DefaultCallback.Create().Register("gorm:create", createCallback)
    DefaultCallback.Create().Register("gorm:force_reload_after_create", forceReloadAfterCreateCallback)
    DefaultCallback.Create().Register("gorm:save_after_associations", saveAfterAssociationsCallback)
    DefaultCallback.Create().Register("gorm:after_create", afterCreateCallback)
    DefaultCallback.Create().Register("gorm:commit_or_rollback_transaction", commitOrRollbackTransactionCallback)
}

Callback事务保证

查看 gorm源码,诸如CRUD的方法如 Create()&#x3001;Update()&#x3001;Find()&#x3001;Delete()等内部都进行了callback方法等调用,callback内部代码如下:

func (scope *Scope) callCallbacks(funcs []*func(s *Scope)) *Scope {

    defer func() {
        if err := recover(); err != nil {
            if db, ok := scope.db.db.(sqlTx); ok {

                db.Rollback()
            }
            panic(err)
        }
    }()
    for _, f := range funcs {
        (*f)(scope)
        if scope.skipLeft {
            break
        }
    }
    return scope
}

下面来看一个 callback中产生panic异常,数据库回滚的示例,如下:

func Callback() {

    DB.Callback().Create().

        After("gorm:create").
        Register("gorm:AfterCreate", func(scope *gorm.Scope) {
        fmt.Println("AfterCreate")

        panic("AfterCreate error")
    })

    CreateUser()
}

异常处理

可以对 RecordNotFound()&#x3001;Error&#x3001;GetErrors()来处理记录不存在、异常、多个异常等情况

func Error() {

    bool := DB.First(&User{Id: 9999}).RecordNotFound()
    fmt.Println("RecordNotFound => ", bool)

    err := DB.Create(&User{}).Error
    fmt.Println("err => ", err)

    errs := DB.Create(&User{}).GetErrors()
    fmt.Println("errs => ", errs)
}

事务

注意,一旦你在一个事务中,使用 tx作为数据库句柄, 如果使用 gorm.DB 操作,是不受事务管控的,这里需要注意!

func Tx() {

    tx := DB.Begin()

    err1 := tx.Create(&User{
        UserId:     "USER_002",
        UserName:   "zhangsan",
        Age:        0,
        Phone:      17701020304,
        CreateTime: time.Now(),
        UpdateTime: time.Now(),
        UserRemark: "",
        IgnoreMe:   0,
    }).Error
    if err1 != nil {
        tx.Rollback()
        fmt.Println("=== Create 1 error ===")
    }

    err2 := tx.Create(&User{
        UserId:     "USER_002",
        UserName:   "zhangsan",
        Age:        0,
        Phone:      17701020304,
        CreateTime: time.Now(),
        UpdateTime: time.Now(),
        UserRemark: "",
        IgnoreMe:   0,
    }).Error
    if err2 != nil {
        tx.Rollback()
        fmt.Println("=== Create 2 error ===")
    }
    tx.Commit()
}

日志


db.LogMode(true)

db.LogMode(false)

db.Debug().Where("name = ?", "jinzhu").First(&User{})

参考

https://jasperxu.com/gorm-zh/

Original: https://blog.csdn.net/u013161278/article/details/117435811
Author: 大摩羯先生
Title: [Golang]Gorm使用汇总

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/816544/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球