使用 GORM(Go 的 ORM 库)连接数据库,并实现增删改查操作 | 青训营

107 阅读7分钟

Object-Relationl Mapping(orm),即对象关系映射,这里的Relationl指的是关系型数据库,它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的sql语句打交道,只要像平时操作对象一样操作它就可以了 。gorm是一个使用Go语言编写的ORM框架。它文档齐全,对开发者友好,支持主流数据库。**

1. 安装gorm与数据库驱动

gorm通过驱动连接数据库,在windows环境下在使用go get获取grom库与数据库驱动,实际上为从github下载,我使用的是mysql,使用go get要配置好GOPATH与GOPROXY,可以在网络上学习配置,

//安装 MySQL 驱动包
go get gorm.io/driver/mysql

//安装 Gorm 包
go get gorm.io/gorm

2. 项目导入依赖包

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

3. 连接数据库

Mysql dsn字符串格式:{username}:{password}@tcp({host}:{port})/{Dbname}?{charset=utf8&parseTime=True&loc=Local&timeout=10s}

  • username := "root" 账号
  • password := "123456" 密码
  • host := "127.0.0.1" 数据库地址,可以是Ip或者域名
  • port := ""3306 数据库端口
  • Dbname := "codebaoku" 数据库名

db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}),连接mysql,dsn字符串可以通过fmt.Sprintf()或strings.Join()进行拼接

db为(*gorm.DB)类型

//连接mysql可以放在init函数中,调用main函数之前会调用init函数
var DB *gorm.DB //全局变量,DB可以用来连接数据库与程序
func init() {  
mylogger := logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Config{
		SlowThreshold: time.Second, //慢SQL阈值
		LogLevel:      logger.Info, //级别
		Colorful:      true,        //彩色
	})
	db, err := gorm.Open(mysql.Open("root:101929@tcp(127.0.0.1:3306)/ginchat?charset=utf8&parseTime=True&loc=Local&timeout=10s"), &gorm.Config{
		Logger: mylogger,
	})
	DB = db
	if err != nil {
		panic("failed to connect database")
	} else {
		fmt.Println("连接成功")
	}

4.模型声明及表的创建

在go程序中定义一个struct结构体,Gorm可以通过该结构体在数据库中创建表,表的列名即结构体的字段名,默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt UpdatedAt 字段追踪创建、更新时间

4.1 声明模型,字段配置

type 表名 struct {
    ID uint `gorm:"primaryKey"`
    列名 类型 `gorm:"这里可以进行相关配置多个字段之间用分号隔开"`
} 

type Student struct {  
    gorm.Model  
    Email string `gorm:"type:varchar(20)"`  
    Name string `gorm:"type:varchar(9)"`  
    Sex string `gorm:"type:char(3)"`  
}

gorm默认约束列名是字段名的蛇形小写(PassWd->pass_word),若要自定义列名,可用column约束

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

4.2 模型映射为数据库中的表,并添加数据

4.2.1 映射

DB.AutoM1grate(&Student{})

 DB.AutoMigrate()可以把模型映射成数据库中表

在gorm中,默认的表名都是结构体名称的复数形式,比如User结构体默认创建的表为users

DB.SingularTable(true) 可以取消表名的复数形式,使得表名和结构体名称一致 ,也可以结构体增加一个TableName方法,返回表名

func (stu Student) TableName() string { return "product" }

4.2.2 单个加入

单个添加
	stu := Student{}
	stu.ID = 42
	DB.Create(&stu)*/


4.2.3 批量添加

批量添加
	stuList := []Student{}
	stu := Student{}
	stu.ID = 12
	stu1 := Student{}
	stu1.ID = 13
	stuList = append(stuList, stu)
	stuList = append(stuList, stu1)
	DB.Create(stuList)

5. 表数据的增删查改

5.1 增加

将实例化的结构体或切片(结构体)传递给DB.creat()函数即可实现增加

5.2 删除

5.2.0 软删除与物理删除

软删除:结构体存在匿名字段gorm.Model是删除时会修改删除时间这个字段 物理删除:是将这条记录删除

5.2.1 根据主键删除

删除一条记录时,删除对象需要指定主键,否则会触发批量删除

DB.Delete(&Student{}, 42)
var stuList []Student
DB.Delete(&stuList, []int{12, 13})

5.2.2 批量删除

如果指定的值不包括主属性,那么 GORM 会执行批量删除,它将删除所有匹配的记录

DB.Where("name LIKE ?", "徐%").Delete(&Student{})
// DELETE from student where name LIKE "徐%";  

DB.Delete(&Student{}, "name LIKE ?", "徐%") 
// DELETE from emails where name LIKE "徐%"; 

可以将一个主键切片传递给Delete 方法,以便更高效的删除数据量大的记录

var stus = []Student{{ID: 1}, {ID: 2}, {ID: 3}} 
DB.Delete(&stus) 
// DELETE FROM Student WHERE id IN (1,2,3);  

