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 |
| UNSIGNED | not 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 支持 BeforeDelete、AfterDelete 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 提供了 First、Take、Last 方法,以便从数据库中检索单个对象。当查询数据库时它添加了 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_)
}