go:GORM

399 阅读31分钟

Gorm 一个go开发中的常用ORM

安装使用步骤

使用 Gorm 的步骤如下:

  1. 安装 Gorm:可以使用 go get 命令安装 Gorm,如下所示:
go get -u gorm.io/gorm //安装GORM
go get -u gorm.io/driver/postgres	//安装postgres驱动程序

查看是否安装成功及版本信息

看起来你的系统不是基于 Linux 或 macOS,而是基于 Windows。在 Windows 系统中,命令行工具和 Linux 或 macOS 系统中的略有不同。你可以使用以下命令来检查 Gorm 是否安装成功:

go list -m all | findstr "gorm.io/gorm"

如果安装成功,会输出 Gorm 的版本信息,例如:

gorm.io/gorm v1.21.11

如果没有输出任何信息,说明 Gorm 没有安装成功。

另外,你也可以在代码中使用以下语句来输出 Gorm 的版本信息:

fmt.Println("Gorm version:", gorm.Version)

这样就可以在程序运行时输出 Gorm 的版本信息了。

  1. 导入 Gorm 包:在 Go 代码中导入 Gorm 包,如下所示:
import (
	"gorm.io/gorm"
	"gorm.io/driver/postgres"
)
  1. 配置数据库连接:在代码中配置数据库连接,如下所示:
	dsn := "host=123.57.166.115 user=postgres password=Nrec1234. dbname=postgres port=5432 sslmode=disable TimeZone=Asia/Shanghai"
	//dsn := "user:password@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	if err != nil {
        //处理错误
		panic(err)
	}

其中,dsn 是数据库连接字符串,包括用户名、密码、主机地址、端口号、数据库名等信息。在这个例子中,我们使用 pgsql 数据库,dsn 中的参数表示使用 UTF-8 编码,开启时间解析,使用本地时间。

  1. 定义数据模型:在代码中定义数据模型,如下所示:
type User struct {
    ID   uint
    Name string
    Age  int
}

在这个例子中,我们定义了一个 User 结构体,包括 ID、Name、Age 三个字段。

  1. 创建表格:在代码中使用 Gorm 自动创建表格,如下所示:
err := db.AutoMigrate(&User{})
if err != nil {
    // 处理错误
}

这个例子中,我们使用 db.AutoMigrate() 方法自动创建 User 表格。

  1. 插入数据:在代码中使用 Gorm 插入数据,如下所示:
user := User{Name: "Tom", Age: 18}
result := db.Create(&user)
if result.Error != nil {
    // 处理错误
}

这个例子中,我们创建了一个 User 对象,然后使用 db.Create() 方法将其插入到数据库中。

  1. 查询数据:在代码中使用 Gorm 查询数据,如下所示:
var users []User
result := db.Find(&users)
if result.Error != nil {
    // 处理错误
}

这个例子中,我们使用 db.Find() 方法查询所有的 User 记录,并将结果存储在一个 User 数组中。

  1. 更新数据:在代码中使用 Gorm 更新数据,如下所示:
result := db.Model(&user).Update("Name", "Jerry")
if result.Error != nil {
    // 处理错误
}

这个例子中,我们使用 db.Model() 方法指定要更新的数据模型,然后使用 Update() 方法更新 Name 字段的值。

  1. 删除数据:在代码中使用 Gorm 删除数据,如下所示:
result := db.Delete(&user)
if result.Error != nil {
    // 处理错误
}

这个例子中,我们使用 db.Delete() 方法删除指定的 User 记录。

以上就是使用 Gorm 的基本步骤,具体使用时可以根据实际情况进行调整。

完整代码

package main

import (
	"github.com/gin-gonic/gin"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

type User struct {
	ID   uint
	Name string
	Age  int
}

func main() {
	dsn := "host=123.57.166.115 user=postgres password=Nrec1234. dbname=postgres port=5432 sslmode=disable TimeZone=Asia/Shanghai"
	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	if err != nil {
		//panic(err)
	}

	err3 := db.AutoMigrate(&User{})
	if err3 != nil {
		// 处理错误
	}

	user := User{Name: "Tom", Age: 18}
	result := db.Create(&user)
	if result.Error != nil {
		// 处理错误
	}

	var users []User
	result2 := db.Find(&users)
	if result2.Error != nil {
		// 处理错误
	}

	result3 := db.Model(&user).Update("Name", "Jerry")
	if result3.Error != nil {
		// 处理错误
	}

	result4 := db.Delete(&user)
	if result4.Error != nil {
		// 处理错误
	}

	r := gin.Default()
	r.GET("/", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "Hello, World!",
		})
	})
	r.Run(":8083")
}

gorm.Open

gorm.Open() 函数的定义如下:

func Open(dialect string, dsn string, config *Config) (*DB, error)

其中,参数含义如下:

  • dialect:数据库方言,例如 mysqlpostgres 等。
  • dsn:数据源名称,包括连接数据库的用户名、密码、主机名、端口号、数据库名称等信息。
  • config:GORM 配置,包括连接池、日志等配置信息。这个参数需要传递一个 *Config 类型的指针。

函数返回值为一个 *DB 类型的指针和一个 error 类型的值。如果打开数据库连接成功,返回一个指向 *DB 类型的指针;否则返回一个非空的 error 类型的值,表示打开数据库连接失败的原因。

