GORM连接数据库,并实现增删改查操作 7200字实践🎉🎉🎉|青训营

91 阅读12分钟

这是一篇实践文章。在本篇文章中,将会使用GORM连接数据库,并实现增删改查操作。

1. GORM简介

GORM是一个Go语言的ORM(对象关系映射)库,提供了一种简单、强大和灵活的方式来操作数据库。GORM的目标是让开发人员能够更加方便地使用Go语言来进行数据库操作,而无需过多关注底层的SQL语句和数据库细节。

GORM支持多种数据库,包括MySQL、PostgreSQL、SQLite、SQL Server等,并提供了一致的API来操作这些数据库。它使用结构体来定义数据模型,并通过标签来指定数据库表名、字段名、索引等信息。通过这种方式,可以轻松地将Go语言的结构体映射到数据库中的表和字段。

GORM提供了丰富的查询和操作方法,包括创建、读取、更新和删除数据等。它支持链式调用,可以方便地构建复杂的查询条件和排序方式。同时,GORM还提供了事务管理、预加载、关联查询等高级功能,以满足各种复杂的业务需求。

除了基本的CRUD操作,GORM还提供了一些其他的特性,如自动迁移、数据库连接池管理、钩子函数等。自动迁移功能可以根据数据模型自动创建数据库表和字段,简化了数据库的初始化和升级过程。数据库连接池管理可以提高数据库的性能和并发能力。钩子函数可以在数据库操作的不同阶段插入自定义的逻辑,增强了灵活性和扩展性。

GORM是一个功能强大且易于使用的ORM库,简化了Go语言与数据库交互的过程,提高了开发效率和代码质量。GORM对开发者很友好,目前使用最广的Go ORM库之一。

2. GORM连接数据库

2.1 MySQL数据库建表

打开GoLand,进入到查询控制台,执行SQL语句建表:

CREATE TABLE `user` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID',
    `username` varchar(30) NOT NULL COMMENT '用户名',
    `password` varchar(100) NOT NULL COMMENT '密码',
    `create_time` timestamp  NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

创建一个名为"user"的表,表中包含以下字段:

  • id:自增ID,数据类型为无符号整数(int),长度为10,用于唯一标识每条记录。
  • username:用户名,数据类型为字符串(varchar),长度为30,用于存储用户的用户名信息。
  • password:密码,数据类型为字符串(varchar),长度为100,用于存储用户的密码信息。
  • create_time:创建时间,数据类型为时间戳(timestamp),长度为20,用于记录用户创建的时间戳,默认值为当前时间。 该表使用InnoDB引擎,这是MySQL中一种常用的存储引擎,它提供了事务支持和行级锁定等功能。表的字符集为utf8mb4,支持存储各种Unicode字符。 此外,id字段被指定为主键(PRIMARY KEY),用于唯一标识每条记录。主键字段的值必须是唯一的,且不能为空。通过指定主键,可以提高查询性能和数据的完整性。

2.2 定义数据模型

在GORM框架中,操作数据库需要预先定义数据模型。首先定义了User结构体用来和数据表user构成映射。这个结构体就是数据模型。GORM框架远离即为使用的Go的database标准库,利用反射原理,执行读写操作时,将结构体翻译为SQL语句,并将获得结果转化为对应的数据模型。

//定义User模型,绑定users表
type User struct {
    ID int64 // 主键
    Username string `gorm:"column:username"`
    Password string `gorm:"column:password"`
    //创建时间戳
    CreatedAt time.Time `gorm:"column:created_at"`
}

gorm:"column:username" 为模型标签。

标签介绍
column指定列名
primaryKey指定主键
-忽略字段

2.3 表名映射

定义了TableName()方法,用于指定该结构体对应的数据库表名为"user"

type User struct {
	ID         int64
	Username   string `gorm:"column:username"`
	Password   string `gorm:"column:password"`
	CreatedAt time.Time  `gorm:"column:create_at"`
}

