Gorm框架学习---CRUD接口之创建

Posted 大忽悠爱忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gorm框架学习---CRUD接口之创建相关的知识,希望对你有一定的参考价值。

Gorm框架学习---CRUD接口之创建


本文内容摘抄自Gorm 2022-8月份官方文档教程,如果Gorm框架后续有更新,还是以最新版本的官方文档为准


系列文章:

Gorm框架学习–入门


环境准备

先确保能够连接上指定的数据库并且将相关表创建好,这里用mysql作为演示:

package main

import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

const MYSQL_ADDR ="user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"

type User struct 
    gorm.Model
	Name     string
	Age      int
	Birthday time.Time


func main() 
	DB = openDB()
	createTable()


func createTable() 
	DB.AutoMigrate(&User)


func openDB() *gorm.DB 
	db, err := gorm.Open(mysql.Open(MYSQL_ADDR), &gorm.Config)
	if err != nil 
		panic("failed to connect database")
	
	return db


创建

创建记录

func main() 
	DB = openDB()
	user := UserName: "大忽悠", Age: 18, Birthday: time.Now()

	result := DB.Create(&user) // 通过数据的指针来创建

	fmt.Println("返回插入数据的主键: ", user.ID)
	fmt.Println("返回 error: ", result.Error)
	fmt.Println("返回插入记录的条数: ", result.RowsAffected)


用指定的字段创建记录

创建记录并更新给出的字段。

	DB.Select("Name", "Age", "CreatedAt").Create(&user)
	// INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("大忽悠", 18, "2022-08-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")

批量插入

要有效地插入大量记录,请将一个 slice 传递给 Create 方法。 GORM 将生成单独一条SQL语句来插入所有数据,并回填主键的值,钩子方法也会被调用。

	var users = []UserName: "dhy1", Name: "dhy2", Name: "dhy3"
	DB.Create(&users)

	for _, user := range users 
		fmt.Printf("id=%d,name=%s,age=%d\\n", user.ID, user.Name, user.Age)
	

ERROR是因为Mysql5.7版本及以上版本的datetime值不能为’0000-00-00 00:00:00’,
解决方法: 修改mysql.int
在[mysqld]添加一项:sql_mode=NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,支持特殊的语法,这样就可以导入了,导入完毕后,移除兼容项即可。此方法简单,建议使用此方法。

使用 CreateInBatches 分批创建时,你可以指定每批的数量,例如:

	var users = []UserName: "dhy1", Name: "dhy2", Name: "dhy3"
	// 数量为 100
	DB.CreateInBatches(users, 100)

UpsertCreate With Associations 也支持批量插入

注意 使用CreateBatchSize 选项初始化 GORM 时,所有的创建& 关联 INSERT 都将遵循该选项

func openDB() *gorm.DB 
	//针对全局设置
	db, err := gorm.Open(mysql.Open(MYSQL_ADDR), &gorm.Config
		CreateBatchSize: 1,
	)
	if err != nil 
		panic("failed to connect database")
	
	return db

	var users = []UserName: "dhy1", Name: "dhy2", Name: "dhy3"
	//针对某次会话设置
	DB.Session(&gorm.SessionCreateBatchSize: 1).Create(users)

创建钩子

GORM 允许用户定义的钩子有 BeforeSave, BeforeCreate, AfterSave, AfterCreate 创建记录时将调用这些钩子方法,请参考 Hooks 中关于生命周期的详细信息

钩子方法常与模板方法模式搭配使用,通常暴露给用户自定义相关组件,以此提高框架整体可扩展性

func (u *User) BeforeCreate(tx *gorm.DB) (err error) 
  u.UUID = uuid.New()

    if u.Role == "admin" 
        return errors.New("invalid role")
    
    return

如果您想跳过 钩子 方法,您可以使用 SkipHooks 会话模式,例如:

//都是针对单词会话进行设置的
DB.Session(&gorm.SessionSkipHooks: true).Create(&user)

DB.Session(&gorm.SessionSkipHooks: true).Create(&users)

DB.Session(&gorm.SessionSkipHooks: true).CreateInBatches(users, 100)

根据 Map 创建

GORM 支持根据 map[string]interface[]map[string]interface 创建记录,例如:

	DB.Model(&User).Create(map[string]interface
		"Name": "DHY", "Age": 18,
	)

	// batch insert from `[]map[string]interface`
	DB.Model(&User).Create([]map[string]interface
		"Name": "dhy1", "Age": 18,
		"Name": "dhy2", "Age": 20,
	)

注意: 根据 map 创建记录时,association 不会被调用,且主键也不会自动填充


使用 SQL 表达式、Context Valuer 创建记录

GORM 允许使用 SQL 表达式插入数据,有两种方法实现这个目标。根据 map[string]interface自定义数据类型 创建,例如:

	// 通过 map 创建记录
	DB.Model(User).Create(map[string]interface
		"Name": clause.ExprSQL: "UPPER(?)", Vars: []interface"dhy",
		"age":  18,
	)

对应创建的sql语句为:

INSERT INTO `users` (`name`,`age`) VALUES (UPPER('dhy'),18)

通过自定义类型创建记录:

// 通过自定义类型创建记录
type Location struct 
    X, Y int