//&gorm.Config{}表示创建一个gorm.Config结构体,并将地址传入。因为gorm.Open的参数二需要一个指针
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})

这行代码使用 GORM 库来连接到 PostgreSQL 数据库。具体来说,它执行以下操作:

  1. 使用 postgres.Open(dsn) 函数创建一个 PostgreSQL 数据库驱动程序。
  2. 使用 gorm.Config{} 配置选项创建一个 GORM 数据库连接。
  3. 使用 gorm.Open() 函数将数据库驱动程序和 GORM 连接选项传递给 GORM 库,以打开与数据库的连接。
  4. 如果成功建立连接,则将连接存储在 db 变量中,否则将错误存储在 err 变量中。

需要注意的是,dsn 是连接字符串,它包含了数据库的连接信息,如主机地址、用户名、密码、数据库名等。在这里,我们使用 postgres.Open() 函数创建一个 PostgreSQL 数据库驱动程序,该函数需要一个连接字符串作为参数。&gorm.Config{} 是一个 GORM 配置选项,它告诉 GORM 库如何连接到数据库。

&gorm.Config{} 是 Go 语言中的一个结构体字面量(Struct Literal)。在 GORM 中,gorm.Config 是一个结构体类型,它包含了 GORM 库的配置选项。

& 符号用于获取结构体字面量的地址,即取址符。在这里,&gorm.Config{} 表示获取 gorm.Config 结构体字面量的地址。

{} 符号表示创建一个新的结构体实例,并将其初始化为默认值。在这里,我们创建了一个空的 gorm.Config 结构体实例,并将其传递给 gorm.Open() 函数,以便在打开数据库连接时使用。

因此,&gorm.Config{} 的语法表示创建一个新的 gorm.Config 结构体实例,并将其地址传递给 gorm.Open() 函数,以便在打开数据库连接时使用。

为什么加取址符?

在 GORM 中,gorm.Open() 函数的第二个参数需要传递一个 gorm.Config 类型的指针。因此,如果我们想要将一个 gorm.Config 类型的结构体实例传递给 gorm.Open() 函数,我们需要先获取该结构体实例的地址。

&gorm.Config{} 表示创建一个新的 gorm.Config 结构体实例,并返回该实例的地址。因此,我们可以将 &gorm.Config{} 作为参数传递给 gorm.Open() 函数,以便在打开数据库连接时使用。

总之,加取址符 & 是为了获取 gorm.Config 结构体实例的地址,以便在传递给 gorm.Open() 函数时使用。

常用方法属性

以下是 gorm 包中常用的方法和属性的关键信息表格:

方法/属性函数签名描述
Openfunc Open(dialect string, args ...interface{}) (*DB, error)打开数据库连接。返回一个 *DB实例。
DB.Preloadfunc (db *DB) Preload(query string, args ...interface{}) *DB使用预加载方式加载关联的模型数据。
DB.Firstfunc (db *DB) First(dest interface{}, conds ...interface{}) *DB从数据库中查询第一个匹配的记录,并将结果存储到 dest中。
DB.Createfunc (db *DB) Create(value interface{}) *DB在数据库中创建记录。
DB.Savefunc (db *DB) Save(value interface{}) *DB保存(插入/更新)记录到数据库中。
DB.Deletefunc (db *DB) Delete(value interface{}, conds ...interface{}) *DB从数据库中删除符合条件的记录。
DB.Wherefunc (db *DB) Where(query interface{}, args ...interface{}) *DB添加查询条件。
DB.Modelfunc (db *DB) Model(value interface{}) *DB指定模型对象,用于构建查询和操作。
DB.Tablefunc (db *DB) Table(name string) *DB指定表名,用于构建查询和操作。
DB.AutoMigratefunc (db *DB) AutoMigrate(dst ...interface{}) error根据模型定义自动迁移数据库结构。
DB.Transactionfunc (db *DB) Transaction(fc func(tx *gorm.DB) error) error开启数据库事务,并执行回调函数。
DB.Rawfunc (db *DB) Raw(sql string, values ...interface{}) *DB执行原生 SQL 查询。
DB.Execfunc (db *DB) Exec(sql string, values ...interface{}) *DB执行 SQL 语句。
DB.Setfunc (db *DB) Set(name string, value interface{}) *DB设置数据库连接的属性。
DB.Debugfunc (db *DB) Debug() *DB启用调试模式,输出执行的 SQL 语句。
Modelfunc Model(value interface{}) *gorm.DB指定模型对象,用于构建查询和操作。
Wherefunc (db *gorm.DB) Where(query interface{}, args ...interface{}) *gorm.DB添加查询条件。
Selectfunc (db *gorm.DB) Select(query string, args ...interface{}) *gorm.DB指定要查询的字段。
Joinsfunc (db *gorm.DB) Joins(query string, args ...interface{}) *gorm.DB连接其他表进行查询。
Orderfunc (db *gorm.DB) Order(value interface{}) *gorm.DB指定查询结果的排序方式。
Limitfunc (db *gorm.DB) Limit(limit int) *gorm.DB指定查询结果的最大记录数。
Offsetfunc (db *gorm.DB) Offset(offset int) *gorm.DB指定查询结果的偏移量。
Scanfunc (db *gorm.DB) Scan(dest interface{}) *gorm.DB扫描查询结果并将结果存储到 dest中。
Pluckfunc (db *gorm.DB) Pluck(column string, value interface{}) *gorm.DB从查询结果中提取指定列的值。
FirstOrCreatefunc (db *gorm.DB) FirstOrCreate(out interface{}, where ...interface{}) *gorm.DB查询第一个匹配记录,如果不存在则创建并返回。
Associationfunc (db *gorm.DB) Association(column string) *gorm.Association获取关联关系。
Preloadfunc (db *gorm.DB) Preload(column string, conditions ...interface{}) *gorm.DB预加载关联数据。
Tablefunc (db *gorm.DB) Table(name string) *gorm.DB指定表名,用于构建查询和操作。
AutoMigratefunc (db *gorm.DB) AutoMigrate(dst ...interface{}) error根据模型定义自动迁移数据库结构。
Debugfunc (db *gorm.DB) Debug() *gorm.DB启用调试模式,输出执行的 SQL 语句。
Errorfunc (db *gorm.DB) Error() error获取最后一次执行操作时的错误信息。
RowsAffectedfunc (db *gorm.DB) RowsAffected() int64获取最后一次执行操作受影响的行数。
Setfunc (db *gorm.DB) Set(key string, value interface{}) *gorm.DB设置数据库连接的属性。
Callbacktype Callback struct{}GORM 的回调函数管理器,用于注册和执行回调函数。
Associationtype Association struct{}关联关系的管理器,用于处理模型之间的关联操作。