func (User) TableName() string {
	//绑定MYSQL表名为user
	return "user"
}

2.4 数据库连接

GORM 官方支持的数据库类型有:

  • MySQL
  • PostgreSQL
  • SQlite
  • SQL Server

连接数据库主要是两个步骤:

  • 配置DSN (Data Source Name)
    • GORM使用DSN作为连接数据库的参数,DSN即为数据源名称,用来描述数据库连接信息。
    • 一般都包含数据库连接地址,连接名,密码信息。
  • 使用gorm.Open连接数据库

var db *gorm.DB

func init() error {
    var err error

    dsn := "root:li06338687@tcp(127.0.0.1:3306)/community?charset=utf8mb4&parseTime=True&loc=Local"
    db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
        Logger: logger.Default.LogMode(logger.Info),
    })
    return err

}
//mysql dsn格式
//username   数据库账号
//password   数据库密码
//host       数据库连接地址,可以是Ip或者域名
//port       数据库端口
//Dbname     数据库名
//  charset=utf8mb4 客户端字符集为utf8
//  parseTime=true 支持把数据库datetime和date类型转换为golang的time.Time类型
//  loc=Local 使用系统本地时区
username:password@tcp(host:port)/Dbname?charset=utf8&parseTime=True&loc=Local
  • 设置10秒后连接超时,timeout=10s
  • readTimeout - 读超时时间,0代表不限制
  • writeTimeout - 写超时时间,0代表不限制
    sqlDB, _ := db.DB()

    //设置数据库连接池参数
    sqlDB.SetMaxOpenConns(100)   //设置数据库连接池最大连接数
    sqlDB.SetMaxIdleConns(20)   //连接池最大允许的空闲连接数,如果没有sql需要执行的连接数大于20,超过的连接会被连接池关闭

3. 增删改查

3.1 增加数据


type UserDao struct {
}

var userDao *UserDao
var userOnce sync.Once

func NewUserDaoInstance() *UserDao {
    userOnce.Do(
       func() {
          userDao = &UserDao{}
       })
    return userDao
}

func (*UserDao) InsertNewUserInfo(user *User) error {

    if err := db.Create(usert).Error; err != nil {
        Util.Logger.Error("insert user err:" + err.Error())
        return err
    }
    return nil

}

这段代码定义了一个UserDao的结构体,用于封装与用户数据表相关的数据库操作。该结构体中包含了一个方法InsertNewUserInfo,用于向数据库中插入新的用户信息。

在代码中,使用了单例模式来创建UserDao的实例。通过NewUserDaoInstance函数获取UserDao的实例,该函数使用了sync.Once来确保实例只被创建一次。在NewUserDaoInstance函数中,通过匿名函数的方式创建了UserDao的实例,并将其赋值给userDao变量。

InsertNewUserInfo方法接受一个指向User结构体的指针作为参数,表示要插入的用户信息。在方法中,通过db.Create(user).Error来执行数据库插入操作。如果插入过程中出现错误,会记录错误日志并返回错误信息。如果插入成功,则返回nil

r.GET("/register", func(c *gin.Context) {
    user := &Dao.User{
		Username:   "lorenzo",
		Password:   "123456",
		CreateTime: time.Now(),
	}
	if err := Dao.NewPostDaoInstance().InsertNewUserInfo(user); err != nil {
        fmt.Errorf("failed: %w", err)

   }
        c.JSON(200, user)
})

基于Gin框架的路由处理函数,当客户端发送GET请求到"/register"时,该函数会被执行。

在函数中,首先创建了一个名为"user"的结构体指针user,并给其字段赋值。注册的用户的用户名为"lorenzo",密码为"123456",创建时间为当前时间。

接下来,调用了Dao.NewPostDaoInstance().InsertNewUserInfo(user)方法,将用户信息插入到数据库中。该方法会将用户信息插入到"user"表中。