DB.Delete(&users, "name LIKE ?", "徐%") 
// DELETE FROM users WHERE name LIKE "徐%" AND id IN (1,2,3);  

5.2.2 钩子函数

对于删除操作,GORM 支持 BeforeDeleteAfterDelete Hook,在删除记录时会调用这些方法,查看 Hook 获取详情

func (u *Student) BeforeDelete(tx *gorm.DB) (err error) {     
    if u.Role == "admin" {         
        return errors.New("admin student not allowed to delete")     
    }     
    return
 }

5.3 查询

5.3.1 检索单个对象

GORM 提供了 FirstTakeLast 方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1 条件,且没有找到记录时,它会返回 ErrRecordNotFound 错误

var student Student
// 获取第一条记录(主键升序) 
DB.First(&student) 
// 获取一条记录,没有指定排序字段 
DB.Take(&student) 

// 获取最后一条记录(主键降序) 
DB.Last(&student) 
result := DB.First(&student) 
result.RowsAffected  // 返回找到的记录数 
result.Error        // returns error or nil  

// 检查 ErrRecordNotFound 错误 errors.Is(result.Error,gorm.ErrRecordNotFound) 

5.3.2 批量查询

var stus []Student
// Get all matched records
Db.Where("name <> ?", "徐兵").Find(&stus)
// IN
DB.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&stus)

/ LIKE
DB.Where("name LIKE ?", "%jin%").Find(&stus)

// AND
DB.Where("name = ? AND ID >= ?", "jinzhu", "22").Find(&stus)

更多高级查询可以去看:gorm.io/zh_CN/docs/…

5.4 更新

通过修改实例字段来更新 Save() 会保存所有的字段,即使字段是零值,也可以创建记录

var user Student
DB.First(&user) 

user.Name = "jinzhu 2" 
user.ID = 100 
DB.Save(&user) 
// UPDATE users SET name='jinzhu 2', ID=100;

//创建记录,,没有指定的字段会保存为零值
DB.Save(&User{Name: "jinzhu", Age: 100})  
  
DB.Save(&User{ID: 1, Name: "jinzhu", Age: 100})  

更多高级修改:gorm.io/zh_CN/docs/…

6. 功能实现

package main

import (
	"fmt"
	"log"
	"os"
	"time"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

type Student struct {
	gorm.Model
	Email string `gorm:"type:varchar(20)"`
	Name  string `gorm:"type:varchar(9)"`
	Sex   string `gorm:"type:char(3)"`
}

var DB *gorm.DB

func init() {
	mylogger := logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Config{
		SlowThreshold: time.Second, //慢SQL阈值
		LogLevel:      logger.Info, //级别
		Colorful:      true,        //彩色
	})
	db, err := gorm.Open(mysql.Open("root:101929@tcp(127.0.0.1:3306)/ginchat?charset=utf8&parseTime=True&loc=Local&timeout=10s"), &gorm.Config{
		Logger: mylogger,
	})
	DB = db
	if err != nil {
		panic("failed to connect database")
	} else {
		fmt.Println("连接成功")
	}
}

func main() {
	/*DB.AutoMigrate(&Student{})
	//单个连接
	stu := Student{}
	stu.ID = 42
	DB.Create(&stu)

	//批量添加
	stuList := []Student{}
	stu1 := Student{}
	stu2 := Student{}
	stu1.ID = 12
	stu2.ID = 13
	stuList = append(stuList, stu1)
	stuList = append(stuList, stu2)
	stu3 := Student{}
	stu4 := Student{}
	stu5 := Student{}
	stu3.ID = 63
	stu4.ID = 64
	stu5.ID = 50
	stuList = append(stuList, stu1)
	stuList = append(stuList, stu2)
	stuList = append(stuList, stu3)
	stuList = append(stuList, stu4)
	stuList = append(stuList, stu5)
	DB.Create(stuList)
	//根据主键删除
	DB.Delete(&Student{}, 42)
	var stuList []Student
	DB.Delete(&stuList, []int{12, 13})*/

	/*//查询
	var student Student
	// 获取第一条记录(主键升序)
	fmt.Println(DB.First(&student))
	// 获取一条记录,没有指定排序字段
	fmt.Println(DB.Take(&student))
	// 获取最后一条记录(主键降序)
	fmt.Println(DB.Last(&student))*/

	/*
	//更新
	var user Student
	DB.First(&user)
	user.Name = "许秀"
	user.ID = 100
	DB.Save(&user)
	//创建记录,,没有指定的字段会保存为零值
	DB.Save(&Student{Name: "jinzhu", Sex: "男"})
	VALUES ("jinzhu",100,"0000-00-00 00:00:00","0000-00-00 00:00:00")
	stu_ := Student{Name: "jinzhu", Sex: "女"}
	stu_.ID = 64
	DB.Save(&stu_)
	
}

image.png

image.png

image.png

image.png