请注意,这些方法和属性是 gorm 包中常用的,用于进行数据库查询、操作和模型关联等任务的函数和类型。

package main

import (
	"fmt"

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

type User struct {
	ID   uint
	Name string
	Age  int
}

func main() {
	// 连接数据库
	dsn := "user:password@tcp(127.0.0.1:3306)/database?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println("Failed to connect to database:", err)
		return
	}

	// 创建表结构
	err = db.AutoMigrate(&User{})
	if err != nil {
		fmt.Println("Failed to migrate table:", err)
		return
	}

	// 创建记录
	user := User{Name: "Alice", Age: 25}
	result := db.Create(&user)
	if result.Error != nil {
		fmt.Println("Failed to create record:", result.Error)
		return
	}
	fmt.Println("Created record:", user)

	// 查询记录
	var fetchedUser User
	result = db.First(&fetchedUser, "name = ?", "Alice")
	if result.Error != nil {
		fmt.Println("Failed to fetch record:", result.Error)
		return
	}
	fmt.Println("Fetched record:", fetchedUser)

	// 更新记录
	result = db.Model(&fetchedUser).Update("Age", 30)
	if result.Error != nil {
		fmt.Println("Failed to update record:", result.Error)
		return
	}
	fmt.Println("Updated record:", fetchedUser)

	// 删除记录
	result = db.Delete(&fetchedUser)
	if result.Error != nil {
		fmt.Println("Failed to delete record:", result.Error)
		return
	}
	fmt.Println("Deleted record:", fetchedUser)

	// 其他方法和属性示例
	db.Preload("Orders").Find(&user)                             // 预加载关联数据
	db.Model(&user).Association("Orders").Append(&Order{})        // 关联关系操作
	db.Where("name = ?", "Alice").Find(&users)                    // 添加查询条件
	db.Table("users").Select("name, age").Find(&result)           // 指定查询字段
	db.Joins("JOIN orders ON users.id = orders.user_id").Find(&result) // 连接其他表进行查询
	db.Order("age desc").Find(&users)                             // 指定查询结果排序方式
	db.Limit(10).Offset(5).Find(&users)                            // 指定查询结果的限制和偏移
	db.Scan(&user)                                                // 扫描查询结果并存储到对象中
	db.Pluck("name", &names)                                      // 提取查询结果中的指定列值
	db.FirstOrCreate(&user, "name = ?", "Alice")                   // 查询或创建记录
	db.Association("Orders").Find(&orders)                         // 获取关联关系
	db.Debug()                                                    // 启用调试模式
	db.Error()                                                    // 获取最后一次操作的错误信息
	db.RowsAffected()                                             // 获取最后一次操作受影响的行数
	db.Set("gorm:save_associations", false)                        // 设置数据库连接属性
}

// 其他定义的结构体和函数
type Order struct {
	ID     uint
	UserID uint
	Amount float64
}

func CallbackFunc(db *gorm.DB) {
	// 自定义的回调函数逻辑
}

func main() {
	db.Callback().Create().Before("gorm:create").Register("custom_callback", CallbackFunc) // 注册自定义回调函数
}

DB类型扩展的方法

gorm框架为DB类型增加了以下常用方法:

  1. Create(value interface{}) error:创建一个新的记录并将其插入数据库中。
type User struct {
	ID   uint
	Name string
	Age  int
}

user := User{Name: "Tom", Age: 18}
result := db.Create(&user)
  1. Save(value interface{}) error:如果记录已经存在,则更新记录;否则,创建一个新的记录。
  2. First(out interface{}, where ...interface{}) error:获取第一个匹配条件的记录并将其赋值给 out
  3. Find(out interface{}, where ...interface{}) error:获取所有匹配条件的记录并将其赋值给 out