最后,函数使用c.JSON(200, data)方法返回一个JSON响应给客户端,状态码为200。

3.2 修改数据

func (*UserDao) UpdateUserInfoById(id int64) error {
	if err := db.Model(&User{}).Where("id=?", id).Update("username", "new"); err != nil {
        Util.Logger.Error("update user err:" + err.Error())
        return err
    }
    return nil
}

定义了一个名为UpdateUserInfoById的方法,该方法属于UserDao结构体。

该方法接受一个id参数,表示要更新的用户的ID。在方法中,使用db.Model(&User{})来指定要更新的数据模型为User结构体。然后,使用Where("id=?", id)来指定更新条件,即根据id字段进行匹配。

接下来,使用Update("username", "new")来更新username字段的值为"new"。"username"表示要更新的字段名,"new"表示要更新的字段值。

如果更新过程中出现错误,会记录错误日志并返回错误信息。如果更新成功,则返回nil

r.GET("/update", func(c *gin.Context) {
	if err := Dao.NewPostDaoInstance().UpdateUserInfoById(1); err != nil {
        fmt.Errorf("failed: %w", err)
     }
        if err := user := Dao.NewPostDaoInstance().GetUserInfoById(1); err != nil {
        fmt.Errorf("failed: %w", err)
     }
        c.JSON(200, user)
})

基于Gin框架的路由处理函数,当客户端发送GET请求到"/register"时,该函数会被执行。

在函数中,首先调用了Dao.NewPostDaoInstance().UpdateUserInfoById(1)方法,该方法会根据用户ID为1来更新用户信息。具体的更新操作是由UpdateUserInfoById方法来完成。

接下来,调用了Dao.NewPostDaoInstance().GetUserInfoById(1)方法,该方法会根据用户ID为1来获取用户信息。具体的获取操作是由GetUserInfoById方法来完成。

最后,函数使用c.JSON(200, user)方法返回一个JSON响应给客户端,状态码为200。其中,user是获取到的用户信息。

3.3 查询数据

func (*UserDao) GetUserInfoById(id int64) (User, error) {
	var user User
	if err := db.Where("id=?", id).First(&user).Error; err != nil {
            Util.Logger.Error("search user err:" + err.Error())
            return err
        }
	return user, nil
}


func (*UserDao) GetUserInfoAll() ([]User, error) {
	var users []User
	if err := db.Find(&users); err != nil {
            Util.Logger.Error("search users err:" + err.Error())
            return err
        }
	return users, nil
}

第一段代码是一个方法GetUserInfoById,属于UserDao结构体。该方法接受一个id参数,表示要查询的用户的ID。在方法中,首先定义了一个user变量,用于存储查询结果。

然后,使用db.Where("id=?", id).First(&user)来查询数据库中符合条件的用户信息,并将结果存储到user变量中。db是数据库连接对象,Where方法用于指定查询条件,First方法用于执行查询操作。

如果查询过程中出现错误,会记录错误日志。最后,返回查询到的用户信息。

第二段代码GetUserInfoAll,属于UserDao结构体。该函数没有参数。在函数中,首先定义了一个users变量,用于存储查询结果。

然后,使用db.Find(&users)来查询数据库中的所有用户信息,并将结果存储到users变量中。Find方法用于执行查询操作,&users表示将查询结果赋值给users变量的地址。

如果查询过程中出现错误,会记录错误日志。最后,返回查询到的所有用户信息。

r.GET("/searchAll", func(c *gin.Context) {
     user, err := Dao.NewPostDaoInstance().GetUserInfoAll()
     if err != nil {
         fmt.Errorf("failed: %w", err)
     }
     c.JSON(200, users)
})

