Gorm CRUD
前言
Gorm
是go
的一个ORM
框架,官方文档地址为-> GORM 指南
本文将介绍与gorm
有关的CRUD操作,操作数据库类型为mysql
数据库
数据库连接
func Open(dialector Dialector, opts …Option) (db *DB, err error)
该函数用于进行gorm连接对应数据库
输入
go get -u "gorm.io/gorm"
下载gorm库
输入go get -u gorm.io/driver/mysql
即可下载mysql驱动
对于mysql
的连接,还需要引入gorm的mysql
驱动,否则没有Open这个函数
var gormDb *gorm.DB
func initGormDB() {
var err error
gormDb, err = gorm.Open(
mysql.Open("root:xxxxxx@tcp(127.0.0.1:3306)/go_db?charset=utf8mb4&parseTime=True&loc=Local"),
&gorm.Config{
PrepareStmt: true, //缓存预编译命令
SkipDefaultTransaction: true, //禁用默认事务操作
},
)
if err != nil {
// 这里因为是panic所以可以直接进行赋值操作,如果不是panic需要在错误之后才能进行赋值给全局变量
panic(err)
}
fmt.Println("gormDb connection success~")
}
下图将给出gormConfig
的配置
支持自定义logger
,但是需要实现对应接口的方法
// Interface logger interface
type Interface interface {
LogMode(LogLevel) Interface
Info(context.Context, string, ...interface{})
Warn(context.Context, string, ...interface{})
Error(context.Context, string, ...interface{})
Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error)
}
因为gorm底层是通过使用 database/sql 维护连接池
因此可以获取sqlDB,然后设置连接池
sqlDB, err := gormDb.DB()
// SetMaxIdleConns 设置空闲连接池中连接的最大数量
sqlDB.SetMaxIdleConns(10)
// SetMaxOpenConns 设置打开数据库连接的最大数量。
sqlDB.SetMaxOpenConns(100)
// SetConnMaxLifetime 设置了连接可复用的最大时间。
sqlDB.SetConnMaxLifetime(time.Hour)
数据库迁移
下图是gorm中关于数据库迁移的源代码,给出了自动迁移和手动迁移两种方案,而自动迁移其实和手动迁移没有区别。
// Migrator returns migrator
func (db *DB) Migrator() Migrator {
tx := db.getInstance()
// apply scopes to migrator
for len(tx.Statement.scopes) > 0 {
scopes := tx.Statement.scopes
tx.Statement.scopes = nil
for _, scope := range scopes {
tx = scope(tx)
}
}
return tx.Dialector.Migrator(tx.Session(&Session{}))
}
// AutoMigrate run auto migration for given models
func (db *DB) AutoMigrate(dst ...interface{}) error {
return db.Migrator().AutoMigrate(dst...)
}
迁移数据库
结构体中要嵌套gorm的Model结构体,用于管理软删除和时间
type Model struct {
ID uint `gorm:"primarykey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt DeletedAt `gorm:"index"`
}
type GormUser struct {
gorm.Model
Age int `gorm:"age"`
Name string `gorm:"name"`
Password string `gorm:"password"`
}
// Migrant 数据库迁移
func Migrant() error {
return gormDb.AutoMigrate(&GormUser{})
}
在tag处可以选择gorm中的字段名,我这里只做演示操作,数据与实际开发并不相符
测试
使用 go test -run=Migrant -v 该命令可以详细打印出测试结果
func TestMain(m *testing.M) {
initGormDB()
code := m.Run()
os.Exit(code)
}
func TestMigrant(t *testing.T) {
err := Migrant()
if err != nil {
t.Errorf("Migrant fail,%v", err)
}
}
CRUD实战
创建
func (db *DB) Create(value interface{}) (tx *DB)
增加一条用户信息
// GormCreate gorm 创建数据
func GormCreate() error {
u := GormUser{
Name: "张三",
Age: 15,
Password: "qwer",
}
return gormDb.Create(&u).Error
}
测试
go test -run=GormCreate -v
func TestGormCreate(t *testing.T) {
err := GormCreate()
if err != nil {
t.Errorf("Migrant fail,%v", err)
}
}
增加多条用户信息
通过切片或者数组的方式插入多条数据
// GormCreateMore gorm 创建多条数据
func GormCreateMore() error {
u := GormUser{
Name: "张三",
Age: 15,
Password: "qwer",
}
u2 := GormUser{
Name: "李四",
Age: 16,
Password: "qwer",
}
u3 := GormUser{
Name: "王五",
Age: 17,
Password: "qwer",
}
uu := [...]GormUser{u, u2, u3}
return gormDb.Create(&uu).Error
}
测试
func TestGormCreateMore(t *testing.T) {
err := GormCreateMore()
if err != nil {
t.Errorf("Migrant fail,%v", err)
}
}
删除
func (db *DB) Delete(value interface{}, conds …interface{}) (tx *DB)
gorm中有两种删除方法
- 硬删除,直接通过结构体中的字段值进行删除
- 条件删除,先进行where查询然后删除
// GormDelete gorm 删除数据
func GormDelete() error {
// 直接删除
gormDb.Delete(&GormUser{Name: "张三"})
// 条件删除
return gormDb.Where("name = ?", "张三").Delete(&GormUser{}).Error
}
测试
这里出错是因为我删除了两次
func TestGormDelete(t *testing.T) {
err := GormDelete()
if err != nil {
t.Errorf("Migrant fail,%v", err)
}
}
gorm中删除使用的是软删除,好处是不会产生碎片化数据,方便维护。
修改
修改的方法起码有四种,两种批量修改,两种单行修改。还可以进行保存
保存修改
u := GormUser{}
//先查询一条记录, 保存在模型变量u
//等价于: SELECT * FROM `gorm_users` WHERE (id = '2') LIMIT 1
db.Where("id = ?", 3).Take(&u)
u.age = 100
db.Save(&u)
直接修改
Model
用于绑定操作的数据库
gormDb.Model(&GormUser{}).Update("age", 25).Where("id", 3).Error
显然该操作不行,报错说该操作没有where,因此可以得出where语句必须要在Update之前。所以我们可以再尝试一次
gormDb.Model(&GormUser{}).Where("id", 3).Update("age", 25).Error
测试成功
查询
func (db *DB) First(dest interface{}, conds …interface{}) (tx *DB)
First finds the first record ordered by primary key, matching given conditions condsFirst 用来查询单条数据,对所有数据进行正序排序并且放回第一条
等价于:SELECT * FROM
gorm_users
ORDER BYgorm_users.id
ASC LIMIT 1
// GormQuery gorm 查询数据
func GormQuery(u *GormUser) error {
return gormDb.First(u).Error
// gormDb.Last(u).Error Last与First刚好相反(逆序排列),就不再测试了
}
测试
func TestGormQuery(t *testing.T) {
u := GormUser{}
err := GormQuery(&u)
if err != nil {
t.Errorf("gormQuery fail,%v", err)
}
t.Logf("gormUser first, data: %v", u)
}
func (db *DB) Take(dest interface{}, conds …interface{}) (tx *DB)
Take finds the first record returned by the database in no specified order, matching given conditions condsTake 查询单条
等价于:SELECT * FROM
gorm_users
LIMIT 1
指定查询单条数据并返回查询行数
// GormQuery gorm 查询数据
func GormQuery(u *GormUser) (cnt int64, err error) {
err = gormDb.Where("age = ?", 18).Count(&cnt).First(u).Error
return
}
查询失败的原因是需要Model,或者Table,提前指定表名。因此我们加上之后可以再次尝试
// GormQuery gorm 查询数据
func GormQuery(u *GormUser) (cnt int64, err error) {
err = gormDb.Model(&GormUser{}).Where("age = ?", 18).Count(&cnt).First(u).Error
return
}
测试
func TestGormQuery(t *testing.T) {
u := GormUser{}
rowNum, err := GormQuery(&u)
if err != nil {
t.Errorf("gormQuery fail,%v", err)
}
t.Logf("gormUser first, data: %v\n", u)
t.Logf("gormQuery method queryRows = %d", rowNum)
}
查到了三条数据,但我用的First,因此只返回一条数据。
查询多条数据
func (db *DB) Find(dest interface{}, conds …interface{}) (tx *DB)
Find finds all records matching given conditions conds
Find 用于查询多条数据
// GormQueryMore gorm 查询多条数据
func GormQueryMore(u []*GormUser) (cnt int64, err error) {
err = gormDb.Model(&GormUser{}).Where("age = ?", 18).Count(&cnt).Find(u).Error
return
}
测试
func TestGormQueryMore(t *testing.T) {
u := make([]*GormUser, 3)
rowNum, err := GormQueryMore(u)
if err != nil {
t.Errorf("gormQuery fail,%v", err)
}
for i, v := range u {
t.Logf("gormUser first,第%d条数据, data: %v\n", i, v)
}
t.Logf("gormQuery method queryRows = %d", rowNum)
}
小结
gorm底层仍然是database/sql,因此原生的sql方法仍然能够被使用。对于gorm的CRUD简单操作就介绍到这里了。如果感兴趣的话可以到gorm的官网更深入的研究。