Gorm从建表到增删改查
GORM是一款全功能的ORM,可以实现数据库关联,支持数据库的增删改查,构造SQL等功能,本文主要使用GORM进行MySQL数据库连接,并实现增删改查等操作,文章目标是让读者也能用GORM实现增删改查,更多拓展操作请移步GORM官方文档。
操作过程中,使用了viper进行了配置管理,未使用其他第三方库。viper可能会增加理解成本,但对于配置管理应该也能为大家做一个参考。详细代码请参考github源码链接
1. 创建数据库
在你的数据库上直接进行创建即可,只需要创建架构,无需建表,本文创建的数据库架构名为gorm_demo
本文默认使用的mysql版本为8.0以上的版本,如果是以前的版本,可能需要在第四步第26行代码的mysql.Config配置中添加上几个字段
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
SkipInitializeWithVersion: false, // 根据版本自动配置
2. 配置文件管理编辑
使用了yaml进行配置,放在了config目录下
config.go
var Conf *Config
type Config struct {
MySQL *MySQL `yaml:"mysql"`
}
type MySQL struct {
UserName string `yaml:"username"`
Password string `yaml:"password"`
Host string `yaml:"host"`
Port string `yaml:"port"`
DbName string `yaml:"dbname"`
Charset string `yaml:"charset"`
ParseTime string `yaml:"parse_time"`
Loc string `yaml:"loc"`
}
func InitConfig() {
workDir, _ := os.Getwd()
viper.SetConfigName("config")
viper.SetConfigType("yml")
viper.AddConfigPath(workDir + "/config")
if err := viper.ReadInConfig(); err != nil {
panic(err) //配置读取错误
}
if err = viper.Unmarshal(&Conf); err != nil {
panic(err) //配置解析错误
}
}
配置文件首先从yaml中读取出MySQL的各项配置,用户名,密码,主机,端口号等,如果配置读取错误,会panic,毕竟配置都读取错误了,后续操作也无法进行了。
3. 数据库模型建立
数据库的增删改查都是对表进行的操作,我们只是建立了gorm_demo这一个架构,并未建表,添加数据,这里我们直接建立数据库的模型。
假定我们要建的一张表为user用户表,需要的字段有主键id,用户名username,用户密码password,用户年龄age
model.go
type User struct {
gorm.Model
UserName string `gorm:"not null;comment:用户名称;size:20"`
Password string `gorm:"not null;comment:用户密码;size:20"`
Age uint `gorm:"comment:用户年龄"`
}
实际写下来的代码,大家应该能发现和构想的不同,user模型的结构体,我们没有写id,并且加上了gorm.Model这一个参数,后每一行后面也添加了一些代码
-
首先,我们建立user模型是为了gorm能根据这个模型对数据库里的user数据表进行操作
-
gorm.Model,可以看它的源码发现,它也是个结构体,内容如下
type Model struct { ID uint `gorm:"primarykey"` CreatedAt time.Time UpdatedAt time.Time DeletedAt DeletedAt `gorm:"index"` }写上gorm后会添加上这些参数,其中一些注意事项稍后补充,大家现在知道这个user数据模型的字段由我们写的三个字段,和gorm.model内的三个组成即可
-
“gorm:” 后的内容,为gorm生成表的参考内容,比如not null,会表示这个属性在数据表中是不可为空的,comment表示注释,还有更多的字段说明(包括外键等内容)请参考Gorm官方文档
4. 数据表的初始化与连接
db_init.go
func InitDB() *gorm.DB {
// 从viper中获取配置
mysqlConfig := config.Conf.MySQL
username := mysqlConfig.UserName
password := mysqlConfig.Password
host := mysqlConfig.Host
port := mysqlConfig.Port
dbname := mysqlConfig.DbName
charset := mysqlConfig.Charset
parseTime := mysqlConfig.ParseTime
loc := mysqlConfig.Loc
// 根据配置拼接dsn
dsn := strings.Join([]string{username, ":", password, "@tcp(", host, ":", port, ")/", dbname, "?charset=", charset, "&parseTime=", parseTime, "&loc=", loc}, "")
db, err := Database(dsn) // 传入dsn获取db模型
if err != nil {
panic(err)
}
return db
}
// Database 根据dsn返回gorm.DB实例
func Database(conn string) (*gorm.DB, error) {
// 开启连接并设置配置文件
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: conn,
DefaultStringSize: 256,
}), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
SingularTable: false, // 为true的话,不会给表加yes,false的话,不用写,这里只是列出来做提醒
},
})
if err != nil {
panic(err)
}
err = db.AutoMigrate(models.User{}) // 自动迁移
if err != nil {
return nil, err
}
return db, err
}
db_init.go用于获取gorm.DB来进行数据库操作
-
首先InitDB获取mysql配置,并且拼接出dsn
-
Database()通过传入的dsn构建gorm.DB实例
- 通过mysql驱动连接
- 自动迁移
- 返回
注意:
- 在本文中,NamingStrategy这个字段没有必要写出来的,这里有一个gorm的坑,如果不把这个字段设置为true,gorm会给生成的表,表名添加上一个s,比如我的模型名叫user,那么gorm生成的表为users,这会对初学的人造成很大困惑,但gorm就这样进行约定的,不希望数据库表名加上s可以把这个字段设置为true,我个人觉得在本篇中使用问题,不大,如果是初学者,跟着做的话,直接忽略这个字段,不进行设置。
- 自动迁移,AutoMigrate()是个很强大的方法,直到这一步之前,我们的数据库架构都只有一个架构,但是这个方法,将传入的User{}模型转换成了users数据表,它会自动在数据库对应架构中进行创建,所以我们先前一直不必要创建数据表,不需要写建表语句,直接通过模型生成即可,如果你对这个功能的强大还有疑虑,可以再次查看模型中gorm tag的更多使用方法,gorm已经能很好的完成建表工作了。
- 该函数也能对数据库进行修改,如果users表已经生成,我们再给user模型添加上一些属性,比如加上一个性别属性,那么再次调用这个方法时,users数据表会添加上这个属性。不过,如果删除或者修改字段,该函数,不能支持,因为这可能会清除数据库的数据。
5. 测试
那么到了这一步,我们就可以准备直接写main函数进行测试了
func main() {
config.InitConfig()
db := dao.InitDB()
// user实例用于插入,修改删除操作
user := models.User{
UserName: "vhsj",
Password: "123456",
}
db.Create(&user) // 插入数据
user.Age = 11
db.Save(&user) // 修改数据
db.Delete(&user) // 删除数据
// user2实例,用于查找数据
db.Save(&models.User{ // 插入数据
UserName: "lisi",
Password: "123456",
Age: 10,
})
user2 := models.User{}
db.First(&user2, 2) // 查询数据
fmt.Println("user2的年龄是", user2.Age)
}
- 主函数首先进行了配置读取与数据库初始化,分别调用了两个的init函数
- 在得到实例db后,首先创建了一个models.User实例,然后,通过该实例进行了创建数据,修改数据,删除数据,操作
- 创建了另一个users,进行查询操作
可能有人已经注意到了,在我修改user后直接,进行删除,我又该如何进行验证我修改成功了呢,这个其实直接运行后能够看到的。
我们直接运行查看结果:
这是数据表里的结果
这是终端输出的结果
6. 数据解释
- 从数据表我们可以看到,有七个字段,AutoMigrate()方法,成功生成了这个数据表
- user实例已经被创建了——创建成功
- user实例在创建时未添加age数据,此时,age字段已有数据,为修改后的数据——修改成功
- user实例的deleted_at字段有数据,这代表该字段被软删除了,再使用gorm查找时,将无法查找到该字段。这个功能将会在有deleted_at字段时开启,所以使用gorm.Model建立数据模型时,都会出现这个情况,实际上,数据是宝贵资源,一般业务情况都会选软删除——软删除成功
- 创建了第二个实例user2,user2创建时没有附加任何字段,通过First方法查询后,user2获得了id=2的用户的年龄——查询成功
- Save()方法,可能大家能看到,这个方法,我用来进行了修改数据,也用来创建数据,这个方法可以顾名思义:保存,如果没有这个数据,就创建然后保存,如果有这个数据,就修改再保存。所以它能同时进行修改和创建。这里只做了简单的增删改查示范,更多操作请查看官方文档
- 关于这里使用的Save、Delete、Create数据库操作方法,他们是db的一个方法,函数也有返回值,返回的内容仍然是个db。我们可以这样理解,我们使用
SELECT * FROM users WHERE id IN (1,2,3)进行mysql数据库查询,得到的仍然是一张表,而新得到的db将会包括影响的行数,等内容。我们查询得到的数据,会直接反映在我们传入的user结构体中,因为我们传入的是一个指针,所以直接进行了改变,在上面,user2本是个空结构体,其后也才有了年龄。
7. 总结
本文通过viper进行了配置管理,通过gorm实现了简单的数据库建表,增删改查操作。viper其实不必要出现在本文中,不过应该也能为大家的配置管理做一个参考。
通过本文,读者应该能大体了解gorm是如何连接数据库,建表的(更复杂的建表方法,请阅读官方文档),也能了解到简单的增删改查方法和实现操作(更多增删改查方法,请阅读官方文档)。更详细的增删改查操作或者外键等建表操作,高级查询阅读文档即可,有时间我可能也会再整理一下,如果本文有纰漏或疑问欢迎在评论区指正补充