r.GET("/searchUser", func(c *gin.Context) {
     user, err := Dao.NewPostDaoInstance().GetUserInfoById(1)
     if err != nil {
        fmt.Errorf("failed: %w", err)
     }
     c.JSON(200, user)
}

基于Gin框架的路由处理函数。

第一段代码处理了"/searchAll"的GET请求。当客户端发送GET请求到"/searchAll"时,该函数会被执行。

在函数中,首先调用了Dao.NewPostDaoInstance().GetUserInfoAll()方法来获取所有用户信息。该方法返回一个users变量和一个err错误。接着,通过if err != nil判断是否有错误发生。

如果有错误发生,会使用fmt.Errorf("failed: %w", err)将错误包装并返回给客户端。如果没有错误发生,会使用c.JSON(200, users)users变量作为JSON响应返回给客户端。

第二段代码处理了"/searchUser"的GET请求。当客户端发送GET请求到"/searchUser"时,该函数会被执行。

在函数中,首先调用了Dao.NewPostDaoInstance().GetUserInfoById(1)方法来获取ID为1的用户信息。该方法返回一个user变量和一个err错误。接着,通过if err != nil判断是否有错误发生。

如果有错误发生,会使用fmt.Errorf("failed: %w", err)将错误包装并返回给客户端。如果没有错误发生,会使用c.JSON(200, user)user变量作为JSON响应返回给客户端。

3.4 删除数据

func (*UserDao) DeleteUserById(id int64) error {
	if err := DB.Where("id=?", id).Delete(&User{}); err != nil {
              Util.Logger.Error("search user err:" + err.Error())
              return err
        }
	return nil
}

这段代码是一个方法DeleteUserById,属于UserDao结构体。该方法接受一个id参数,表示要删除的用户的ID。在方法中,首先使用DB.Where("id=?", id).Delete(&User{})来删除数据库中符合条件的用户信息。

DB是数据库连接对象,Where方法用于指定删除条件,Delete方法用于执行删除操作。&User{}表示删除符合条件的用户信息。

如果删除过程中出现错误,会记录错误日志并返回错误信息。如果删除成功,会返回nil表示没有错误发生。

r.GET("/deleteUser", func(c *gin.Context) {
     err := Dao.NewPostDaoInstance().DeleteUserById(1)
     if err != nil {
        fmt.Errorf("failed: %w", err)
     }
     c.JSON(200)
}

基于Gin框架的路由处理函数。

当客户端发送GET请求到"/deleteUser"时,该函数会被执行。

在函数中,首先调用了Dao.NewPostDaoInstance().DeleteUserById(1)方法来删除ID为1的用户信息。该方法返回一个err错误。接着,通过if err != nil判断是否有错误发生。

如果有错误发生,会使用fmt.Errorf("failed: %w", err)将错误包装并返回给客户端。

4. GORM实践结语

在今天的实践中,我成功地使用GORM连接了数据库,并实现了增删改查的操作。这个过程让我对GORM有了更深入的了解,并对数据库操作有了更好的掌握。

首先,GORM提供了一种简洁而直观的方式来定义和映射数据库模型。通过结构体和标签的方式,我们可以轻松地将Go语言的数据结构映射到数据库表中,而无需手动编写SQL语句。这大大简化了数据库操作的过程,提高了开发效率。

其次,GORM的链式查询功能非常强大。我们可以使用连续的方法调用来构建复杂的查询条件,包括筛选、排序、分页等操作。这样,我们可以灵活地根据需求来查询数据库,并获取我们所需要的数据。同时,GORM还提供了预加载、延迟加载等功能,可以优化查询性能,减少数据库的负载。

另外,GORM还支持事务操作,保证了数据的一致性和完整性。通过开启事务,我们可以在多个操作中保持数据的一致性,并在出现错误时进行回滚,避免了数据损坏或不一致的情况。

GORM是一个功能强大、易用性高的ORM框架,使得数据库操作变得简单、高效。通过今天的实践,我深刻体会到了它的优势和便利性。我相信在今后的开发工作中,我会更加倾向于使用GORM来处理数据库操作,以提高开发效率和代码质量。