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

167 阅读6分钟
  • GORM的简单的例子(以mysql数据库为例)
  • GORM的增删改查
  • GORM的事务简单介绍

1.GORM的简单例子

The fantastic ORM library for Golang aims to be developer friendly.

Gorm是一个对开发者友好的ORM框架。

1.1 安装GORM

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

1.2 简单实现例子

type Product struct {  
gorm.Model  
name string  
Price uint  
number uint  
}  
  
func main() {  
var dsn = "user:password@(localhost)/dbname?charset=utf8mb4&parseTime=True&loc=Local"  
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})  
if err != nil {  
panic("不能·成功连接数据库")  
}  
//接下来将gorm的框架注入到数据库  
db.AutoMigrate(&Product{})  
  
//接下来进行简单的创建数据,修改数据,删除数据和查询数据  
db.Create(&Product{name: "加菲猫",  
Price: 2200, number: 12,  
}) //创建数据  
  
//查询数据和修改数据  
var product Product  
db.First(&product, 1) //使用主键进行查询  
db.First(&product, "name=?", "加菲猫") //查找name叫做加菲猫的商品  
  
//加菲猫价格上涨,我们进行更新  
db.Model(&product).Update("price", 2400)  
//对所有信息都进行更新  
db.Model(&product).Updates(Product{name: "加菲猫肥", Price: 3000, number: 8})  
db.Model(&product).Updates(map[string]interface{}{"name": "加菲猫肥", "price": "3200", "number": "6"})  
//删除  
db.Delete(&product, 1)  
  
}

上面实现的代码结果如下所示

image.png 上面是一个简单实现的GORM连接数据库进行简单操作的例子,下面会对其进行简单的解释。 GORM中存在一些约定,比如默认使用ID作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAtUpdatedAt 字段追踪创建、更新时间,DeleteAt代表软删除。例如我们使用Product作为结构体,最终数据库生成的是products。蛇形的含义是单词之间的分隔符是_,例如UpdateTime的蛇型风格就是update_time。

上面定义了DSN,其各个字段的含义如下:username代表数据库账号,password代表密码,host代表链接地址,port代表链接端口,Dbname代表数据库名称。

上面的一段代码的GORM的增删改查方法如下:使用Create创建一个数据,Update更新数据的一项,Updates更新全部数据,First进行查询,Delete进行删除。

此外GORM存储一个默认的结构体GORM.Model,我们日常的时候可以直接拿来用。


// gorm.Model 的定义
type Model struct {
  ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt gorm.DeletedAt `gorm:"index"`
}

例如我们直接将GORM.Model注入到Product中。此外对于正常的结构体,我们也可以使用embedded 将其嵌入到结构体。例如

type Product struct {  
gorm.Model  
name string  
Price uint  
number uint  
}  

type User struct{
gorm.Model  
product Product embedded

}

1.3遇到的问题

我在连接本地mysql数据考的时候出现以下错误,failed to initialize database, got error Error 1049 (42000): Unknown database 'gorm'

我这里是数据库没有建立,因为没有设置自动建库,所以需要我们手动提前建立好数据库,或者设置一下自动建立数据库。

2.GORM的增删改查

2.1 GORM的插入

插入数据

user := User{
	Username:"xxy",
	Password:"125245",
}
db.Create(&user)


返回内容主要是 user.ID // 返回插入数据的主键 result.Error // 返回 error result.RowsAffected // 返回插入记录的条数 并且我们使用Create创建数据的时候可以创建多个数据,而非只是一个数据。

此外还可以使用指定的字段创建数据

db.Select("Name", "Age", "CreatedAt").Create(&user)

我们有时候大量数据需要创建,可以进行批量创建数据。

var users = []User{{Username: "jinzhu1"}, {Username: "jinzhu2"}, {Username: "jinzhu3"}}
db.Create(&users)

for _, user := range users {
  user.ID // 1,2,3
}

还可以指定创建的数目,例如指定创建数量为100,db.CreateInBatches(users, 100) 不过需要前提确实有这些数据。

Gorm还支持根据Map进行创建和根据SQL语句进行创建,其代码如下所示


db.Model(&User{}).Create([]map[string]interface{}{
  {"Name": "jinzhu_1", "Age": 18},
  {"Name": "jinzhu_2", "Age": 20},
})

批量插入数据从 []map[string]interface{}{} 中进行。 下面使用原生SQL进行插入数据

db.Exec("insert into users (username,password,createtime) values (?,?,?)", user.Username, user.Password, user.CreateTime)

2.2 GORM的更新

Save 会保存所有的字段,即使字段是零值,其中save是一个组合函数。如果 save value 不包含主键,它将执行 Create,否则将执行 Update (包含所有字段)。

db.Save(&User{Name: "jinzhu", Age: 100})

如果user以及有对象就更新,没有对象就创建相关值。

更新单列

db.Model(&User{}).Where("active = ?", true).Update("name", "hello")

当更新单列的时候,需要有条件语句where否则他不知道更新哪里。

更新多列

db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})

Updates支持使用struct或map[string]接口{}进行更新,当使用struct进行更新时,默认情况下只更新非零字段,这一点需要注意很多情况下很多人会出现问题。

更新选定字段

db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})

上面的结果等价于UPDATE users SET name='hello' WHERE id=111; 所有没有被更新的数据相等于被默认了Omit,他和下面这句sql相互补充

db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})

下面这句代码等价于UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;

批量更新

db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})

这是批量更新结构体,更新user对象的name和age进行更新。

2.3 GORM的删除

根据主键删除

DB.Delete(&Goods{}, 1)

2.4 GORM的查询

查询数据可以分为查询一条数据,主键正序查询,主键倒序查询,查询多条数据 其代码展示如下所示:

db.Take(&user)
db.First(&user)
db.Last(&user)
db.Find(&user)

其代表的意思分别如下: Take获取一条记录,没有指定排序字段 First获取第一条记录(主键升序) Last获取最后一条记录(主键降序) Find查询多条记录

通过db.Where函数设置条件 例如

db.Where("id in (?)", []int{1,2,5,6}).Take(&goods)

还可以我们只返回几个指定的数据,而非一整个对象

DB.Select("id", "title").Find(&goods)

我们还可以排序

DB.Order("id desc").Find(&goods)

通过Order字段对于查询的数据进行排序

分页

DB.Order("create_time desc").Limit(10).Offset(10).Find(&goods)

通过limit和offset对于数据进行分页操作。

分组

type Result struct {
    Type  int
    Total int
}
var results []Result
db.Model(Goods{}).Select("type, count(*) as  total").Group("type").Having("total > 0").Scan(&results)

上面的这个代码通过分组将数据分成了两组,一组数据total>0,另一组total小于0。这里的scan类似Find都是用于执行查询语句,然后把查询结果赋值给结构体变量,区别在于scan不会从传递进来的结构体变量提取表名.

直接执行SQL语句

sql := "SELECT type, count(*) as  total FROM `goods` where create_time > ? GROUP BY type HAVING (total > 0)"
db.Raw(sql, "2022-11-06 00:00:00").Scan(&results)
fmt.Println(results)

首先因为sql语句使用了一个问号(?)作为绑定参数, 所以需要传递一个绑定参数(Raw第二个参数),并且Raw函数支持绑定多个参数。

2.5 常见的GORM