GORM入门

GORM

下载驱动

 go get gorm.io/driver/mysql
 go get gorm.io/gorm

连接数据库

var DB *gorm.DB

func init() {
	username := "root"                     // 账号
	password := "Qza040203@quantumgameing" //密码
	host := "127.0.0.1"                    // 数据库地址
	port := 3306                           //端口号
	Dbname := "qza"                        //数据库名
	timeout := "10s"                       // 连接超时时间

	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, Dbname, timeout)

	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		SkipDefaultTransaction: true, //是否开启默认事物,默认为false,配置为true可取消系统默认事物,若没有需求禁用可提升60%性能
		Logger:                 logger.Default.LogMode(logger.Info),
		NamingStrategy: schema.NamingStrategy{
			TablePrefix: "tb_",//表名前缀
			SingularTable: true,//是否单数命名
			NoLowerCase: true,//不进行小写转换
		},
	})
	if err != nil {
		panic("数据库连接失败,error=" + err.Error())
	}
	DB = db
}

模型定义

​ 模型是标准的 struct,由 Go 的基本数据类型、实现了 ScannerValuer 接口的自定义类型及其指针或别名组成。在创建模型时,可以给字段设置 tag 来对该字段一些属性进行定义。比如我们把ID设置成主键。

type Model struct {
	ID        uint `gorm:"primaryKey"`
	CreatedAt time.Time
	UpdatedAt time.Time
}
type Student struct {
	gorm.Model
	Name string
	Age  int
}

标签名说明
column指定 db 列名
size指定列大小,例如:size:256
primaryKey指定列为主键
unique指定列为唯一
default指定列的默认值
type列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not null、size, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INSTREMENT
primaryKey指定列为主键
unique指定列为唯一
default指定列的默认值
precision指定列的精度
scale指定列大小
not null指定列为 NOT NULL
autoIncrement指定列为自动增长
embedded嵌套字段
embeddedPrefix嵌入字段的列名前缀
autoCreateTime创建时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nano
autoUpdateTime创建/更新时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milli
index根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情
uniqueIndexindex 相同,但创建的是唯一索引
check创建检查约束,例如 check:age > 13,查看 约束 获取详情
<-设置字段写入的权限, <-:create 只创建、<-:update 只更新、<-:false 无写入权限、<- 创建和更新权限
->设置字段读的权限,->:false 无读权限
-忽略该字段,- 无读写权限

自动创建表

在数据库的表尚未初始化时,gorm 可以根据指定的结构体自动建表。

通过 DB.AutoMigrate 方法根据Student结构体,自动创建 Students 表。如果表已存在,该方法不会有任何动作。

DB.AutoMigrate(&Student{})

建表的规则会把 Student 调整为复数,并自动添加 gorm.Model 中的几个字段。由于很多数据库是不区分大小写的,如果采用 camelCase 风格命名法,在迁移数据库时会遇到很多问题,所以数据库的字段命名风格都是采用 underscorecase 风格命名法,gorm 会自动帮我们转换。

使用 DB.Create 方法,传入结构体的指针创建。

student := Student{Age: 18, Name: "Alice"}
	students := []Student{
		{Name: "Bob",Age: 20},
		{Name: "Jack",Age: 19},
	}
	DB.Create(&student)
	DB.Create(students)

相当于

INSERT INTO
    `students` ( `created_at`, `updated_at`, `deleted_at`, `name`, `age` )
VALUES
	(
		'2023-09-27 19:17:47.921',
		'2023-09-27 19:17:47.921',
		NULL,
		'Alice',
		18
    ),
    (
    	'2023-09-27 19:17:47.921',
		'2023-09-27 19:17:47.921',
		NULL,
		'Bob',
		20
    ),
    (
        '2023-09-27 19:17:47.921',
		'2023-09-27 19:17:47.921',
		NULL,
		'Jack',
		19
    )

gorm 会自动维护 created_at、updated_ad 和 deleted_at 三个字段。

通过 Select 选择指定字段。

使用 Omit 方法过滤一些字段。

	student := Student{Age: 18, Name: "zhangsan"}
	DB.Select("Name").Create(&student)
	student = Student{Age: 18, Name: "lisi"}
	DB.Omit("Age").Create(&student)

gorm 提供了 First、Take、Last 方法。它们都是通过 LIMIT 1 来实现的,分别是主键升序、不排序和主键降序。

	student := Student{}

	DB.First(&student)
	// SELECT * FROM students ORDER BY id LIMIT 1;
	
	DB.Take(&student)
	// SELECT * FROM students  LIMIT 1;

	DB.Last(&student)
	// SELECT * FROM students ORDER BY id DESC LIMIT 1;
	fmt.Println(student)

在 First/Take/Last 等函数中设置第二个参数,该参数被认作是 ID。可以选择 int 或 string 类型。

DB.First(&student, 2)

如果没有查询到对象,会返回 ErrRecordNotFound 错误。

使用 Find 方法查询多个对象。

返回值会映射到 users 切片上。

依然可以通过访问返回值上的 Error 和 RowsAffected 字段获取异常和影响的行号。

	students := []Student{}
	res := DB.Find(&students)
	fmt.Println(students)
	fmt.Println(res.Error)
	fmt.Println(res.RowsAffected)

设置查询条件 Where

