【GORM】快速入门GORM,使用GORM进行CURD
- 1.GORM Model定义
- 模型的基本使用
- 主键、表名、列名的约定
- 创建表
- 2.创建表记录
- 3.查询记录
- 检索单个对象
- 用主键检索
- 检索全部对象
- 条件
- 限制和偏移
- 4.更新
- 简要更新
- 更新单个列
- 5.删除
- 删除一条记录
- 根据主键删除
- 软删除和物理删除
1.GORM Model定义
模型的基本使用
约定:GORM 倾向于约定,而不是配置。默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间
为了方便模型定义,GORM内置了一个gorm.Model结构体。gorm.Model是一个包含了ID, CreatedAt, UpdatedAt, DeletedAt四个字段的Golang结构体
// gorm.Model 定义
type Model struct {
ID uint `gorm:"primary_key"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
}你可以将它嵌入到你自己的模型中:
// 将 `ID`, `CreatedAt`, `UpdatedAt`, `DeletedAt`字段注入到`User`模型中
type User struct {
gorm.Model
Name string
}模型定义示例:
type User struct {
gorm.Model
Name string
Age sql.NullInt64
Birthday *time.Time
Email string `gorm:"type:varchar(100);unique_index"`
Role string `gorm:"size:255"` // 设置字段大小为255
MemberNumber *string `gorm:"unique;not null"` // 设置会员号(member number)唯一并且不为空
Num int `gorm:"AUTO_INCREMENT"` // 设置 num 为自增类型
Address string `gorm:"index:addr"` // 给address字段创建名为addr的索引
IgnoreMe int `gorm:"-"` // 忽略本字段
}使用结构体声明模型时,标记(tags)是可选项。gorm支持以下标记:
支持的结构体标记(Struct tags):

关联相关标记(tags):

主键、表名、列名的约定
GORM 默认会使用名为ID的字段作为表的主键
type User struct {
ID string // 名为`ID`的字段会默认作为表的主键
Name string
}
// 使用`AnimalID`作为主键
type Animal struct {
AnimalID int64 `gorm:"primary_key"`
Name string
Age int64
}表名默认就是结构体名称的复数,例如:
type User struct {} // 默认表名是 `users`
// 将 User 的表名设置为 `profiles`
func (User) TableName() string {
return "profiles"
}
func (u User) TableName() string {
if u.Role == "admin" {
return "admin_users"
} else {
return "users"
}
}
// 禁用默认表名的复数形式,如果置为 true,则 `User` 的默认表名是 `user`
db.SingularTable(true)列名由字段名称进行下划线分割来生成:
type User struct {
ID uint // column name is `id`
Name string // column name is `name`
Birthday time.Time // column name is `birthday`
CreatedAt time.Time // column name is `created_at`
}可以使用结构体tag指定列名:
type Animal struct {
AnimalId int64 `gorm:"column:beast_id"` // set column name to `beast_id`
Birthday time.Time `gorm:"column:day_of_the_beast"` // set column name to `day_of_the_beast`
Age int64 `gorm:"column:age_of_the_beast"` // set column name to `age_of_the_beast`
}创建表
有了表的结构体,创建表就很简单了
package main
import (
"database/sql"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
"time"
)
type User struct {
gorm.Model
Name string
Age sql.NullInt64
Birthday *time.Time
Email string `gorm:"type:varchar(100);unique_index"`
Role string `gorm:"size:255"` // 设置字段大小为255
MemberNumber *string `gorm:"unique;not null"` // 设置会员号(member number)唯一并且不为空
Num int `gorm:"AUTO_INCREMENT"` // 设置 num 为自增类型
Address string `gorm:"index:addr"` // 给address字段创建名为addr的索引
IgnoreMe int `gorm:"-"` // 忽略本字段
}
func main() {
db, err := gorm.Open("mysql", "root:root@(localhost)/gorm-test?charset=utf8mb4&parseTime=True&loc=Local")
if err != nil {
panic(err)
}
defer db.Close()
// 自动迁移
db.AutoMigrate(&User{})
}创建的结果是这样的:

2.创建表记录
普通的创建:
package main
import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
type User struct {
gorm.Model
Name string
Age int
}
func main() {
db, err := gorm.Open("mysql", "root:root@(localhost)/gorm-test?charset=utf8mb4&parseTime=True&loc=Local")
if err != nil {
panic(err)
}
defer db.Close()
user := User{Name: "dahe", Age: 22}
result := db.Create(&user)
fmt.Println(result.RowsAffected) // 返回插入记录的条数
if result.Error == nil { // 检测error
fmt.Println("no errors!!!")
}
}批量插入:
要有效地插入大量记录,请将一个 slice 传递给 Create 方法。 GORM 将生成单独一条SQL语句来插入所有数据,并回填主键的值,钩子方法也会被调用。
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type User struct {
gorm.Model
Name string
Age int
}
func main() {
dsn := "root:root@tcp(127.0.0.1:3306)/gorm-test?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
var userInfo = []User{{Name: "qianyue"}, {Name: "王伟"}, {Name: "李四"}}
result := db.Create(&userInfo)
fmt.Println(result.RowsAffected) // 返回插入记录的条数
if result.Error == nil { // 检测error
fmt.Println("no errors!!!")
}
}使用 CreateInBatches 分批创建时,你可以指定每批的数量,例如:
// 数量为 100
db.CreateInBatches(users, 100)3.查询记录
检索单个对象
GORM 提供了 First、Take、Last 方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1 条件,且没有找到记录时,它会返回 ErrRecordNotFound 错误
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type User struct {
gorm.Model
Name string
Age int
}
func main() {
dsn := "root:root@tcp(127.0.0.1:3306)/gorm-test?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
var user User
result := db.First(&user) // 查询表的第一行
fmt.Println(user) // 输出查询的结果
fmt.Println(result.RowsAffected) // 返回插入记录的条数
if result.Error == nil { // 检测error
fmt.Println("no errors!!!")
}
}// 获取第一条记录(主键升序)
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;
// 检查 ErrRecordNotFound 错误
errors.Is(result.Error, gorm.ErrRecordNotFound)
First和Last会根据主键排序,分别查询第一条和最后一条记录。 只有在目标 struct 是指针或者通过db.Model()指定 model 时,该方法才有效。 此外,如果相关 model 没有定义主键,那么将按 model的第一个字段进行排序
用主键检索
如果主键是数字类型,您可以使用 内联条件 来检索对象。 传入字符串参数时,需要特别注意 SQL 注入问题
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);如果主键是字符串(例如像 uuid),查询将被写成这样:
db.First(&user, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a")
// SELECT * FROM users WHERE id = "1b74413f-f3b8-409f-ac47-e8c062e3472a";检索全部对象
// Get all records
result := db.Find(&users)
// SELECT * FROM users;
var users []User
result := db.Find(&users) // 查询全部结果
fmt.Println(users) // 输出查询的结果条件
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';内联条件:
// 如果是非整数类型,则通过主键获取
db.First(&user, "id = ?" , "string_primary_key" )
// SELECT * FROM users WHERE id = 'string_primary_key';
// 普通 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 条件:
构建 NOT 条件,工作方式类似于Where
db.Not( "name = ?" , "jinzhu" ).First(&user)
// SELECT * FROM users WHERE NOT name = "jinzhu" ORDER BY id LIMIT 1;
// 不在
db.Not( map [ string ] interface {}{ "name" : [] string { "jinzhu" , "jinzhu 2" }}).Find(&users)
// SELECT * FROM users WHERE name NOT IN (“jinzhu”、“jinzhu2”);
// Struct
db.Not(User{Name: "jinzhu" , Age: 18 }).First(&user)
// SELECT * FROM users WHERE name <> "jinzhu" AND age <> 18 ORDER BY id LIMIT 1;
// 不在主键切片中
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 (姓名 = '金珠 2' AND 年龄 = 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);限制和偏移
Limit指定要检索的最大记录数Offset指定在开始返回记录之前要跳过的记录数
db.Limit( 3 ).Find(&users)
// SELECT * FROM users LIMIT 3;
// 使用 -1 取消限制条件
db.Limit( 10 ).Find(&users1).Limit( -1 ).Find(&users2)
// SELECT * FROM users LIMIT 10; (users1)
// 选择 * FROM 用户;(用户2)
db.Offset( 3 ).Find(&users)
// SELECT * FROM users OFFSET 3;
db.Limit( 10 ).Offset( 5 ).Find(&users)
// SELECT * FROM users OFFSET 5 LIMIT 10;
// 使用 -1 取消偏移条件
db.Offset( 10 ).Find(&users1).Offset( -1 ).Find(&users2)
// SELECT * FROM users OFFSET 10; (users1)
// 选择 * FROM 用户;(用户2)4.更新
简要更新
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type User struct {
gorm.Model
Name string
Age int
}
func main() {
dsn := "root:root@tcp(127.0.0.1:3306)/gorm-test?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
var user User
db.First(&user)
user.Name = "tang"
user.Age = 11
db.Save(&user)
}更新单个列
当使用 Update 更新单个列时,你需要指定条件,否则会返回ErrMissingWhereClause 错误
db.Model(&User{}).Where("id = ?", 1).Update("name", "gorm")// 条件更新
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;5.删除
删除一条记录
删除一条记录时,删除对象需要指定主键,否则会触发批量 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";根据主键删除
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);软删除和物理删除
如果一个 model 有 DeletedAt 字段,他将自动获得软删除的功能! 当调用 Delete 方法时, 记录不会真正的从数据库中被删除, 只会将DeletedAt 字段的值会被设置为当前时间
// Unscoped 方法可以物理删除记录
db.Unscoped().Delete(&order)
// DELETE FROM orders WHERE id=10;
