var users []User
result2 := db.Find(&users)
  1. Update(column string, value interface{}) error:更新指定列的值。
result3 := db.Model(&user).Update("Name", "Jerry")
  1. Updates(values interface{}, ignoreProtectedAttrs ...bool) error:使用 mapstruct 更新记录的多个列。
  2. Delete(value interface{}, where ...interface{}) error:删除与条件匹配的记录。
//会删除所有满足user条件{Name: "Tom", Age: 18}的数据
result4 := db.Delete(&user)
  1. Model(value interface{}) *DB:为 DB 类型创建一个新的查询。
//&user代表操作以及存在的user对象
result3 := db.Model(&user).Update("Name", "Jerry")
  1. Table(name string) *DB:指定要查询的表名。
  2. Where(query interface{}, args ...interface{}) *DB:指定查询条件。
  3. Order(value interface{}, reorder ...bool) *DB:指定查询结果的排序方式。
  4. Limit(limit int) *DB:限制查询结果的数量。
  5. Offset(offset int) *DB:指定查询结果的偏移量。
  6. Select(query interface{}, args ...interface{}) *DB:指定要查询的列。
  7. Count(value interface{}) *DB:计算查询结果的数量。

这些方法可以通过 DB 类型调用,例如:

db := gorm.Open(...)
var user User
db.First(&user, "name = ?", "Alice")
db.Model(&user).Update("age", 30)

Model

db.Model() 方法的原型如下:

func (db *DB) Model(value interface{}) *DB

其中,db 是一个 *gorm.DB 类型的指针,表示数据库连接,value 是一个任意类型的参数,表示要操作的模型。

该方法返回一个 *gorm.DB 类型的指针,可以通过链式调用该指针上的其他方法来进行后续的操作。

在 GORM 中,db.Model() 方法用于指定要操作的模型。它的作用是将当前数据库连接与指定的模型关联起来,以便后续的操作可以针对该模型进行。

db.Model() 方法接受一个模型作为参数,例如:

db.Model(&User{})

上面的代码将当前数据库连接与 User 模型关联起来,以便后续的操作可以针对 User 模型进行。可以通过链式调用 db.Model() 方法来对同一个模型进行多个操作,例如:

db.Model(&User{}).Where("name = ?", "Alice").Update("email", "alice@example.com")

上面的代码将当前数据库连接与 User 模型关联起来,并使用 Where() 方法指定查询条件,然后使用 Update() 方法更新满足条件的记录的 email 字段。

db.Model(&User{})与db.Model(User{})

在 GORM 中,db.Model(&User{})db.Model(User{}) 是有区别的。

db.Model(&User{}) 传递的是 *User 类型的指针,表示要操作的是一个已经存在的 User 对象,而 db.Model(User{}) 传递的是 User 类型的值,表示要操作的是一个新的、未经过初始化的 User 对象。

因此,如果你想要操作已经存在的 User 对象,应该使用 db.Model(&User{}),如果你想要操作新的、未经过初始化的 User 对象,应该使用 db.Model(User{})

错误用法:db.Model(User)

db.Model(User) 是不正确的用法,因为 Model 方法需要传入一个指针类型的参数,而 User 是一个结构体类型,不是指针类型。如果你要操作 User 对象,应该使用 &User{}new(User) 创建一个指向 User 对象的指针,然后传递给 Model 方法。

需要传入一个值,不能传入一个类型。。

AutoMigrate

type User struct {
	ID   uint
	Name string
	Age  int
}
err3 := db.AutoMigrate(&User{})

db.AutoMigrate(&User{}) 是 gorm 中用于自动迁移数据库的方法。它的作用是根据 User 结构体的定义自动创建对应的数据表,并自动更新表结构。

具体来说,AutoMigrate 方法会执行以下操作:

  1. 检查数据库中是否已经存在名为 users 的表。如果不存在,则创建该表。
  2. 检查表结构是否与 User 结构体的定义匹配。如果不匹配,则根据 User 结构体的定义自动更新表结构。

对于 User 结构体,AutoMigrate 方法会创建一个名为 users 的数据表,并为该表创建 idnameage 三个字段。如果该表已经存在,AutoMigrate 方法会检查表结构是否与 User 结构体的定义匹配,并自动更新表结构以保持一致。

AutoMigrate 方法可以一次性自动迁移多个结构体,例如:

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

这样就会自动创建 usersproductsorders 三个数据表,并为每个表创建对应的字段。

需要注意的是,AutoMigrate 方法只会创建缺失的表和字段,不会删除多余的表和字段。如果需要删除多余的表和字段,可以使用 db.DropTableIfExists(&User{})db.Migrator().DropColumn(&User{}, "Age") 等方法。

Create、CreateInBatches

db.Create(&user) 是 GORM 中用于向数据库中插入一条记录的方法。其中,&user 是一个指向 User 结构体的指针,表示要插入的数据。

下面是一个示例:

user := User{Name: "Tom", Age: 18, Email: "tom@example.com"}
result := db.Create(&user)
if result.Error != nil {
    // 处理错误
}

