初识Go Gorm| 青训营

100 阅读2分钟

Gorm

连接数据库

与Springboot那套比起来,我觉得使用方面比较难受,目前我使用gorm连接数据库是把url写在程序里面:

dsn := "root:6125@tcp(127.0.0.1:3306)/mybatis?charset=utf8mb4&parseTime=True&loc=Local"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
        //设置命名规则
        NamingStrategy: schema.NamingStrategy{
            SingularTable: true,
        },
        //关闭事务
        SkipDefaultTransaction: true,
    })

官方有提到:Mysql驱动程序提供了一些高级配置可以初始化过程中使用:

db, err := gorm.Open(mysql.New(mysql.Config{
        //配置……
    }), &gorm.Config{})

配置参数如下:

  • DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
  • DefaultStringSize: 256, // string 类型字段的默认长度
  • DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
  • DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
  • DontSupportRenameColumn: true, // 用 change 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
  • SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置

基本用法CRUD

在看了官方文档过后

我把使用gorm来操作简单的CRUD的方式,或者说代码风格分为两类:

  1. Raw:首先就是我最为熟悉的手写sql了,用法基本跟Java的Mybatis差不多
  2. 链式调用:基本上都是使用db点点点,经典orm操作

链式调用那个需要注意的是,查询操作的时候,需要最后进行Scan()操作进行对象的赋值,由于没有面向对象那样直接由orm层返回对应的实体类(结构体),gorm的查询返回更像是返回的一行(row)数据,点进源码可以到看:

image-20230725200319719

是利用了go底层的反射进行赋值的

Hook

就以创建对象为例吧:

BeforeSave BeforeCreate AfterCreate AfterSave

func (u *User) AfterCreate(tx *gorm.DB) (err error) {
    return
}
​
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
    return
}

开始的时候还有点纳闷,我就定义了一个方法就可以实现钩子的功能了?于是我点进了方法名,发现,底层实现已经做好了约定:

// AfterCreate after create hooks
func AfterCreate(db *gorm.DB) {
    if db.Error == nil && db.Statement.Schema != nil && !db.Statement.SkipHooks && (db.Statement.Schema.AfterSave || db.Statement.Schema.AfterCreate) {
        callMethod(db, func(value interface{}, tx *gorm.DB) (called bool) {
            if db.Statement.Schema.AfterCreate {
                if i, ok := value.(AfterCreateInterface); ok {
                    called = true
                    db.AddError(i.AfterCreate(tx))
                }
            }
​
            if db.Statement.Schema.AfterSave {
                if i, ok := value.(AfterSaveInterface); ok {
                    called = true
                    db.AddError(i.AfterSave(tx))
                }
            }
            return called
        })
    }
}

估计是做了一个回调,以达到拦截器(中间件)的实现

事务

如果没有事务上的严格要求,可以选择关闭在事务里执行写入操作,这将获得大约 30%+ 性能提升

全局禁用

SkipDefaultTransaction: true

在代码里面自己手动使用事务:

db.Transaction(func(tx *gorm.DB) error {
        //这里开始使用tx进行数据库操作
        if err := tx.Create(&User{UserHead: "ys"}).Error; err != nil {
            return err
        }
​
        return nil
    })