GORM 学习笔记 02-单表模型和单表操作

gorm的引入肯定是为了我们更好的操作数据库

在使用原生sql查询的时候

最大的问题就是没办法映射到结构体上

我们不难发现,数据库里面的记录就很想一个结构体,数据库的一列对应一个字段

所以数据库到结构体的映射就是orm的一个最大特点

所有的orm都是如此

使用一个类或者一个结构体表示一张表

然后使用一个实例化对象表示一条记录

字段表示列

安装

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

一个简单的示例

package main

import (
	//"database/sql"
	//"fmt"
	//"log"

	"fmt"

	//引入驱动
	_ "github.com/go-sql-driver/mysql"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

// 用结构体来表示一个表
type User struct {
	ID       int
	Username string
	Age      int
}

// 在结构体中指定表名
func (User) TableName() string {
	return "user"
}
func main() {
	//连接数据库
	//数据源名称(用户名:密码@tcp(地址)/库名)
	dsn := "root:888888@tcp(127.0.0.1:3306)/mydb"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println(err)
		return
	}
	//fmt.Println(db)
	//查询
	//实例化切片
	var userList []User
	db.Find(&userList)
	fmt.Println(userList)
}
[{1 wangwu 19} {2 lisi 20} {3 zhangsan 18} {4 fengfeng 23} {5 fangfang 18} {6 fengfengzhidao 25} {7 feifei12138 28} {8 lier 29}]

单表模型

用一个 Go 结构体(struct)来对应数据库中的一张表。结构体的字段对应表的列

表结构

在gorm中,使用结构体表示一张表

例如User表

通常情况下,我们会使用Model结尾表示它是表的结构体

type UserModel struct {
  ID        int64     `gorm:"primaryKey"` // 主键
  Name      string    `gorm:"not null;unique"` // 不能为空,且唯一
  CreatedAt time.Time // 在创建记录时自动设置为当前时间
}

标签参考这个 https://gorm.io/zh_CN/docs/models.html#字段标签

为了方便可以建一个全局变量

package global

import (
	"fmt"

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

var DB *gorm.DB
func Connect()  {
	dsn := "root:888888@tcp(127.0.0.1:3306)/mydb"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Println(err)
		return
	}
	DB=db
}

自动生成表

让 GORM 根据结构体的定义,自动在数据库里创建或更新表结构

生成表结构只会新增字段,不会删除字段和更新字段

package main

import (
	"fmt"
	"gorm_study/global"
	"time"
)

type UserModel struct {
	ID        int    //一般不写就是默认主键
	Usernamme string `gorm:"not null;unique"`
	Age       int    `gorm:"default:18"`
	CreatedAt time.Time
}

func migrate() {
	fmt.Println(global.DB)
	//表迁移
	err := global.DB.AutoMigrate(&UserModel{})
	if err != nil {
		fmt.Println("表结构迁移失败:", err)
		return
	}
	fmt.Println("表结构迁移成功")
}
func main() {
	global.Connect()
	migrate()
}

单表操作

插入数据

为了方便可以单独创建 models 下一个 user_model

package models

import "time"

type UserModel struct {
	ID        int    //一般不写就是默认主键
	Usernamme string `gorm:"not null;unique"`
	Age       int    `gorm:"default:18"`
	CreatedAt time.Time
}
package main

import (
	"fmt"
	"gorm_study/global"
	"gorm_study/models"
)

func main() {
	global.Connect()
  //插入数据(一次插入)
	err := global.DB.Create(&models.UserModel{
		Usernamme: "张三",
		Age:       25,
	}).Error
	if err != nil {
		fmt.Println(err)
	}
}

回填式创建

//回填式插入
user := models.UserModel{
	Usernamme: "lisi",
	Age:       25,
}
err := global.DB.Create(&user).Error
if err != nil {
	fmt.Println(err)
}
fmt.Println(user.ID, user.CreatedAt, user.Usernamme)

添加多个

var userList = []models.UserModel{
	{Usernamme: "李四"},
	{Usernamme: "wangwu"},
}
err := global.DB.Create(&userList).Error
if err != nil {
	fmt.Println(err)
}

特别注意:Create函数接收的参数是结构体的指针或者是切片

插入数据的钩子

package models