在这个示例中,我们创建了一个名为 userUser 结构体,并设置了 NameAgeEmail 字段的值。然后,我们调用了 db.Create(&user) 方法将 user 对象插入到数据库中。如果插入过程中发生错误,result.Error 将不为 nil,我们可以在代码中处理这个错误。

需要注意的是,db.Create(&user) 方法会将 user 对象的所有字段都插入到数据库中,包括 ID 字段。如果 ID 字段没有设置值,GORM 会自动生成一个唯一的值作为 ID

生成ID的规则:

如果你使用 GORM 默认的 ID 字段,它会自动为每个新插入的记录生成一个唯一的 ID 值。这个 ID 值可以是一个整数、字符串或 UUID 等类型,具体的类型取决于你在模型中定义的字段类型。

默认情况下,GORM 使用自增的方式生成整数类型的 ID 值。也就是说,每当你插入一条新记录时,GORM 会自动为这条记录分配一个比前一条记录的 ID 值大 1 的值。如果你希望使用其他类型的 ID 值,可以在模型中定义一个对应的字段,并在插入记录时手动为其赋值。

下面是一个示例,展示了如何手动为 ID 字段赋值:

type User struct {
    ID    string `gorm:"primary_key"`
    Name  string
    Email string
}

user := User{
    ID:    "123",
    Name:  "Tom",
    Email: "tom@example.com",
}
result := db.Create(&user)
if result.Error != nil {
    // 处理错误
}

在这个示例中,我们手动为 ID 字段赋值为 "123",然后调用 db.Create(&user) 方法将 user 对象插入到数据库中。注意,这里的 ID 字段使用了 gorm:"primary_key" 标签,表示它是这个模型的主键。如果你不希望使用 ID 字段作为主键,可以在模型中定义一个其他的字段,并使用 gorm:"primary_key" 标签来指定它。

另外,如果要插入多条记录,可以使用 db.CreateInBatches 方法,它可以一次性插入多条记录,从而提高插入的效率。

db.CreateInBatches() 是 GORM 提供的一个批量插入数据的方法。它可以一次性插入多条记录,从而提高插入数据的效率。

CreateInBatches() 方法的函数签名如下:

func (db *DB) CreateInBatches(value interface{}, batchSize int) (result *DB)

其中,value 参数表示要插入的数据,可以是一个结构体切片或指针切片。batchSize 参数表示每批次插入的记录数。

下面是一个示例,展示了如何使用 CreateInBatches() 方法批量插入数据:

type User struct {
    ID    uint
    Name  string
    Email string
}

users := []User{
    {Name: "Tom", Email: "tom@example.com"},
    {Name: "Jerry", Email: "jerry@example.com"},
    {Name: "Mike", Email: "mike@example.com"},
    {Name: "Lucy", Email: "lucy@example.com"},
}

result := db.CreateInBatches(&users, 2)
if result.Error != nil {
    // 处理错误
}

在这个示例中,我们定义了一个 User 结构体,并创建了一个包含 4 条记录的切片。然后,我们调用 db.CreateInBatches(&users, 2) 方法将这些记录分成 2 批次插入到数据库中。这意味着,每批次插入 2 条记录,一共需要插入 2 批次。

需要注意的是,CreateInBatches() 方法只适用于插入数据,不能用于更新数据或删除数据。如果你需要批量更新或删除数据,可以考虑使用 db.Model().Updates()db.Delete() 方法。

DropTableIfExists、DropColumn

//删除表
db.DropTableIfExists(&User{})

//删除字段
db.Migrator().DropColumn(&User{}, "Age")

Delete

db.Delete 是 GORM 框架中的一个方法,用于从数据库中删除符合条件的记录。它的基本语法如下:

db.Delete(&user)

其中,db 是一个 *gorm.DB 类型的对象,表示要进行删除操作的数据库连接;&user 是一个指向要删除记录的指针。

你可以通过在 Delete 方法中传入一个条件表达式来删除符合条件的记录。例如,下面的代码将删除 age 大于等于 18 的所有用户记录:

db.Where("age >= ?", 18).Delete(&User{})

这里,Where 方法指定了一个条件表达式,? 是占位符,它将被后面的参数 18 替换。Delete 方法的参数是一个指向要删除记录的指针,这里传入的是 &User{},表示要删除 User 表中符合条件的所有记录。

使用参数2指定条件

Delete 方法的第二个参数 where 是一个可变参数,用于指定删除记录的条件。它可以是一个字符串,也可以是一个结构体、map、slice 等类型。如果省略该参数,则会删除表中的所有记录。

where 参数是一个字符串时,它表示一个 SQL 条件表达式,例如:

db.Delete(&User{}, "age >= ?", 18)

这条语句将删除 User 表中所有 age 大于等于 18 的记录。

where 参数是一个结构体、map、slice 等类型时,它表示一个查询条件。例如:

db.Delete(&User{}, map[string]interface{}{"name": "Tom", "age": 18})

这条语句将删除 User 表中所有 name 为 "Tom" 且 age 为 18 的记录。

如果需要同时指定多个条件,可以使用 ANDOR 连接它们,例如:

db.Delete(&User{}, "name = ? AND age >= ?", "Tom", 18)