gorm 提供了万能的 Where 方法,可以实现 =、<>、IN、LIKE、AND、>、<、BETWEEN 等方法,使用 ? 来占位。

	DB.Where("Name = ?", "Alice").First(&students)
	//SELECT * FROM students WHERE name = "Alice" ORDER BY id LIMIT 1;
	fmt.Println(students)

	DB.Where("Name <> ?", "Alice").Find(&students)
	//SELECT * FROM students WHERE name <> "Alice";
	fmt.Println(students)

	DB.Where("Age in ?", []int{18, 20}).Find(&students)
	//SELECT * FROM students WHERE age name IN (18,20);
	fmt.Println(students)

	DB.Where("Name like ?", "%l%").Find(&students)
	// SELECT * FROM students WHERE name LIKE '%l%';
	fmt.Println(students)

	DB.Where("Name = ? AND Age = ?", "Alice", 18).Find(&students)
	// SELECT * FROM students WHERE name = 'Alice' AND age = 18;
	fmt.Println(students)

	DB.Where("Age between ? and ?", 18, 20).Find(&students)
	// SELECT * FROM students WHERE age BETWEEN 18 AND 20;
	fmt.Println(students)

传递 Struct、Map 和 切片时,可以实现更简便的设置条件。

	DB.Where(&Student{Name: "Alice"}).Find(&students)
	DB.Where(map[string]interface{}{"Name":"Ailce"}).Find(students)

结构体和 Map 的效果几乎是相等的,两者唯一的不同之处在于 struct 中的零值字段不会查询。比如 0、“”、false。切片是查询主键。所有的查询,gorm 都会默认设置 tabel.deleted_at IS NULL 查询条件。

DB.Where([]int{1,3}).Find(&students)
SELECT
	*
FROM
	`students`
WHERE
	`id` IN ( 10, 11 )
	AND `deleted_at` IS NULL

选取特定字段 Select

	DB.Select("ID").Where(&Student{Age: 18}).Find(&students)
SELECT
	`ID`
FROM
	`students`
WHERE
	.`name` = 'lzq'
	AND `deleted_at` IS NULL

类似于select,我们还可以链式操作排序 Order、分页 Limit Offset。

	DB.Order("Age DESC").Find(&students)
	DB.Limit(3).Offset(2).Find(&students)

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

	student := Student{}
	DB.Where("Name = ?", "Alice").First(&student)
	student.Name = "Alice1"
	DB.Save(&student)
UPDATE `students`
SET `created_at` = '2023-09-27 20:15:59.062',
`updated_at` = '2023-09-29 14:28:59.719',
`deleted_at` = NULL,
`user_name` = 'Alice1',
`age` = 18
WHERE
	`Name` = "Alice"

使用 Model 和 Update 方法更新单列。

可以使用结构体作为选取条件,如仅选择 ID。

	student.ID = 2
	DB.Model(&student).Update("Name","Bob1")

也可以在 Model 中设置空结构体,使用 Where 方法自己选取条件。

DB.Model(&Student{}).Where("ID",2).Update("Name", "Bob1")
UPDATE `students`
SET `name` = 'Bob1',
`updated_at` = '2023-09-29 14:32:56.165'
WHERE
	`id` = '2'

使用 Updates 方法进行更新多列。支持 struct 和 map 更新。当更新条件是 struct 时,零值不会更新,如果确保某列必定更新,使用 Select 选择该列。

DB.Model(&Student{}).Where("ID", 2).Updates(map[string]interface{}{
		"Name": "Jack1",
		"Age":  20,
	})

使用 Delete 方法删除单条数据。但需要指定 ID,不然会批量删除

	student := Student{}
	student.ID = 5
	DB.Delete(&student)
UPDATE `students`
SET `deleted_at` = '2023-09-29 14:49:43.106'
WHERE
	`ID` = 5
	AND `users`.`deleted_at` IS NULL

根据主键删除,也可以使用切片 []int、[]string 进行根据 ID 批量删除。

	DB.Delete(&Student{},1)
	DB.Delete(&Student{},[]int{2,3})

如果结构体包含 gorm.DeletedAt 字段,会自动获取软删除的能力,在调用所有的 Delete 方法时,会自动变为 update 语句,在查询时会自动忽略软删除的数据。

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

	DB.Unscoped().Where("ID", 5).Find(&student)
	fmt.Println(student)

使用 Unscoped 方法永久删除数据。

DB.Unscoped().Delete(&Student{},5)

除了上面的封装方法外,gorm 还提供了执行原生 SQL 的能力。执行 SQL 并将结果映射到变量上,使用 Raw 方法配合 Scan 方法。可以查询单条数据扫描并映射到结构体或 map 上。如果返回结果和传入的映射变量类型不匹配,那么变量的值不会有变化。

	DB.Raw("select * from students where id = ?", 1).Scan(&student)
	fmt.Println(student)

gorm 提供了 钩子Hook 功能。可以在创建、查询、更新和删除之前和之后自动执行某些逻辑。

假设现在需要添加一个 RecordID,并且在每次创建时生成一个 16 位的 uuid,还希望在存储之前打印生成的 uuid,在存储之后打印创建后的 id。实现方式就是给模型结构体 User 重写 BeforeCreate 和 AfterCreate 两个方法。

func (stu *Student) BeforeCreate(tx *gorm.DB) error {
	stu.RecordID = uuid.New().String()
	fmt.Println("开始创建,UUID =",stu.RecordID)
	return nil
}

func (stu *Student)AfterCreate(tx *gorm.DB) error  {
	fmt.Println("创建结束,UUID =",stu.RecordID)
	return nil
}

更新的 Hook 是 BeforeUpdate、AfterUpdate 和 BeforeSave、AfterSave,用法与创建一致。

查询的 Hook 是 AfterFind,用法与创建一致。

删除的 Hook 是 BeforeDelete 和 AfterDelete,用法与创建一致。

除了查询的 Hook 外,其他 Hook 都是在事务上运行的,一旦在函数中 return error 时,就会触发事务回滚。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值