// Scan 方法实现了 sql.Scanner 接口
func (loc *Location) Scan(v interface) error 
  // Scan a value into struct from database driver


func (loc Location) GormDataType() string 
  return "geometry"


func (loc Location) GormValue(ctx context.Context, db *gorm.DB) clause.Expr 
  return clause.Expr
    SQL:  "ST_PointFromText(?)",
    Vars: []interfacefmt.Sprintf("POINT(%d %d)", loc.X, loc.Y),
  


type User struct 
  Name     string
  Location Location


db.Create(&User
  Name:     "jinzhu",
  Location: LocationX: 100, Y: 100,
)
// INSERT INTO `users` (`name`,`location`) VALUES ("jinzhu",ST_PointFromText("POINT(100 100)"))

高级选项

关联创建

创建关联数据时,如果关联值是非零值,这些关联会被 upsert,且它们的 Hook 方法也会被调用

upsert: 存在时更新,不存在时插入

	type CreditCard struct 
		gorm.Model
		Number   string
		UserID   uint
	

	type User struct 
		gorm.Model
		Name       string
		CreditCard CreditCard
	

	DB.Create(&User
		Name: "jinzhu",
		CreditCard: CreditCardNumber: "411111111111",
	)
	// INSERT INTO `users` ...
	// INSERT INTO `credit_cards` ...

您也可以通过 Select、 Omit 跳过关联保存,例如:

db.Omit("CreditCard").Create(&user)

// 跳过所有关联
db.Omit(clause.Associations).Create(&user)

默认值

您可以通过标签 default 为字段定义默认值,如:

type User struct 
  ID   int64
  Name string `gorm:"default:galeone"`
  Age  int64  `gorm:"default:18"`

插入记录到数据库时,默认值 会被用于 填充值为 零值 的字段

注意: 对于声明了默认值的字段,像 0、‘’、false 等零值是不会保存到数据库。您需要使用指针类型或 Scanner/Valuer 来避免这个问题,例如:

type User struct 
	gorm.Model
	Name     string    `gorm:"default:18"`
	Age      int       `gorm:"default:100"`
	Birthday time.Time `gorm:"default:2022-08-23 21:28:27.329"`


func main() 
	DB = openDB()
	//我想保存相关数据类型的零值到数据库,但是由于默认值的存在,插入数据库的还是默认值
	DB.Create(&User
		Name: "",
		Age:  0,
	)

指针解决法:

type User struct 
	gorm.Model
	Name     *string   `gorm:"default:18"`
	Age      *int      `gorm:"default:100"`
	Birthday time.Time `gorm:"default:2022-08-23 21:28:27.329"`


func main() 
	DB = openDB()
	str := ""
	age := 0
	DB.Create(&User
		Name: &str,
		Age:  &age,
	)

Scanner/Valuer接口解决法:

type myString string

type User struct 
	gorm.Model
	Name     myString
	Age      int       `gorm:"default:100"`
	Birthday time.Time `gorm:"default:2022-08-23 21:28:27.329"`


//Scan 在从数据库读取记录到结构体时,当需要往某个字段注入值时,会先检查该字段是否实现了对应的Scan接口
//如果实现了,就利用该接口完成赋值
func (s *myString) Scan(src any) error 
	if src == nil 
		*s = ""
		return nil
	
	//... 我们需要在下面完成对当前字段的赋值
	return nil


func (s myString) Value() (driver.Value, error) 
	if s == "" 
		//如果为空,就记录一下
		fmt.Println("当前字段为空")
	
	//注意: 如果返回的类型也实现了Value接口,会继续调用
	//如果这里直接将s返回,会造成死循环,因为会不断去调用s的Value接口
	return "hhhhh", nil


func main() 
	DB = openDB()
	//在获取每个字段值时,会检查对应的字段有没有实现Value接口
	//如果实现了,就获取Value接口的返回值,作为最终结果
	DB.Create(&User
		Name: "xpy",
		Age:  0,
	)

Gorm操作对象属性前,会先去寻找Scan和Value方法,如果有则调用,这一点类似Java中操作对象属性通常使用Getter和Setter方法一般。


default:(-)标签可以让我们在字段为零值时,忽略该字段的插入。

type User struct 
	gorm.Model
	Name     myString
	Age      int       `gorm:"default:(-)"`
	Birthday time.Time `gorm:"default:2022-08-23 21:28:27.329"`


func main() 
	DB = openDB()
	DB.Create(&User
		Name: "xpy",
		Age:  0,
	)

  • Age为0时,生成的insert语句忽略了该字段的插入

  • Age不为0时,insert语句才会添加对应字段的插入


以上是关于Gorm框架学习---CRUD接口之创建的主要内容,如果未能解决你的问题,请参考以下文章

kratos学习从零使用kratos创建一个CRUD的demo,是gorm做数据库操作,经过几天研究,调试代码开源放到github上,还是非常方便的。服务也实现了CRUD的http接口。

kratos学习从零使用kratos创建一个CRUD的demo,是gorm做数据库操作,经过几天研究,调试代码开源放到github上,还是非常方便的。服务也实现了CRUD的http接口。

GORM 基础 -- CRUD 接口

Go开源世界主流成熟ORM框架gorm实践分享

GORM CRUD 5 分钟快速上手

学习笔记Golang之Gorm学习笔记