这条语句将删除 User 表中所有 name 为 "Tom" 且 age 大于等于 18 的记录。

需要注意的是,Delete 方法返回的是一个 error 类型的值,如果执行删除操作时发生错误,将返回相应的错误信息。

Update、Updates

result3 := db.Model(&user).Update("Name", "Jerry")

db.Model(&user).Update("Name", "Jerry") 是 gorm 中用于更新数据的方法。它的作用是将 user 对象在数据库中对应的记录的 Name 字段更新为 "Jerry"

具体来说,Model(&user) 方法用于指定要更新的数据表和记录。在这个例子中,它会根据 user 对象的类型自动推断出要更新的数据表为 users,并根据 user 对象的 ID 属性自动推断出要更新的记录。

Update("Name", "Jerry") 方法用于指定要更新的字段和值。在这个例子中,它会将 users 表中 IDuser.ID 的记录的 Name 字段更新为 "Jerry"

Update 方法还支持同时更新多个字段,例如:

db.Model(&user).Updates(User{Name: "Jerry", Age: 30})

这样就会将 users 表中 IDuser.ID 的记录的 Name 字段更新为 "Jerry"Age 字段更新为 30

需要注意的是,Update 方法只会更新指定的字段,不会更新其他字段。如果需要更新所有字段,可以使用 Updates 方法。另外,如果要更新的字段值与原来的值相同,Update 方法不会执行任何操作。

Updates 方法与 Update 方法类似,都是用于更新数据的方法,但是 Updates 方法可以同时更新多个字段。下面是一个使用 Updates 方法更新数据的例子:

//会更新所有字段,而不仅仅是 User结构体中设置了值的字段。应该可以加参数来忽略更新某些字段
db.Model(&user).Updates(User{Name: "Jerry", Age: 30})

在这个例子中,User{Name: "Jerry", Age: 30} 是一个 User 类型的结构体,它的 Name 字段被设置为 "Jerry"Age 字段被设置为 30Updates 方法会将 users 表中 IDuser.ID 的记录的 NameAge 字段更新为对应的值。

需要注意的是,Updates 方法会更新所有指定的字段,而不仅仅是 User 结构体中设置了值的字段。如果要更新的字段值与原来的值相同,Updates 方法不会执行任何操作。

更新受保护的字段

在 GORM 中,Updates() 方法用于更新模型的某些字段。第二个参数 ignoreProtectedAttrs 是一个可选参数,用于指定是否忽略受保护的属性。默认情况下,受保护的属性是不会被更新的,除非在模型中明确指定了这些属性可以被更新。如果将 ignoreProtectedAttrs 参数设置为 true,则受保护的属性也会被更新。

以下是一个使用 Updates() 方法的示例:

// 定义一个模型
type User struct {
    ID        uint   `gorm:"primaryKey"`
    Name      string `gorm:"not null"`
    Email     string `gorm:"not null;unique"`
    Password  string `gorm:"not null"`
    CreatedAt time.Time
    UpdatedAt time.Time
}

// 更新模型的某些字段
err := db.Model(&User{}).Where("id = ?", 1).Updates(map[string]interface{}{
    "name": "Alice",
    "email": "alice@example.com",
}, true).Error
if err != nil {
    // 处理错误
}

在上面的示例中,Updates() 方法被用于更新 User 模型的 nameemail 字段。第二个参数 true 指定了忽略受保护的属性。这意味着即使 User 模型中的 Password 字段被标记为受保护的属性,它也会被更新。

为什么参数2是ignoreProtectedAttrs ...bool,而不是ignoreProtectedAttrs bool

在 Golang 中,参数列表中的 ... 表示该参数是可变参数。可变参数可以接受任意数量的参数,这些参数会被打包成一个切片。在 GORM 中,ignoreProtectedAttrs 参数就是一个可变参数,因为它可以接受零个或多个布尔值作为参数。

如果 ignoreProtectedAttrs 参数的类型是 bool,那么它只能接受一个布尔值作为参数。但是,GORM 的 Updates() 方法需要支持忽略多个受保护的属性,因此需要一个可变参数来接受多个布尔值。

在调用 Updates() 方法时,可以传递一个或多个布尔值作为 ignoreProtectedAttrs 参数。例如,以下代码演示了如何同时更新多个模型并忽略它们的受保护属性:

err := db.Model(&User{}).Updates(
    User{Name: "Alice", Email: "alice@example.com"},
    User{Name: "Bob", Email: "bob@example.com"},
    true, true,
).Error
if err != nil {
    // 处理错误
}

在上面的示例中,Updates() 方法被用于同时更新两个 User 模型,并忽略它们的受保护属性。最后的两个布尔值表示忽略受保护的属性。

Save

db.Save() 是 GORM 提供的一个保存数据的方法。它可以用于插入新数据或更新已有数据,根据数据的主键或唯一索引来自动判断是插入还是更新操作。

Save() 方法的函数签名如下:

func (db *DB) Save(value interface{}) (result *DB)

其中,value 参数表示要保存的数据,可以是一个结构体或指针。如果该数据已经存在于数据库中,Save() 方法会自动更新该数据;否则,它会自动插入一条新记录。

下面是一个示例,展示了如何使用 Save() 方法插入新数据或更新已有数据:

type User struct {
    ID    uint
    Name  string
    Email string
}

// 插入新数据
user1 := User{Name: "Tom", Email: "tom@example.com"}
result := db.Save(&user1)
if result.Error != nil {
    // 处理错误
}

// 更新已有数据
user2 := User{ID: 1, Name: "Jerry", Email: "jerry@example.com"}
result = db.Save(&user2)
if result.Error != nil {
    // 处理错误
}

在这个示例中,我们定义了一个 User 结构体,并创建了两个实例 user1user2。第一个实例表示要插入一条新记录,第二个实例表示要更新 ID 为 1 的记录。然后,我们调用 db.Save(&user1) 方法将 user1 插入到数据库中;接着,我们调用 db.Save(&user2) 方法更新 ID 为 1 的记录。

需要注意的是,如果数据的主键或唯一索引不存在,Save() 方法会自动插入一条新记录。如果你想强制更新数据,可以使用 db.Model().Updates() 方法或 db.Model().Update() 方法。

First

db.First() 是 GORM 提供的一个查询方法,用于查询符合条件的第一条记录。它的函数签名如下:

func (db *DB) First(dest interface{}, conds ...interface{}) (result *DB)

其中,dest 参数表示查询结果将被存储到的目标对象,可以是一个结构体或指针;conds 参数表示查询条件,可以是一个字符串或结构体。如果省略 conds 参数,First() 方法将返回表中的第一条记录。

下面是一个示例,展示了如何使用 First() 方法查询符合条件的第一条记录:

type User struct {
    ID    uint
    Name  string
    Email string
}

// 查询 ID 为 1 的用户
var user User
result := db.First(&user, 1)
if result.Error != nil {
    // 处理错误
}

// 查询名字为 Tom 的用户
result = db.First(&user, "name = ?", "Tom")
if result.Error != nil {
    // 处理错误
}

在这个示例中,我们定义了一个 User 结构体,并创建了一个变量 user。然后,我们调用 db.First(&user, 1) 方法查询 ID 为 1 的用户,并将结果存储到 user 变量中;接着,我们调用 db.First(&user, "name = ?", "Tom") 方法查询名字为 Tom 的用户。

需要注意的是,如果查询结果为空,First() 方法将返回 record not found 错误。如果你不想返回错误,可以使用 db.Where().First() 方法或 db.Take() 方法,它们在查询结果为空时会返回零值。

Where

db.First() 方法可以支持复杂查询,可以通过链式调用 Where()Order()Limit() 等方法来添加查询条件、排序和限制记录数。下面是一个示例,展示了如何使用 db.First() 方法进行复杂查询:

type User struct {
    ID    uint
    Name  string
    Email string
    Age   int
}

// 查询年龄最小的用户
var user User
result := db.Order("age asc").First(&user)
if result.Error != nil {
    // 处理错误
}

// 查询名字为 Tom 的年龄最小的用户
result = db.Where("name = ?", "Tom").Order("age asc").First(&user)

//查询名字为 Tom,年龄为 12 的用户
//询条件中的 ? 占位符将会被传递给 Where() 方法的第二个参数,按照顺序依次替换
result := db.Where("name = ? AND age = ?", "Tom", 12).First(&user)
if result.Error != nil {
    // 处理错误
}

//用结构体指定查询条件
db.Where(map[string]interface{}{"age": 18, "name": "Alice"}).Find(&users)

在这个示例中,我们定义了一个 User 结构体,并创建了一个变量 user。然后,我们调用 db.Order("age asc").First(&user) 方法查询年龄最小的用户,并将结果存储到 user 变量中;接着,我们调用 db.Where("name = ?", "Tom").Order("age asc").First(&user) 方法查询名字为 Tom 的年龄最小的用户。

需要注意的是,如果查询结果为空,First() 方法将返回 record not found 错误。如果你不想返回错误,可以使用 db.Where().First() 方法或 db.Take() 方法,它们在查询结果为空时会返回零值。

Order

在 GORM 中,可以使用 DB.Order 方法指定查询结果的排序方式。该方法的用法如下:

func (db *DB) Order(value interface{}, reorder ...bool) *DB

其中,value 参数是排序规则,可以是一个字符串或一个 map[string]interface{} 类型的结构体。reorder 参数是可选的,用于指定是否覆盖之前的排序规则。

例如,假设要查询 users 表中所有用户,并按照 age 字段升序排序,可以使用以下代码:

db := gorm.Open("mysql", "user:password@tcp(127.0.0.1:3306)/database_name?charset=utf8mb4&parseTime=True&loc=Local")
var users []User
db.Order("age asc").Find(&users)

在上面的代码中,首先使用 gorm.Open 方法打开了一个 MySQL 数据库连接,并将其赋值给 db 变量。然后,使用 db.Order 方法指定查询结果按照 age 字段升序排序,并将查询结果保存到 users 变量中。

除了字符串形式的排序规则,还可以使用 map[string]interface{} 类型的结构体指定排序规则。例如,以下代码与上面的代码功能相同:

db.Order(map[string]interface{}{"age": "asc"}).Find(&users)

在上面的代码中,使用了一个 map[string]interface{} 类型的结构体指定排序规则。其中,map 中的键为字段名,值为排序方式。这样可以更清晰地指定排序规则。

