[后端与 gorm | 青训营笔记]

113 阅读3分钟

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

Gorm 的使用

gorm 是一个已经迭代了十多年的ORM框架,在字节跳动被广泛使用。

使用gorm连接mysql的案例

import (   
    "gorm.io/driver/mysql"  
    "gorm.io/gorm" 
)  
func main() {   
    // refer https://github.com/go-sql-driver/mysql#dsn-data-source-name for details   
    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{}) 
}

为了正确处理time.Time,您需要包括parseTime作为参数。 为了完全支持UTF-8编码,需要将charset=utf8更改为charset=utf8mb4。 GORM使用database/sql 去维持连接池

sqlDB, err := db.DB()  // SetMaxIdleConns sets the maximum number of connections in the idle connection pool. 
sqlDB.SetMaxIdleConns(10)  // SetMaxOpenConns sets the maximum number of open connections to the database. 
sqlDB.SetMaxOpenConns(100)  // SetConnMaxLifetime sets the maximum amount of time a connection may be reused. 
sqlDB.SetConnMaxLifetime(time.Hour)

创建

创建记录

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

选定区域创建记录

db.Select("Name", "Age", "CreatedAt").Create(&user) 
// INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775") 

非选定区域创建记录

db.Omit("Name", "Age", "CreatedAt").Create(&user) 
// INSERT INTO `users` (`birthday`,`updated_at`) VALUES ("2020-01-01 00:00:00.000", "2020-07-04 11:05:21.775") 

查询

使用First查询时,当查询不到数据时,会返回ErrRecordNotFound,使用Find查询数据时,查询不到数据不会返回错误。当使用结构体查询数据时,GORM只会查询非0字段,这意味着,如果查询字段值为0,“”,false或者其他0值,该字段不会用于构建查询字段。建议使用map查询。

// Struct 
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1; 
// Map 
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users) 
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;  
// Slice of primary keys 
db.Where([]int64{20, 21, 22}).Find(&users) 
// SELECT * FROM users WHERE id IN (20, 21, 22);

更新数据

使用struct更新时,只会更新非0值,如果需要更新非0值建议使用map更新

// Update attributes with `struct`, will only update non-zero fields  
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;  
// Update attributes with `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; 

删除数据

软删除

// user's ID is `111` 
db.Delete(&user) 
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;  
// Batch Delete 
db.Where("age = ?", 20).Delete(&User{}) 
// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;  
// Soft deleted records will be ignored when querying 
db.Where("age = 20").Find(&user) 
// SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;

如果你的模型包括一个gorm。DeletedAt字段(包含在gorm.Model中),它将自动获得软删除能力! 当调用Delete时,记录不会从数据库中删除,但是GORM会将DeletedAt的值设置为当前时间,并且数据不能再用普通的查询方法找到。

事务

GORM在事务内部执行写(创建/更新/删除)操作,以确保数据的一致性,如果不需要,可以在初始化时禁用它,在此之后您将获得约30%以上的性能提升

// Globally disable 
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{ 
   SkipDefaultTransaction: true, 
})  
// Continuous session mode 
tx := db.Session(&Session{SkipDefaultTransaction: true}) 
tx.First(&user, 1) 
tx.Find(&users) 
tx.Model(&user).Update("Age", 18)

GORM支持嵌套事务,你可以回滚在一个大事务范围内执行的操作的子集,例如:

DB.Transaction(func(tx *gorm.DB) error {   
   tx.Create(&user1)
   tx.Transaction(func(tx2 *gorm.DB) error {
   tx2.Create(&user2)
   return errors.New("rollback user2") // Rollback user2
   })
   tx.Transaction(func(tx2 *gorm.DB) error {
   tx2.Create(&user3)
   return nil   
   })
   return nil })
   // Commit user1, user3

kitex 是字节跳动内部的golang微服务框架。 Hertz 是字节跳动内部的HTTP框架。