import (
	"errors"
	"fmt"
	"time"

	"gorm.io/gorm"
)

type UserModel struct {
	ID        int    //一般不写就是默认主键
	Usernamme string `gorm:"not null;unique"`
	Age       int    `gorm:"default:18"`
	CreatedAt time.Time
}

// 钩子函数
func (u *UserModel) BeforeCreate(*gorm.DB) error {
	fmt.Println("创建钩子")
	return errors.New("xxxx")
}

查询数据

func query() {
	var userList []models.UserModel
	//查全部
	global.DB.Find(&userList)
	fmt.Println(userList)
}

注意此时会报错,连接数据库需要加参数

dsn := "root:888888@tcp(127.0.0.1:3306)/mydb?parseTime=true&loc=Local"
// 查全部
var users []UserModel
DB.Find(&users)
fmt.Println(users)

// 带条件查询
users = []UserModel{}
DB.Find(&users, "name = ?", "张三")
fmt.Println(users)

// 获取一条记录
var user UserModel
DB.Take(&user)
fmt.Println(user)

// 如果查不到则会报错
user = UserModel{}
err := DB.Take(&user, "id = ?", 100).Error
if err == gorm.ErrRecordNotFound {
  fmt.Println("不存在的记录")
}

// 如果不想出现错误
user = UserModel{}
err = DB.Limit(1).Find(&user, "id = ?", 100).Error
fmt.Println(err)

// 打印实际的sql
DB.Debug().Take(&user, "id = ?", 1)

更新

有很多方法,Save、Update、UpdateColumn、Updates

不同的方法有不同的区别,如下:

  1. Save,有主键记录就是更新,并且可以更新零值,否则就是创建
  2. Update,可以更新零值,必须要有条件
  3. UpdateColumn,可以更新零值,不会走更新的Hook
  4. Updates,如果是结构体,则更新非零值,map可以更新零值

save

func updateSave() {
	var user models.UserModel
	//user.ID = 3
	user.CreatedAt = time.Now()
	user.Usernamme = "miaomiao2"
	global.DB.Save(&user)
	fmt.Println(user)
}

update

func update() {
	var user = models.UserModel{ID: 1}
	global.DB.Model(&user).Update("usernamme", "zhangsan")
	fmt.Println(user)
}

更新的钩子

func (u *UserModel) BeforeUpdate(tx *gorm.DB) (err error) {
  fmt.Println("更新的钩子函数")
  return nil
}

Updates

func updates() {
    var user = models.UserModel{ID: 1}
    //global.DB.Model(&user).Updates(models.UserModel{
    // Usernamme: "张三",
    // Age:       18,
    //})
    global.DB.Model(&user).Updates(map[string]any{
       "Usernamme": "张三",
       "Age":       0,
    })
    fmt.Println(user)
}

Expr

通常用于获取原字段的数据

例如年龄加一

global.DB.Model(&user).Update("age", gorm.Expr("age+?", 1))

删除

var user = UserModel{ID: 10}
DB.Delete(&user)

DB.Delete(&UserModel{}, 9)

// 批量删除
DB.Delete(&UserModel{}, []int{1, 2, 3})
func (u *UserModel) BeforeDelete(tx *gorm.DB) (err error) {
    fmt.Println("删除钩子")
    return
}

软删除

如果你的模型包含了 gorm.DeletedAt字段(该字段也被包含在gorm.Model中),那么该模型将会自动获得软删除的能力

当调用Delete时,GORM并不会从数据库中删除该记录,而是将该记录的DeleteAt设置为当前时间,而后的一般查询方法将无法查找到此条记录。

type UserModel struct {
	ID        int    //一般不写就是默认主键
	Usernamme string `gorm:"not null;unique"`
	Age       int    `gorm:"default:18"`
	CreatedAt time.Time
	DeletedAt gorm.DeletedAt
}

新增加了列要迁移

func Migrate() {
	err := DB.AutoMigrate(&models.UserModel{})
	if err != nil {
		log.Fatalf("数据库迁移失败: %s", err)
	}
	fmt.Println("数据库迁移成功")

}

查找被删除的记录

var user models.UserModel
global.DB.Unscoped().Take(&user, 3)
fmt.Println(user)

永久删除

global.DB.Unscoped().Delete(&user)