参数2用法

reorder 参数用于指定是否覆盖之前的排序规则。如果不指定该参数,默认值为 false,即不覆盖之前的排序规则。

例如,假设先使用 Order 方法指定了一个排序规则,然后又使用 Order 方法指定了另一个排序规则,可以通过设置 reorder 参数来控制是否覆盖之前的排序规则。

db.Order("age asc").Order("name desc", true).Find(&users)

在上面的代码中,第一个 Order 方法指定了按照 age 字段升序排序,第二个 Order 方法指定了按照 name 字段降序排序,并将 reorder 参数设置为 true,表示覆盖之前的排序规则。这样,查询结果将先按照 name 字段降序排序,然后再按照 age 字段升序排序。

需要注意的是,如果多次使用 Order 方法指定了多个排序规则,而且其中某个排序规则的 reorder 参数设置为 true,那么之前指定的所有排序规则都会被覆盖。

Limit、Offset

DB.Limit 是 GORM 提供的一个链式调用方法,用于指定查询结果的最大返回行数。它的语法如下:

func (db *DB) Limit(limit int) *DB

其中,limit 参数表示最大返回行数。例如,我们可以这样使用 DB.Limit 方法来查询前 10 条用户记录:

db.Limit(10).Find(&users)

这样,查询结果就会最多返回 10 条用户记录。需要注意的是,如果查询结果的总行数不足 10 行,那么就会返回所有的行。另外,如果我们想要指定查询结果的偏移量,可以使用 DB.Offset 方法。例如,如果我们想要查询从第 11 行开始的 10 条用户记录,可以这样写:

db.Offset(10).Limit(10).Find(&users)

这样,查询结果就会从第 11 行开始,最多返回 10 条用户记录。

Find

DB.Find() 是 GORM 中的一个方法,用于查询符合条件的多条记录。

使用 DB.Find() 方法需要传入一个指向切片的指针,GORM 会将查询结果填充到这个切片中。同时,你也需要提供查询条件。

下面是一个使用 DB.Find() 方法的示例:

type User struct {
    ID    uint
    Name  string
    Email string
    Age   int
}

var users []User
result := db.Where("age > ?", 18).Find(&users)
if result.Error != nil {
    // 处理错误
}

在这个示例中,我们查询了年龄大于 18 岁的所有用户,并将查询结果填充到了 users 切片中。

需要注意的是,如果查询结果为空,Find() 方法会返回一个空的切片,而不是一个错误。

Table

在 GORM 中,可以使用 DB.Table 方法指定要操作的数据库表。该方法的用法如下:

func (db *DB) Table(name string) *DB

其中,name 参数是要操作的数据库表的名称。该方法会返回一个新的 *DB 对象,该对象指定了要操作的数据库表。

例如,假设有一个名为 users 的数据库表,可以使用以下代码指定要操作该表:

db := gorm.Open("mysql", "user:password@tcp(127.0.0.1:3306)/database_name?charset=utf8mb4&parseTime=True&loc=Local")
usersDB := db.Table("users")

在上面的代码中,首先使用 gorm.Open 方法打开了一个 MySQL 数据库连接,并将其赋值给 db 变量。然后,使用 db.Table 方法指定要操作的数据库表为 users,并将返回的新的 *DB 对象赋值给 usersDB 变量。

接下来,就可以使用 usersDB 对象进行各种数据库操作了,例如查询、插入、更新、删除等。

Select

DB.Select 是 GORM 提供的一个链式调用方法,用于指定查询结果需要返回的字段。它的语法如下:

func (db *DB) Select(query interface{}, args ...interface{}) *DB

其中,query 参数表示需要返回的字段,可以是一个字符串,也可以是一个结构体。如果是一个字符串,那么它应该是一个逗号分隔的字段列表,例如:

db.Select("name, age, email").Find(&users)

这样,查询结果就只会返回 nameageemail 这三个字段的值。如果 query 参数是一个结构体,那么 GORM 会根据结构体的字段信息自动生成需要返回的字段列表,例如:

type User struct {
    ID    uint
    Name  string
    Age   int
    Email string
}

var user User
db.Select(&user).First(&user)

这样,查询结果就只会返回 IDNameAgeEmail 这四个字段的值。需要注意的是,如果 query 参数是一个结构体指针,那么 GORM 会根据结构体的类型自动生成表名(自动生成的表名可能是User)。如果表名与结构体类型不同,可以使用 DB.Table 方法来指定表名,例如:

db.Table("my_users").Select(&user).First(&user)

这样,GORM 就会在 my_users 表中查询数据,并返回 IDNameAgeEmail 这四个字段的值。

Count

DB.Count 是 GORM 提供的一个链式调用方法,用于查询符合条件的记录数量。它的语法如下:

func (db *DB) Count(count *int64) *DB

其中,count 参数是一个 int64 类型的指针,用于存储查询结果的数量。例如:

var count int64
db.Model(&User{}).Where("age > ?", 18).Count(&count)

这样,就会查询出年龄大于 18 岁的用户数量,并将结果存储在 count 变量中。需要注意的是,DB.Count 方法必须与 DB.ModelDB.Table 方法一起使用,以指定查询的数据表。