简介
官方文档:https://gorm.io/zh_CN/docs/index.html
github :https://github.com/go-gorm/gorm
同其他语言的ORM框架一样,这是Go 版本的
本文全部以mysql
为例
sql 脚本
为了方便测试,这里准备一份sql 脚本,来创建数据库和表
CREATE DATABASE makalo_test;
USE makalo_test;
CREATE TABLE `test` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',
`user_name` varchar(50) DEFAULT '' COMMENT '用户名',
`password` varchar(50) DEFAULT '' COMMENT '密码',
`description` varchar(500) DEFAULT '' COMMENT '描述',
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`delete_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
`update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='makalo 测试表';
连接mysql 数据库
官方
https://gorm.io/zh_CN/docs/connecting_to_the_database.html
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
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{})
}
可以看到上面引入两个库
- "gorm.io/driver/mysql"
- "gorm.io/gorm"
那我们将这两个库下载并添加进去
go get gorm.io/gorm
go get gorm.io/driver/mysql
连接本地mysql 数据库示例
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
// dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
dsn := "root:root@tcp(127.0.0.1:3306)/makalo_test?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
fmt.Printf("db => %+v, err => %+v", db, err)
}
最开始数据库不存在,也有错误提示,按提示修改即可
model
官方model 更多用法:https://gorm.io/zh_CN/docs/models.html#embedded_struct
GO 操作数据库是需要将model 模型创建出来的,并不想think-php 不用写,自动映射,我这里例子还是字段比较少的,如果字段多了,这也太费时间了,所以推荐一个自动生成的
可以直接将建表语句转为GO 的model 结构体,那我们这张表的model如下:
可以看到连Tag 都写好了 太贴心了
// makalo 测试表
type Test struct {
Id uint64 `gorm:"column:id" db:"id" json:"id" form:"id"` //自增id
UserName string `gorm:"column:user_name" db:"user_name" json:"user_name" form:"user_name"` //用户名
Password string `gorm:"column:password" db:"password" json:"password" form:"password"` //密码
Description string `gorm:"column:description" db:"description" json:"description" form:"description"` //描述
CreateAt int64 `gorm:"column:create_at" db:"create_at" json:"create_at" form:"create_at"` //创建时间
DeleteAt int64 `gorm:"column:delete_at" db:"delete_at" json:"delete_at" form:"delete_at"` //删除时间
UpdateAt int64 `gorm:"column:update_at" db:"update_at" json:"update_at" form:"update_at"` //更新时间
}
但是需要注意的是gorm.Model 的约定
GORM 倾向于约定优于配置 默认情况下,GORM 使用
ID
作为主键,使用结构体名的蛇形复数
作为表名,字段名的蛇形
作为列名,并使用CreatedAt
、UpdatedAt
字段追踪创建、更新时间如果您遵循 GORM 的约定,您就可以少写的配置、代码。 如果约定不符合您的实际要求,GORM 允许你配置它们
但是因为跟他默认约定有点不一样,所以还是要改点配置的,改完后
// makalo 测试表
type Test struct {
// 指定 主键 并 指定数据库列名
Id uint64 `gorm:"primaryKey" gorm:"column:id" db:"id" json:"id" form:"id"` //自增id
UserName string `gorm:"column:user_name" db:"user_name" json:"user_name" form:"user_name"` //用户名
Password string `gorm:"column:password" db:"password" json:"password" form:"password"` //密码
Description string `gorm:"column:description" db:"description" json:"description" form:"description"` //描述
CreateAt time.Time `gorm:"column:create_at" db:"create_at" json:"create_at" form:"create_at"` //创建时间
DeleteAt gorm.DeletedAt `gorm:"index gorm:"column:delete_at" db:"delete_at" json:"delete_at" form:"delete_at"` //删除时间
UpdateAt time.Time `gorm:"column:update_at" db:"update_at" json:"updated_at" form:"update_at"` //更新时间
}
表名设置
GORM 使用结构体名的 蛇形命名
作为表名。对于结构体 User
,根据约定,其表名为 users
也就是说 GORM 会默认将你的 model 结构体 转小写 并加上 复数 s
TableName
您可以实现 Tabler
接口来更改默认表名,例如:
type Tabler interface {
TableName() string
}
// TableName 会将 User 的表名重写为 `profiles`
func (User) TableName() string {
return "profiles"
}
注意:
TableName
不支持动态变化,它会被缓存下来以便后续使用。想要使用动态表名,你可以使用Scopes
,例如:
func UserTable(user User) func (tx *gorm.DB) *gorm.DB {
return func (tx *gorm.DB) *gorm.DB {
if user.Admin {
return tx.Table("admin_users")
}
return tx.Table("users")
}
}
db.Scopes(UserTable(user)).Create(&user)
示例
只需实现 TableName() 接口即可
// TableName 会将 tests 的表名重写为 `test`
func (Test) TableName() string {
return "test"
}
临时指定表名
您可以使用 Table
方法临时指定表名,例如:
// 根据 User 的字段创建 `deleted_users` 表
db.Table("deleted_users").AutoMigrate(&User{})
// 从另一张表查询数据
var deletedUsers []User
db.Table("deleted_users").Find(&deletedUsers)
// SELECT * FROM deleted_users;
db.Table("deleted_users").Where("name = ?", "jinzhu").Delete(&User{})
// DELETE FROM deleted_users WHERE name = 'jinzhu';
查看 from 子查询 了解如何在 FROM 子句中使用子查询
插入
官方文档:
https://gorm.io/zh_CN/docs/create.html
我的示例
package main
import (
"fmt"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// makalo 测试表
type Test struct {
// 指定 主键 并 指定数据库列名
Id uint64 `gorm:"primaryKey" gorm:"column:id" db:"id" json:"id" form:"id"` //自增id
UserName string `gorm:"column:user_name" db:"user_name" json:"user_name" form:"user_name"` //用户名
Password string `gorm:"column:password" db:"password" json:"password" form:"password"` //密码
Description string `gorm:"column:description" db:"description" json:"description" form:"description"` //描述
CreateAt time.Time `gorm:"column:create_at" db:"create_at" json:"create_at" form:"create_at"` //创建时间
DeleteAt gorm.DeletedAt `gorm:"index gorm:"column:delete_at" db:"delete_at" json:"delete_at" form:"delete_at"` //删除时间
UpdateAt time.Time `gorm:"column:update_at" db:"update_at" json:"updated_at" form:"update_at"` //更新时间
}
// TableName 会将 tests 的表名重写为 `test`
func (Test) TableName() string {
return "test"
}
func main() {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
// dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
dsn := "root:root@tcp(127.0.0.1:3306)/makalo_test?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
fmt.Printf("db => %+v, err => %+v \n ", db, err)
test := Test{
UserName: "makalo",
Password: "password",
Description: "贼鸡儿帅",
}
// 通过数据的指针来插入
// 插入所有字段
// result := db.Create(&test)
// 只插入给出的字段
result := db.Select("UserName", "Password", "Description").Create(&test)
// test.Id // 返回插入数据的主键
// result.Error // 返回 error
// result.RowsAffected // 返回插入记录的条数
fmt.Printf("id => %d, error => %+v, RowsAffected => %+v", test.Id, result.Error, result.RowsAffected)
}
输出
数据库
删除
官方:https://gorm.io/zh_CN/docs/delete.html
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);
// 带额外条件的删除
db.Where("name = ?", "jinzhu").Delete(&email)
// DELETE from emails where id = 10 AND name = "jinzhu";
示例
// 删除
db.Delete(&Test{}, 1)
软删除
如果您的模型包含了一个 gorm.deletedat
字段(gorm.Model
已经包含了该字段),它将自动获得软删除的能力!
拥有软删除能力的模型调用 Delete
时,记录不会从数据库中被真正删除。但 GORM 会将 DeletedAt
置为当前时间, 并且你不能再通过普通的查询方法找到该记录。
// 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;
如果您不想引入 gorm.Model
,您也可以这样启用软删除特性:
type User struct {
ID int
Deleted gorm.DeletedAt
Name string
}
查找被软删除的记录
您可以使用 Unscoped
找到被软删除的记录
db.Unscoped().Where("age = 20").Find(&users)
// SELECT * FROM users WHERE age = 20;
永久删除
您也可以使用 Unscoped
永久删除匹配的记录
db.Unscoped().Delete(&order)
// DELETE FROM orders WHERE id=10;
修改
官方:https://gorm.io/zh_CN/docs/update.html
保存所有字段
Save
会保存所有的字段,即使字段是零值
db.First(&user)
user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)
// UPDATE users SET name='jinzhu 2', age=100, birthday='2016-01-01', updated_at = '2013-11-17 21:34:10' WHERE id=111;
更新单个列
当使用 Update
更新单列时,需要有一些条件,否则将会引起错误 ErrMissingWhereClause
,查看 阻止全局更新 了解详情。 当使用 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
和 map[string]interface{}
参数。当使用 struct
更新时,默认情况下,GORM 只会更新非零值的字段
// 根据 `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, "active": false})
// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
注意 当使用 struct 进行更新时,GORM 只会更新非零值的字段。 你可以使用
map
更新字段,或者使用Select
指定要更新的字段
更新选定字段
如果您想要在更新时选定、忽略某些字段,您可以使用 Select
、Omit
// Select with Map
// User's ID is `111`:
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello' WHERE id=111;
db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
// Select with Struct (select zero value fields)
db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})
// UPDATE users SET name='new_name', age=0 WHERE id=111;
// Select all fields (select all fields include zero value fields)
db.Model(&user).Select("*").Updates(User{Name: "jinzhu", Role: "admin", Age: 0})
// Select all fields but omit Role (select all fields include zero value fields)
db.Model(&user).Select("*").Omit("Role").Updates(User{Name: "jinzhu", Role: "admin", Age: 0})
增删改的完整代码
package main
import (
"fmt"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// makalo 测试表
type Test struct {
// 指定 主键 并 指定数据库列名
Id uint64 `gorm:"primaryKey" gorm:"column:id" db:"id" json:"id" form:"id"` //自增id
UserName string `gorm:"column:user_name" db:"user_name" json:"user_name" form:"user_name"` //用户名
Password string `gorm:"column:password" db:"password" json:"password" form:"password"` //密码
Description string `gorm:"column:description" db:"description" json:"description" form:"description"` //描述
CreateAt time.Time `gorm:"column:create_at" db:"create_at" json:"create_at" form:"create_at"` //创建时间
DeleteAt gorm.DeletedAt `gorm:"index gorm:"column:delete_at" db:"delete_at" json:"delete_at" form:"delete_at"` //删除时间
UpdateAt time.Time `gorm:"column:update_at" db:"update_at" json:"updated_at" form:"update_at"` //更新时间
}
// TableName 会将 tests 的表名重写为 `test`
func (Test) TableName() string {
return "test"
}
func main() {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
// dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
dsn := "root:root@tcp(127.0.0.1:3306)/makalo_test?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
fmt.Printf("db => %+v, err => %+v \n ", db, err)
/* test := Test{
UserName: "makalo",
Password: "password",
Description: "贼鸡儿帅",
} */
// 通过数据的指针来插入
// 插入所有字段
// result := db.Create(&test)
// 只插入给出的字段
// result := db.Select("UserName", "Password", "Description").Create(&test)
// test.Id // 返回插入数据的主键
// result.Error // 返回 error
// result.RowsAffected // 返回插入记录的条数
// fmt.Printf("id => %d, error => %+v, RowsAffected => %+v", test.Id, result.Error, result.RowsAffected)
// 删除
// 因为配置 DeleteAt 字段所以会被软删除
// db.Delete(&Test{}, 1)
// 查找被软删除的记录
// result := db.Unscoped().Where("id = 1").Find(&test)
// fmt.Printf("test => %d, result => %+v", test, result)
// 永久删除
// db.Unscoped().Delete(&test)
// 更新
// db.Model(&Test{}).Where("user_name", "makalo").Update("description", "猛男")
}
查询
查询太多了,我把官网的copy 过来了,可以自己试下,感觉都有用
官网:https://gorm.io/zh_CN/docs/query.html
检索单个对象
GORM 提供了 First
、Take
、Last
方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1
条件,且没有找到记录时,它会返回 ErrRecordNotFound
错误
// 获取第一条记录(主键升序)
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;
// 获取一条记录,没有指定排序字段
db.Take(&user)
// SELECT * FROM users LIMIT 1;
// 获取最后一条记录(主键降序)
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;
result := db.First(&user)
result.RowsAffected // 返回找到的记录数
result.Error // returns error or nil
// 检查 ErrRecordNotFound 错误
errors.Is(result.Error, gorm.ErrRecordNotFound)
如果你想避免
ErrRecordNotFound
错误,你可以使用Find
,比如db.Limit(1).Find(&user)
,Find
方法可以接受struct和slice的数据。
Using
Find
without a limit for single objectdb.Find(&user)
will query the full table and return only the first object which is not performant and nondeterministic
The First
and Last
methods will find the first and last record (respectively) as ordered by primary key. They only work when a pointer to the destination struct is passed to the methods as argument or when the model is specified using db.Model()
. Additionally, if no primary key is defined for relevant model, then the model will be ordered by the first field. For example:
var user User
var users []User
// works because destination struct is passed in
db.First(&user)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1
// works because model is specified using `db.Model()`
result := map[string]interface{}{}
db.Model(&User{}).First(&result)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1
// doesn't work
result := map[string]interface{}{}
db.Table("users").First(&result)
// works with Take
result := map[string]interface{}{}
db.Table("users").Take(&result)
// no primary key defined, results will be ordered by first field (i.e., `Code`)
type Language struct {
Code string
Name string
}
db.First(&Language{})
// SELECT * FROM `languages` ORDER BY `languages`.`code` LIMIT 1
根据主键检索
Objects can be retrieved using primary key by using Inline Conditions if the primary key is a number. When working with strings, extra care needs to be taken to avoid SQL Injection; check out Security section for details.
db.First(&user, 10)
// SELECT * FROM users WHERE id = 10;
db.First(&user, "10")
// SELECT * FROM users WHERE id = 10;
db.Find(&users, []int{1,2,3})
// SELECT * FROM users WHERE id IN (1,2,3);
If the primary key is a string (for example, like a uuid), the query will be written as follows:
db.First(&user, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a")
// SELECT * FROM users WHERE id = "1b74413f-f3b8-409f-ac47-e8c062e3472a";
当目标对象有一个主键值时,将使用主键构建查询条件,例如:
var user = User{ID: 10}
db.First(&user)
// SELECT * FROM users WHERE id = 10;
var result User
db.Model(User{ID: 10}).First(&result)
// SELECT * FROM users WHERE id = 10;
检索全部对象
// Get all records
result := db.Find(&users)
// SELECT * FROM users;
result.RowsAffected // returns found records count, equals `len(users)`
result.Error // returns error
条件
String 条件
// Get first matched record
db.Where("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;
// Get all matched records
db.Where("name <> ?", "jinzhu").Find(&users)
// SELECT * FROM users WHERE name <> 'jinzhu';
// IN
db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
// SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');
// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
// SELECT * FROM users WHERE name LIKE '%jin%';
// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
// SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';
// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
// SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';
If the object’s primary key has been set, then condition query wouldn’t cover the value of primary key but use it as a ‘and’ condition. For example:
var user = User{ID: 10} db.Where("id = ?", 20).First(&user) // SELECT * FROM users WHERE id = 10 and id = 20 ORDER BY id ASC LIMIT 1
This query would give
record not found
Error. So set the primary key attribute such asid
to nil before you want to use the variable such asuser
to get new value from database.
Struct & 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);
NOTE When querying with struct, GORM will only query with non-zero fields, that means if your field’s value is
0
,''
,false
or other zero values, it won’t be used to build query conditions, for example:
db.Where(&User{Name: "jinzhu", Age: 0}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu";
To include zero values in the query conditions, you can use a map, which will include all key-values as query conditions, for example:
db.Where(map[string]interface{}{"Name": "jinzhu", "Age": 0}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 0;
For more details, see Specify Struct search fields.
指定结构体查询字段
When searching with struct, you can specify which particular values from the struct to use in the query conditions by passing in the relevant field name or the dbname to Where()
, for example:
db.Where(&User{Name: "jinzhu"}, "name", "Age").Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 0;
db.Where(&User{Name: "jinzhu"}, "Age").Find(&users)
// SELECT * FROM users WHERE age = 0;
内联条件
Query conditions can be inlined into methods like First
and Find
in a similar way to Where
.
// Get by primary key if it were a non-integer type
db.First(&user, "id = ?", "string_primary_key")
// SELECT * FROM users WHERE id = 'string_primary_key';
// Plain SQL
db.Find(&user, "name = ?", "jinzhu")
// SELECT * FROM users WHERE name = "jinzhu";
db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20)
// SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;
// Struct
db.Find(&users, User{Age: 20})
// SELECT * FROM users WHERE age = 20;
// Map
db.Find(&users, map[string]interface{}{"age": 20})
// SELECT * FROM users WHERE age = 20;
Not 条件
Build NOT conditions, works similar to Where
db.Not("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE NOT name = "jinzhu" ORDER BY id LIMIT 1;
// Not In
db.Not(map[string]interface{}{"name": []string{"jinzhu", "jinzhu 2"}}).Find(&users)
// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");
// Struct
db.Not(User{Name: "jinzhu", Age: 18}).First(&user)
// SELECT * FROM users WHERE name <> "jinzhu" AND age <> 18 ORDER BY id LIMIT 1;
// Not In slice of primary keys
db.Not([]int64{1,2,3}).First(&user)
// SELECT * FROM users WHERE id NOT IN (1,2,3) ORDER BY id LIMIT 1;
Or 条件
db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';
// Struct
db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2", Age: 18}).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);
// Map
db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2", "age": 18}).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);
For more complicated SQL queries. please also refer to Group Conditions in Advanced Query.
选择特定字段
Select
allows you to specify the fields that you want to retrieve from database. Otherwise, GORM will select all fields by default.
db.Select("name", "age").Find(&users)
// SELECT name, age FROM users;
db.Select([]string{"name", "age"}).Find(&users)
// SELECT name, age FROM users;
db.Table("users").Select("COALESCE(age,?)", 42).Rows()
// SELECT COALESCE(age,'42') FROM users;
Also check out Smart Select Fields
排序
Specify order when retrieving records from the database
db.Order("age desc, name").Find(&users)
// SELECT * FROM users ORDER BY age desc, name;
// Multiple orders
db.Order("age desc").Order("name").Find(&users)
// SELECT * FROM users ORDER BY age desc, name;
db.Clauses(clause.OrderBy{
Expression: clause.Expr{SQL: "FIELD(id,?)", Vars: []interface{}{[]int{1, 2, 3}}, WithoutParentheses: true},
}).Find(&User{})
// SELECT * FROM users ORDER BY FIELD(id,1,2,3)
Limit & Offset
Limit
specify the max number of records to retrieve Offset
specify the number of records to skip before starting to return the records
db.Limit(3).Find(&users)
// SELECT * FROM users LIMIT 3;
// Cancel limit condition with -1
db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
// SELECT * FROM users LIMIT 10; (users1)
// SELECT * FROM users; (users2)
db.Offset(3).Find(&users)
// SELECT * FROM users OFFSET 3;
db.Limit(10).Offset(5).Find(&users)
// SELECT * FROM users OFFSET 5 LIMIT 10;
// Cancel offset condition with -1
db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
// SELECT * FROM users OFFSET 10; (users1)
// SELECT * FROM users; (users2)
Refer to Pagination for details on how to make a paginator
Group By & Having
type result struct {
Date time.Time
Total int
}
db.Model(&User{}).Select("name, sum(age) as total").Where("name LIKE ?", "group%").Group("name").First(&result)
// SELECT name, sum(age) as total FROM `users` WHERE name LIKE "group%" GROUP BY `name` LIMIT 1
db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)
// SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"
rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
defer rows.Close()
for rows.Next() {
...
}
rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
defer rows.Close()
for rows.Next() {
...
}
type Result struct {
Date time.Time
Total int64
}
db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results)
Distinct
Selecting distinct values from the model
db.Distinct("name", "age").Order("name, age desc").Find(&results)
Distinct
works with Pluck
and Count
too
Joins
Specify Joins conditions
type result struct {
Name string
Email string
}
db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
// SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id
rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
for rows.Next() {
...
}
db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)
// multiple joins with parameter
db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "[email protected]").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)
Joins 预加载
You can use Joins
eager loading associations with a single SQL, for example:
db.Joins("Company").Find(&users)
// SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` LEFT JOIN `companies` AS `Company` ON `users`.`company_id` = `Company`.`id`;
Join with conditions
db.Joins("Company", db.Where(&Company{Alive: true})).Find(&users)
// SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` LEFT JOIN `companies` AS `Company` ON `users`.`company_id` = `Company`.`id` AND `Company`.`alive` = true;
For more details, please refer to Preloading (Eager Loading).
Joins 一个衍生表
You can also use Joins
to join a derived table.
type User struct {
Id int
Age int
}
type Order struct {
UserId int
FinishedAt *time.Time
}
query := db.Table("order").Select("MAX(order.finished_at) as latest").Joins("left join user user on order.user_id = user.id").Where("user.age > ?", 18).Group("order.user_id")
db.Model(&Order{}).Joins("join (?) q on order.finished_at = q.latest", query).Scan(&results)
// SELECT `order`.`user_id`,`order`.`finished_at` FROM `order` join (SELECT MAX(order.finished_at) as latest FROM `order` left join user user on order.user_id = user.id WHERE user.age > 18 GROUP BY `order`.`user_id`) q on order.finished_at = q.latest
Scan
Scanning results into a struct works similarly to the way we use Find
type Result struct {
Name string
Age int
}
var result Result
db.Table("users").Select("name", "age").Where("name = ?", "Antonio").Scan(&result)
// Raw SQL
db.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)
原生SQL
官网:https://gorm.io/zh_CN/docs/sql_builder.html
原生查询 SQL 和 Scan
type Result struct {
ID int
Name string
Age int
}
var result Result
db.Raw("SELECT id, name, age FROM users WHERE name = ?", 3).Scan(&result)
db.Raw("SELECT id, name, age FROM users WHERE name = ?", 3).Scan(&result)
var age int
db.Raw("SELECT SUM(age) FROM users WHERE role = ?", "admin").Scan(&age)
var users []User
db.Raw("UPDATE users SET name = ? WHERE age = ? RETURNING id, name", "jinzhu", 20).Scan(&users)
Exec
原生 SQL
db.Exec("DROP TABLE users")
db.Exec("UPDATE orders SET shipped_at = ? WHERE id IN ?", time.Now(), []int64{1, 2, 3})
// Exec with SQL Expression
db.Exec("UPDATE users SET money = ? WHERE name = ?", gorm.Expr("money * ? + ?", 10000, 1), "jinzhu")
SQL 生成器
官网:https://gorm.io/zh_CN/docs/sql_builder.html#DryRun-模式
DryRun 模式
在不执行的情况下生成 SQL
及其参数,可以用于准备或测试生成的 SQL,详情请参考 Session
stmt := db.Session(&Session{DryRun: true}).First(&user, 1).Statement
stmt.SQL.String() //=> SELECT * FROM `users` WHERE `id` = $1 ORDER BY `id`
stmt.Vars //=> []interface{}{1}
ToSQL
返回生成的 SQL
但不执行。
GORM使用 database/sql 的参数占位符来构建 SQL 语句,它会自动转义参数以避免 SQL 注入,但我们不保证生成 SQL 的安全,请只用于调试。
sql := DB.ToSQL(func(tx *gorm.DB) *gorm.DB {
return tx.Model(&User{}).Where("id = ?", 100).Limit(10).Order("age desc").Find(&[]User{})
})
sql //=> SELECT * FROM "users" WHERE id = 100 AND "users"."deleted_at" IS NULL ORDER BY age desc LIMIT 10
Row
& Rows
获取 *sql.Row
结果
// 使用 GORM API 构建 SQL
row := db.Table("users").Where("name = ?", "jinzhu").Select("name", "age").Row()
row.Scan(&name, &age)
// 使用原生 SQL
row := db.Raw("select name, age, email from users where name = ?", "jinzhu").Row()
row.Scan(&name, &age, &email)
获取 *sql.Rows
结果
// 使用 GORM API 构建 SQL
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows()
defer rows.Close()
for rows.Next() {
rows.Scan(&name, &age, &email)
// 业务逻辑...
}
// 原生 SQL
rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows()
defer rows.Close()
for rows.Next() {
rows.Scan(&name, &age, &email)
// 业务逻辑...
}
转到 FindInBatches 获取如何在批量中查询和处理记录的信息, 转到 Group 条件 获取如何构建复杂 SQL 查询的信息
将 sql.Rows
扫描至 model
使用 ScanRows
将一行记录扫描至 struct,例如:
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
defer rows.Close()
var user User
for rows.Next() {
// ScanRows 将一行扫描至 user
db.ScanRows(rows, &user)
// 业务逻辑...
}
连接
在一条 tcp DB 连接中运行多条 SQL (不是事务)
db.Connection(func(tx *gorm.DB) error {
tx.Exec("SET my.role = ?", "admin")
tx.First(&User{})
})
高级
子句(Clause)
GORM 内部使用 SQL builder 生成 SQL。对于每个操作,GORM 都会创建一个 *gorm.Statement
对象,所有的 GORM API 都是在为 statement
添加、修改 子句
,最后,GORM 会根据这些子句生成 SQL
例如,当通过 First
进行查询时,它会在 Statement
中添加以下子句
clause.Select{Columns: "*"}
clause.From{Tables: clause.CurrentTable}
clause.Limit{Limit: 1}
clause.OrderByColumn{
Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},
}
然后 GORM 在 Query
callback 中构建最终的查询 SQL,像这样:
Statement.Build("SELECT", "FROM", "WHERE", "GROUP BY", "ORDER BY", "LIMIT", "FOR")
生成 SQL:
SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1
您可以自定义 子句
并与 GORM 一起使用,这需要实现 Interface 接口
可以参考 示例
子句构造器
不同的数据库, 子句可能会生成不同的 SQL,例如:
db.Offset(10).Limit(5).Find(&users)
// SQL Server 会生成
// SELECT * FROM "users" OFFSET 10 ROW FETCH NEXT 5 ROWS ONLY
// MySQL 会生成
// SELECT * FROM `users` LIMIT 5 OFFSET 10
之所以支持 Clause,是因为 GORM 允许数据库驱动程序通过注册 Clause Builder 来取代默认值,这儿有一个 Limit 的示例
子句选项
GORM 定义了很多 子句,其中一些 子句提供了你可能会用到的选项
尽管很少会用到它们,但如果你发现 GORM API 与你的预期不符合。这可能可以很好地检查它们,例如:
db.Clauses(clause.Insert{Modifier: "IGNORE"}).Create(&user)
// INSERT IGNORE INTO users (name,age...) VALUES ("jinzhu",18...);
StatementModifier
GORM 提供了 StatementModifier 接口,允许您修改语句,使其符合您的要求,这儿有一个 Hint 示例
import "gorm.io/hints"
db.Clauses(hints.New("hint")).Find(&User{})
// SELECT * /*+ hint */ FROM `users
标签:users,gorm,db,id,user,使用,Go,Gorm,name
From: https://www.cnblogs.com/makalochen/p/17101130.html