gorm Update方法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gorm Update方法相关的知识,希望对你有一定的参考价值。

参考技术A gorm ,字节 jinzhu 开源的ORM(Object Relational Mapping)项目。

如果直接使用struct类型(表的映射对象)进行更新时,零值将被忽略。加入字段之前有值,现在需要更新为零值。这是直接使用struct进行更新是无效的。零值被忽略之后,相当于字段还保留有旧值。

这种情况下,需要将struct转换为 map[string]interface 之后再更新。使用 structs.Map(*struct) 将struct转换为map格式再调用 Update 方法即可, 参考这里 。这个问题也是类似情况 Update method does not update zero value

GORM CRUD 5 分钟快速上手

文章目录

1.ORM 是什么

ORM(Object Relational Mapping),中文名为对象关系映射。

使用 ORM 组件,可以让开发者通过操作对象的方式完成对数据库的操作(读写),避免手动书写 SQL 和完成数据到对象的转换,让我们更方便的操作数据库。

理论上 ORM 可以让我们脱离 SQL,但实际上还是需要懂 SQL 才能更好地使用 ORM。

2.GORM 是什么

GORM 是一个流行的 Golang ORM 库。

类似于 Java 生态里大家听到过的 Mybatis、Hibernate、SpringData 等。

GORM 由国人开发,中文文档齐全,对开发者友好,支持主流关系型数据库。

  • MySQL
  • SQL Server
  • PostgreSQL
  • SQlite

GORM 功能丰富齐全:

  • 关联 (拥有一个,拥有多个,属于,多对多,多态,单表继承)
  • 钩子(before/after create/save/update/delete/find)
  • 支持 Preload、Joins 的预加载
  • 事务,嵌套事务,保存点,回滚到保存点
  • Context、预编译模式、DryRun 模式
  • 批量插入,FindInBatches,使用 Map Find/Create,使用 SQL 表达式、Context Valuer 进行 CRUD
  • SQL 构建器,Upsert,锁,Optimizer/Index/Comment Hint,命名参数,子查询
  • 复合主键,索引,约束
  • 自动迁移
  • 自定义 Logger
  • 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
  • 每个特性都经过了测试的重重考验
  • 开发者友好

GORM 最新源码地址:go-gorm/gorm

GORM V1 版本地址:jinzhu/gorm

GORM 中文文档地址:这里

本文将对 GORM 中常用的功能进行讲解,帮助你快速上手。

当然除了 GORM,你还有其他选择,比如 facebook-entsqlxsqlc 等。

3.安装

基于 Go Module 开发,import 最新包然后 go get 即可。

go get -u gorm.io/gorm

// 不同 DB 对应的驱动
go get -u gorm.io/driver/sqlite
go get -u gorm.io/driver/mysql
go get -u gorm.io/driver/postgres
go get -u gorm.io/driver/sqlserver

驱动包按照自己实际使用的 DB 选择即可。

本文将以 MySQL 为例,讲解 GORM 的使用。

4.连接 DB

以 MySQL 为例,建立数据库连接。

import (
	"fmt"

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

// MySQLConn GORM MySQL 连接。
var MySQLConn *gorm.DB

// Init gorm mysql connnection.
// 依赖服务配置初始化完成。
func InitMySQLConn() error 
	// data source name.
	dsn := fmt.Sprintf("%v:%v@tcp(%v:%v)/%v?charset=utf8mb4&parseTime=True&loc=Local", Conf.Mysql.User, Conf.Mysql.Passwd, Conf.Mysql.IP, Conf.Mysql.Port, Conf.Mysql.Dbname)
	var err error
	MySQLConn, err = gorm.Open(mysql.Open(dsn))
	return err

填入 DB 对应的正确的用户名、密码、地址、端口、数据库名称等信息后,便可建立对应数据源的连接。相关配置一般在服务启动时,事先从配置文件中加载。

5.创建数据表

在进行增查改删(CRUD)之前,需要先创建一个数据表。

GORM 中一个 struct 对应一张数据库表,对应的 struct 被称为模型。

假如我们要创建一张商品(goods)表,那么 struct 可定义为:

// Good 商品。
type Good struct 
	gorm.Model
	Name  string `gorm:"type:varchar(255) not null"`
	Price int    `gorm:"type:bigint not null"`

其中 gorm.Model 时 GORM 预先定义的一些基础字段,我们可以嵌入直接拿来用。

// Model a basic GoLang struct which includes the following fields: ID, CreatedAt, UpdatedAt, DeletedAt
// It may be embedded into your model or you may build your own model without it
//
//	type User struct 
//	  gorm.Model
//	
type Model struct 
	ID        uint `gorm:"primarykey"`
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt DeletedAt `gorm:"index"`

字段后的 tag 用来定义字段在 DB 中的相关属性,如 primarykey 表示主键,index 表示索引,type 表示字段类型。

除此以外,还有更加丰富的标签定义参见官方文档:字段标签

一般在服务启动时创建数据表,如建立 DB 连接后只执行一次来完成数据表的创建。

db.AutoMigrate(&User)

db.AutoMigrate(&User, &Product, &Order)

// 创建表时添加后缀
db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User)

