Go框架基础一 | 青训营笔记

68 阅读5分钟

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

1 ORM框架-GORM

ORM 是 Object Relational Mapping 的缩写,译为“对象关系映射”,它解决了对象和关系型数据库之间的数据交互问题。提前配置好对象和数据库之间的映射关系,ORM 就可以自动生成 SQL 语句,并将对象中的数据自动存储到数据库中,整个过程不需要人工干预。

1.1 定义 gorm model

type Product struct {
    ID      uint
    Code    string
    Price   uint
}

1.2 为model定义表名

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

1.3 约定

默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAtUpdatedAt 字段追踪创建、更新时间。

1.4 连接数据库

GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server。

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

dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

if err != nil {
    panic("failed to connect database")
}

1.5 创建数据

db.Create(&Product{Code : "D42", Price: 100})

p := &Product{Code: "A15", Price: 200}
result := db.Create(p)

p.ID  // 返回插入数据的主键
result.Error  // 返回error
result.RowsAffected  // 返回插入记录的条数

// 创建多条
products := []*Product{{Code : "D41"},{Code : "D43"}}
result := db.Create(products)

可以通过default标签为字段定义默认值:

type Product struct {
    ID      uint
    Code    string `gorm:"default:galeone"`
    Price   uint   `gorm:"default:100"`
}

1.6 查询数据

// 获取第一条记录(主键升序)
// 没有找到记录时,它会返回 ErrRecordNotFound 错误
p := &Product{}

// SELECT * FROM product ORDER BY id LIMIT 1;
db.First(p)

// 查询多条数据
products := make([]*Product, 0)
// SELECT * FROM product WHERE price > 10;
result := db.Where("price > 100").Find(&products)
result.Error  // returns error
result.RowsAffected  // 返回找到的记录数,相当于len(products)

// SELECT * FROM product WHERE code IN ('D41', 'D43');
db.Where("code IN ?", []string{"D41", "D43"}).Find(&products)

// SELECT * FROM product WHERE code LIKE 'D%';
db.Where("code LIKE ?", "D%").Find(&products)

// SELECT * FROM product WHERE code = 'D41' AND price >= 100;
db.Where("code = ? AND price >= ?", "D41", "100").Find(&products)

当使用结构作为条件查询时,GORM 只会查询非零值字段。这意味着如果字段值为 0''false 或其他零值,该字段不会被用于构建查询条件。例如:

// SELECT * FROM product WHERE code = 'D41';
db.Where(&Product{Code: "D41", Price: 0}).Find(&products)

可以使用map来构建查询条件,如:

// SELECT * FROM product WHERE code = 'D41' AND price = 0;
db.Where(map[string]interface{}{"Code": "D41", "Price": 0}).Find(&products)

1.7 更新数据

Update用于更新单列,当使用了 Model 方法,且该对象主键有值,该值会被用于构建条件,例如:

// 条件更新 
db.Model(&User{}).Where("active = ?", true).Update("name", "hello") 
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;  

// User 的 ID 是 `111` 
db.Model(&user).Update("name", "hello") 
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;  

// 根据条件和 model 的值进行更新 
db.Model(&user).Where("active = ?", true).Update("name", "hello") 
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true; 

Updates用于更新多列,当使用 struct 更新时,默认情况下,GORM 只会更新非零值的字段,如果想确保指定字段被更新,应该使用 Select 更新选定字段,或使用 map 来完成更新操作。例如:

// 根据 `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;  
  
// 根据 `map` 更新属性  
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})  
// UPDATE users SET name='hello', age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;


// 使用Select

// Select 和 Map
// User's ID is `111`:
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
// UPDATE users SET name='hello' WHERE id=111;

// Select 和 Struct (可以选中更新零值字段)
DB.Model(&result).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})
// UPDATE users SET name='new_name', age=0 WHERE id=111;

GORM还允许使用SQL表达式更新,例如:

// product 的 ID 是 `3` 
db.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100)) 
// UPDATE "products" SET "price" = price * 2 + 100, "updated_at" = '2013-11-17 21:34:10' WHERE "id" = 3;

1.8 删除数据

  • 物理删除
// Email 的 ID 是 `10` 
db.Delete(&email) 
// DELETE from emails where id = 10;  

// 带额外条件的删除 
db.Where("name = ?", "jinzhu").Delete(&email) 
// DELETE from emails where id = 10 AND name = "jinzhu"; 


db.Delete(&User{}, 10)  // DELETE FROM users WHERE id = 10;  
db.Delete(&User{}, "10")  // DELETE FROM users WHERE id = 10;  
db.Delete(&users, []int{1,2,3})  // DELETE FROM users WHERE id IN (1,2,3); 
  • 软删除

如果模型包含一个gorm.DeletedAt字段(gorm.Model已经包含),它将自动获得软删除的能力。拥有软删除能力的模型调用Delete时,记录不会从数据库中真正删除。但GORM会将DeletedAt置为当前时间,并且通过正常的查询方法找不到该记录,可以使用Unscoped找到被软删除的记录。

// user 的 ID 是 `111`  
db.Delete(&user)  
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;  
  
// 批量删除  
db.Where("age = ?", 20).Delete(&User{})  
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;  
  
// 在查询时会忽略被软删除的记录  
db.Where("age = 20").Find(&user)  
// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;

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

1.9 事务

GORM提供了BeginCommitRollback方法用于使用事务。

// 开始事务 
tx := db.Begin()  

// 在事务中执行一些 db 操作(应该使用 'tx' 而不是 'db') 
tx.Create(...)  

// ...  

// 遇到错误时回滚事务 
tx.Rollback()  

// 否则,提交事务 
tx.Commit()

GORM提供了Transaction方法自动提交事务,避免用户漏写CommitRollback

db.Transaction(func(tx *gorm.DB) error {   
    // 在事务中执行一些 db 操作(从这里开始,使用 'tx' 而不是 'db')   
    if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {     
    // 返回任何错误都会回滚事务     
        return err   
    }    
    if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {     
        return err   
    }    
    // 返回 nil 提交事务   
    return nil 
})

1.10 Hook

Hook 是在创建、查询、更新、删除等操作之前、之后调用的函数。如果为模型定义了指定的方法,它会在创建、更新、查询、删除时自动被调用。如果任何回调返回错误,GORM 将停止后续的操作并回滚事务。钩子方法的函数签名为 func(*gorm.DB) error

参考资料

ORM是什么

GORM官方文档