Gorm框架 | 青训营笔记

82 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天

重点内容

  1. 三件套介绍
  2. 三件套使用
  3. 实战案例

详细知识点

Gorm

github.com/go-gorm/gor…

文档: gorm.cn

Gorm是迭代10年+的ORM框架

基本使用

定义gorm model

type Product struct {
    Code string
    Price uint
}
​
//为model定义表名
func (p Product) TableName() string {  
    return "product"
}
​
func main(){
    db, err := gorm.Open (
        mysql.Open("user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"),&gorm.Config{})
    if err != nil {
        panic("failed to connect database")
    }
    //Create
    db.Create(&Product{Code: "D42", Price: 100})
    //Read
    var product Product
    db.First(&product,1) //根据整形主键查找
    db.First(&product,"code = ?", "D42")
    //Update
    db.Model(&product).Update("Price",200)//更新单列数据
    //Update多个字段
    db.Model(&product).Updates(Product{Price:200, Code: "F42"})//仅更新非零值字段
    db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"}) //更新零值
    //Delete
    db.Delete(&product, 1)
}

这里对Model的用处用法有些不太清楚?

几个约定:

  1. 默认使用名为ID的字段为主键
  2. 使用结构体的 蛇形负数作为表名
  3. 字段名的蛇形作为列名
  4. 使用CreateAt、UpdateAt字段作为创建、更新时间

支持的数据库

MySQL、SQLServer、PostgreSQL、SQLite

import (
    "gorm.io/driver/sqlserver"
    "gorm.io/gorm"
)
​
dsn := "sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm"
db, err := gorm.Open(sqlserver.Open(dsn), &gorm.Config{})

GORM创建数据

package main
​
import (
    "fmt"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)
type Product struct {
    ID uint     `gorm:"primarykey"`     //定义字段为主键
    Code string `gorm:"column: code"`   //定义列名
    Price uint  `gorm:"column: user_id"`
}
​
func main() {
    db, err := gorm.Open(mysql.Open("..."), &gorm.Config{})
    if err != nil // 处理错误
    //创建一条
    p := &Product{Code: "D42"}
    res := db.Create(p)
    fmt.Println(res.Error)
    fmt.Println(p.ID)
    //创建多条
    products := []*Product{{Code: "D41"},{Code: "D42"},{Code: "D43"}}
    res = db.Create(products)
    fmt.Println(res.Error)
    for _, p := range products {
        fmt.Println(p.ID)
    }
}

创建的时候有些字段并没有赋值,可以通过在结构体中字段后面加标签设置默认值,比如 'gorm:"default:galeone',类型要匹配上

查询数据

u := &User{}
db.First{u}

First查询,获取第一条记录(主键升序),如果查询不到数据则返回ErrRecordNotFound,不常用,一般用Find

users := make([]*User, 0)
result := db.Where("age > 10").Find(&users)
fmt.Println(result.RowsAffected)    //返回找到的记录数
fmt.Println(result.Error)
//IN 
db.Where("name IN ?", []string{"jinzhu","jinzhu 2"}).Find(&users)
//LIKE
db.Where("name LIKE ?", "%jin%"}).Find(&users)
//AND
db.Where("name = ? AND age > ?", "jinzhu", "22").Find(&users)
​
​

Find查询,查询不到则返回空数组,不会出问题

使用结构体作为查询条件时,GORM只查询非零值字段,如果字段值为0值,则该字段不会被构建为查询条件,解决方案是使用Map构建查询条件

更新数据

//更新单列
db.Model(&User{ID: 111}).Where("age > ?", 18).Update("name", "hello")
​
//更新多个列
db.Model(&User{ID: 111}).Update(User{Name: "hello", Age: 18})
​
//使用map
db.Model(&User{ID: 111}).Update(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
​
//更新选定字段
db.Model(&User{ID: 111}).Select("name").Update(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
​
//SQL表达式更新
db.Model(&User{ID: 111}).Update("age", gorm.EXpr("age * ? + ?", 2, 100))

删除数据

物理删除

db.Delete(&User{}, 10)
db.Delete(&User{}, "10")
db.Delete(&User{}, []int{1,2,3})
db.Where("name LIKE ?", "%jinzhu%").Delete(User{})
db.Delete(User{}, "email LIKE ?", "%jinzhu%")

物理删除,一旦删除就找不回来了

软删除

GORM提供gorm.DeleteAt用于实现软删除

拥有软删除能力的Model调用Delete时,记录不会被真正删除。GORM会将DeteleAt置为当前时间,不能通过正常的查询方法找到该记录

使用Unscoped可以查询到被软删的数据

GORM事务

Gorm提供了Begin、Commit、Rollback方法用于事务

db, err := gorm.Open(mysql.Open("..."), &gorm.Config{})
if err != nil {
    panic("错误信息")
}
tx := db.Begin() // 开始事务
//执行db操作,这里开始应该用tx,而不是db
if err = tx.Create(&User{Name: "name"}).Error; err != nil {
    tx.Rollback()
    //遇到错误时回滚事务
    return
}
if err = tx.Create(&User{Name: "name1"}).Error; err != nil {
    tx.Rollback()
    //遇到错误时回滚事务
    return
}
//提交
tx.Commit()

问题: 多个表时容易忘记rollback() 所以Gorm提供了Tansaction方法用于自动提交事务,避免漏写commit和rollback

db, err := gorm.Open(mysql.Open("..."), &gorm.Config{})
if err != nil {
    panic("错误信息")
}
if err = db.Transaction(func(tx *gorm.DB) error {
    if err = tx.Create(&User{Name: "name"}).Error; err != nil {
    //tx.Rollback()
    return err
    }
    if err = tx.Create(&User{Name: "name1"}).Error; err != nil {
        tx.Rollback()
        return err
    }
    return nil
}); err != nil {
    return
}

Hook

Hook是在创建、查询、更新、删除等操作之前、之后自动调用的函数。如果任何Hook返回错误,GORM将停止后续操作并回滚事务

type User struct {
    ID int64
    Name string 'gorm:"default:galeone"'
    Age int64   'gorm:"18"'
}
type Email struct {
    ID int64
    Name string
    Email string
}
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
    if u.Age < 0 {
        return errors.New("...")
    }
    return 
}
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
    return tx.Create(&Email{ID: u.ID, Email: u.Name + "@***.com"}).Error
}

GORM性能提高

对于写操作,GORM会封装在事务内运行,但会降低性能,可以用SkipDefaultTransaction关闭默认事务

使用PrepareStmt缓存预编译语句可以提高后续调用的速度。

&gorm.config{

SkipDefaultTransaction: true,

PrepareStmt: true

}