比如创建我们上面的商品表。

// 自动创建表,如果表已经存在不会有任何动作。
err := MySQLConn.AutoMigrate(&Good)

创建好后的数据表名为 struct 名称的 snake_case 复数形式,字段名为 struct 字段的 sanke_case 形式。

创建好的表结构如下:

6.增加(Create)

// createGood 插入商品。
func createGood(name string, price int) 
	task := &Good
		Name:   name,
		Price: price,
	
	result := MySQLConn.Create(task)
	if result.Error != nil 
		return 
	
	return nil

主键 ID 会自增,此外 GORM 还会自动维护 created_at、updated_ad 和 deleted_at 三个字段。

7.查询(Read)

比如按照主键查询。

// getGoodByID 根据主键检索。
func getGoodByID (id int) (Good, error) 
	var good Good
	result := MySQLConn.First(&good, id)
	return good, result.Error

如果查不到,将报 “not found error” 错误。

再如按照其他字段进行 and 查询。

// getGoods 根据商品信息分页拉取。
func getTaskTypesByInfo(name string, price int, last_id uint) ([]Good, error) 
	db := internal.MySQLConn
	if name != "" 
		db = db.Where("name = ?", req.TypeInfo.Name)
	
	db = db.Where("price >= ?", price)
	
	// 按照每页大小 50 拉取商品。
	db.Where("id > ?", last_id).Order("id asc").Limit(50)

	var goods []Good
	result := db.Find(&goods)
	return goods, result.Error

还有很多查询方式,比如按照 struct、map 指定查询字段以及 or 和 not 条件等,具体请参考官方文档 GORM 查询

8.更新(Update)

比如更新所有字段。

使用 Save 方法更新所有字段,即使是零值也会更新。

// 先根据 ID  查询。
db.First(&good, 1)

// 再修改值。
good.Name = "小米"

// 最后写回。
db.Save(&user)

再如更新单列。

注意,当使用 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 方法支持 structmap[string]interface 参数。当使用 struct 更新时,默认情况下,GORM 只会更新非零值的字段。

// 根据 `struct` 更新属性,只会更新非零值的字段
db.Model(&user).Updates(UserName: "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, "active": false)
// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;

9.删除(Delete)

如删除一条记录。

删除一条记录时,删除对象需要指定主键,否则会触发 批量 Delete,例如:

// 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";

再如根据主键删除。

GORM 允许通过主键(可以是复合主键)和内联条件来删除对象,它可以使用数字,也可以使用字符串。

db.Delete(&User, 10)
// DELETE FROM users WHERE id = 10;

db.Delete(&User, "10")
// DELETE FROM users WHERE id = 10;

db.Delete(&users, []int1,2,3)
// DELETE FROM users WHERE id IN (1,2,3);

注意:

如果您的模型包含了一个 gorm.DeletedAt 字段(gorm.Model 已经包含了该字段),它将自动获得软删除的能力!

如果您不想引入 gorm.Model,您也可以这样启用软删除特性:

type User struct 
  ID      int
  Deleted gorm.DeletedAt
  Name    string

拥有软删除能力的模型调用 Delete 时,记录不会被数据库。但 GORM 会将 DeletedAt 置为当前时间, 并且你不能再通过普通的查询方法找到该记录。

使用 Unscoped 方法查找被软删除的数据。

db.Unscoped().Where("user_name = gry").Find(&users)

要想物理删除,使用 Unscoped 方法永久删除数据。

user.ID = 14
db.Unscoped().Delete(&user)

10.小结

本文简单介绍了 ORM、GORM、以及 GORM 连接数据库,创建数据表和 CRUD 的简单操作,帮忙新手快速上手。

更多用法,请参见官方文档 GORM 指南,这里有你想要的一切。


参考文献

GORM 指南| GORM - GORM
GORM 极速入门- 卢振千的博客
19-Gorm入门到精通- 刘清政 - 博客园
Go组件学习——gorm四步带你搞定DB增删改查 - 掘金

以上是关于gorm Update方法的主要内容,如果未能解决你的问题,请参考以下文章

Django 的 select_for_update 方法是不是与 update 方法一起使用?

Python 字典(Dictionary) update()方法

Python 字典 update() 方法

怎样使用Hibernate中的merge方法 merge方法和update方法的区别

Golang之ORM框架Gorm快速开始

Golang之ORM框架Gorm快速开始