Gorm从建表到增删改查 | 青训营

131 阅读8分钟

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这一个参数,后每一行后面也添加了一些代码

  1. 首先,我们建立user模型是为了gorm能根据这个模型对数据库里的user数据表进行操作

  2. gorm.Model,可以看它的源码发现,它也是个结构体,内容如下

     type Model struct {
         ID        uint `gorm:"primarykey"`
         CreatedAt time.Time
         UpdatedAt time.Time
         DeletedAt DeletedAt `gorm:"index"`
     }
    

    写上gorm后会添加上这些参数,其中一些注意事项稍后补充,大家现在知道这个user数据模型的字段由我们写的三个字段,和gorm.model内的三个组成即可

  3. “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来进行数据库操作

  1. 首先InitDB获取mysql配置,并且拼接出dsn

  2. Database()通过传入的dsn构建gorm.DB实例

    1. 通过mysql驱动连接
    2. 自动迁移
    3. 返回

注意:

  1. 在本文中,NamingStrategy这个字段没有必要写出来的,这里有一个gorm的坑,如果不把这个字段设置为true,gorm会给生成的表,表名添加上一个s,比如我的模型名叫user,那么gorm生成的表为users,这会对初学的人造成很大困惑,但gorm就这样进行约定的,不希望数据库表名加上s可以把这个字段设置为true,我个人觉得在本篇中使用问题,不大,如果是初学者,跟着做的话,直接忽略这个字段,不进行设置。
  2. 自动迁移,AutoMigrate()是个很强大的方法,直到这一步之前,我们的数据库架构都只有一个架构,但是这个方法,将传入的User{}模型转换成了users数据表,它会自动在数据库对应架构中进行创建,所以我们先前一直不必要创建数据表,不需要写建表语句,直接通过模型生成即可,如果你对这个功能的强大还有疑虑,可以再次查看模型中gorm tag的更多使用方法,gorm已经能很好的完成建表工作了。
  3. 该函数也能对数据库进行修改,如果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)
 }
  1. 主函数首先进行了配置读取与数据库初始化,分别调用了两个的init函数
  2. 在得到实例db后,首先创建了一个models.User实例,然后,通过该实例进行了创建数据,修改数据,删除数据,操作
  3. 创建了另一个users,进行查询操作

可能有人已经注意到了,在我修改user后直接,进行删除,我又该如何进行验证我修改成功了呢,这个其实直接运行后能够看到的。

我们直接运行查看结果:

数据库结果 这是数据表里的结果

终端输出内容 这是终端输出的结果

6. 数据解释

  1. 从数据表我们可以看到,有七个字段,AutoMigrate()方法,成功生成了这个数据表
  2. user实例已经被创建了——创建成功
  3. user实例在创建时未添加age数据,此时,age字段已有数据,为修改后的数据——修改成功
  4. user实例的deleted_at字段有数据,这代表该字段被软删除了,再使用gorm查找时,将无法查找到该字段。这个功能将会在有deleted_at字段时开启,所以使用gorm.Model建立数据模型时,都会出现这个情况,实际上,数据是宝贵资源,一般业务情况都会选软删除——软删除成功
  5. 创建了第二个实例user2,user2创建时没有附加任何字段,通过First方法查询后,user2获得了id=2的用户的年龄——查询成功
  6. Save()方法,可能大家能看到,这个方法,我用来进行了修改数据,也用来创建数据,这个方法可以顾名思义:保存,如果没有这个数据,就创建然后保存,如果有这个数据,就修改再保存。所以它能同时进行修改和创建。这里只做了简单的增删改查示范,更多操作请查看官方文档
  7. 关于这里使用的Save、Delete、Create数据库操作方法,他们是db的一个方法,函数也有返回值,返回的内容仍然是个db。我们可以这样理解,我们使用SELECT * FROM users WHERE id IN (1,2,3)进行mysql数据库查询,得到的仍然是一张表,而新得到的db将会包括影响的行数,等内容。我们查询得到的数据,会直接反映在我们传入的user结构体中,因为我们传入的是一个指针,所以直接进行了改变,在上面,user2本是个空结构体,其后也才有了年龄。

7. 总结

本文通过viper进行了配置管理,通过gorm实现了简单的数据库建表,增删改查操作。viper其实不必要出现在本文中,不过应该也能为大家的配置管理做一个参考。

通过本文,读者应该能大体了解gorm是如何连接数据库,建表的(更复杂的建表方法,请阅读官方文档),也能了解到简单的增删改查方法和实现操作(更多增删改查方法,请阅读官方文档)。更详细的增删改查操作或者外键等建表操作,高级查询阅读文档即可,有时间我可能也会再整理一下,如果本文有纰漏或疑问欢迎在评论区指正补充

github源码链接