青训营笔记 | day6 学习记录

77 阅读4分钟

GORM 介绍和简单使用

GORM

Gorm: 一个迭代超过十年的,快速的,对开发者友好的, 由 Golang 开发的 ORM 框架

Gorm 的基本使用

package main

import (
 "gorm.io/driver/mysql"
 "gorm.io/gorm"
)

// Declaring Models
type Product struct {
 Code  string
 Price uint
}

func (p Product) TableName() string {
 return "Product"
}

func main() {

 db, err := gorm.Open(mysql.Open(
  "user:pass@tcp(localhost: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: 332})

 // Read
 var product Product
 db.First(&product, 1)                 // find product with integer primary key
 db.First(&product, "code = ?", "D42") // find product with code D42

 // Update - update product's price to 200
 db.Model(&product).Update("Price", 200)
 // Update - update multiple fields
 db.Model(&product).Updates(Product{Price: 200, Code: "F42"}) // non-zero fields
 db.Model(&product).Updates(map[string]interface{}{"Price": 200, "Code": "F42"})

 // Delete - delete product
 db.Delete(&product, 1)
}

GORM 支持的数据库

1. MySQL
2. SQLServer
3. PostgreSQL
4. SQLite

连接数据库的方法

import (
  "gorm.io/driver/postgres"
  "gorm.io/gorm"
)

dsn := "user=gorm password=gorm dbname=gorm port=9920 sslmode=disable TimeZone=Asia/Shanghai"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})

在 MySQL 中为保证 time.Time 正确性, 在 DSN 中我们还需要添加 parseTime 参数,为了更好支持 UTF8 我们需要添加 charset=utf8mb4

可以看出GORM是通过驱动进行数据库的连接,需要其它数据库是更换驱动即可(当然可能还有存在一些数据处理的细微差别需要进行修改

GORM 创建数据

user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}

result := db.Create(&user) // pass pointer of data to Create

user.ID             // returns inserted data's primary key
result.Error        // returns error
result.RowsAffected // returns inserted records count

我们可以通过 clause.OnConflict 处理数据的冲突,我们还可以设置 filed 的默认值

user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
db.Clauses(clause.OnConfilict{DoNothing:true}).Create(&user)


// set Default values
type User struct {
  ID         int64
  Name       string `gorm:"default:galeone"`
  Age        int64  `gorm:"default:18"`
  uuid.UUID  UUID   `gorm:"type:uuid;default:gen_random_uuid()"` // db func
}

GORM 查询数据

GORM 查询提供的方法

  1. First: 查询第一条记录
  2. Take
  3. Last: 查询最后一条记录
// Get the first record ordered by primary key
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;

// Get one record, no specified order
db.Take(&user)
// SELECT * FROM users LIMIT 1;

// Get last record, order by primary key desc
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;

result := db.First(&user)
result.RowsAffected // returns found records count
result.Error        // returns error

// check error ErrRecordNotFound
errors.Is(result.Error, gorm.ErrRecordNotFound)

查询时默认会附加上 LIMIT 1,记录不存在时返回 ErrRecordNotFound,但是只有查询单条记录才会返回, first/last 通过主键进行查询,查询的struct 或者 model value ,不存在则采用第一个 field

GORM 更新数据

// struct update
// Update with conditions 单笔数据更新
db.Model(&User{}).Where("active = ?", true).Update("name", "hello")

// Update attributes with `struct`, will only update non-zero fields 多笔数据更新
db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})

// Select with Map
// User's ID is `111`: 
// 通过  map or select 
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})

struct update 只会 update 非零值, 要更新零值需要通过 map 或者 select

GORM 删除数据

数据的删除一般有两种方式,一种是物理的删除,直接从磁盘上删除,一种时软删除,就是对某些 field 进行标记,在查询忽略已标记特定 field的记录


type User struct {
  ID      int
  Deleted gorm.DeletedAt
  Name    string
}
// Email's ID is `10`
db.Delete(&email)
// DELETE from emails where id = 10;

// Delete with additional conditions
db.Where("name = ?", "jinzhu").Delete(&email)
// DELETE from emails where id = 10 AND name = "jinzhu";

// query soft deleted record 
db.Unscoped().Where("age = 20").Find(&users)
// SELECT * FROM users WHERE age = 20;

// 物理删除 软删除的数据
db.Unscoped().Delete(&order)
// DELETE FROM orders WHERE id=10;

如果 struct 或者 model 存在 gorm.DeletedAt field 删除则是采用软删除,我们需要软删除的数据我们可以 call Unscoped(),

GORM 事务

GORM 提供联Begin commit rollback 用于事务

操作的基本流程

// begin a transaction
tx := db.Begin()

// do some database operations in the transaction (use 'tx' from this point, not 'db')
tx.Create(...)

// ...

// rollback the transaction in case of error
tx.Rollback()

// Or commit the transaction
tx.Commit()

for exampl

func CreateAnimals(db *gorm.DB) error {
  // Note the use of tx as the database handle once you are within a transaction
  tx := db.Begin()
  defer func() {
    if r := recover(); r != nil {
      tx.Rollback()
    }
  }()

  if err := tx.Error; err != nil {
    return err
  }

  if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
     tx.Rollback()
     return err
  }

  if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
     tx.Rollback()
     return err
  }

  return tx.Commit().Error
}

在开发中我们难免会遇到忘记对事务进行提交所以我们就可以通过 GORM 提供的 transaction 对操作进行包裹

db.Transaction(func(tx *gorm.DB) error {
  // do some database operations in the transaction (use 'tx' from this point, not 'db')
  if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
    // return any error will rollback
    return err
  }

  if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
    return err
  }

  // return nil will commit the whole transaction
  return nil
})

Note: 我们可以通过设置 SkipDefaultTransaction filed 选择是否使用事务

Reference