GORM&RESTFUL API

链接与建表

基础概念

  • 数据库类似于一个Excel表,与Redis相比,SQL必须建一个库,才能建表
  • Table类似于Excel里面的Sheet工作表,表必须规定好表头
  • Model模型,就是Go的结构体,GORM负责把结构体变成MySQL表。

核心函数解析

dsn (Data Source Name)

这是连接数据库的“身份证”,格式是固定的: 用户名:密码@tcp(IP:端口)/数据库名?参数

  • charset=utf8mb4:必须加,否则存不了表情包(支持Emoji)。
  • parseTime=True:把数据库的时间自动转成 Go 的 time.Time

gorm.Open(dialector, config)

  • 建立连接池。
  • 如果是生产环境,通常还需要配置 SetMaxIdleConns 等参数,基础学习默认配置即可。

db.AutoMigrate(&Struct{})

  • 自动迁移,它会扫描你的 Struct 字段,对比数据库里的表。如果表不存在 -> 自动创建。如果表存在但少了字段 -> 自动添加字段。不会删除字段:为了安全,它绝不会自动删除你数据库里已有的数据列。

gorm.Model

  • 包含字段:ID (主键), CreatedAt, UpdatedAt, DeletedAt
  • 在你的结构体里匿名嵌入它,你的表就自动拥有了这四个标准字段。

代码示例

这里我用docker起了一个数据库

image-20260202205834577

并且

 docker exec -it mysql01 mysql -uroot -p123456
 create database gin_rank;
 package main
 ​
 import (
  "fmt"
 ​
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
 )
 ​
 type User struct {
  gorm.Model
  Name string
  Age  int
 }
 ​
 func main() {
  dsn := "root:123456@tcp(localhost:3306)/gin_rank?charset=utf8mb4&parseTime=True&loc=Local"
  /*链接*/
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
  if err != nil {
  panic("Failed to connect database")
  }
  fmt.Println("Database connected successfully")
  /*建表*/
  err = db.AutoMigrate(&User{})
  if err != nil {
  panic("Migartion failed")
  }
  fmt.Println("Table 'users' migrated successfully!")
 }
 ​

我们进入容器

 docker exec -it mysql01 mysql -uroot -p123456

执行SQL

 use gin_rank;
 desc users;
image-20260202211314248

CRUD

Create—增

核心函数解析

result := db.Create(&user)

  • 参数:必须传入结构体的指针 &user。GORM 插入数据后,数据库会生成一个自增的 ID(比如 1, 2, 3),GORM 需要把这个新生成的 ID 写回到你的 user 变量里,让你知道刚才插入的数据 ID 是多少。
  • 返回值:返回一个 *gorm.DB 对象(这里叫 result),包含两个重要字段:
    • result.Error: 如果插入失败(比如名字太长、数据库断开),这里会有值。
    • result.RowsAffected: 告诉你插入了几条数据(通常是 1)。

代码实现

 package main
 ​
 import (
  "fmt"
 ​
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
 )
 ​
 type User struct {
  gorm.Model
  Name string
  Age  int
 }
 ​
 func main() {
  /*此处省略链接和建表*/
  u1 := User{
  Name: "ttdr",
  Age:  18,
  }
  result := db.Create(&u1)
  if result.Error != nil {
  fmt.Println("插入失败", result.Error)
  } else {
  fmt.Printf("插入成功,新用户ID为:%d\n影响行数:%d\n", u1.ID, result.RowsAffected)
  }
 }
image-20260202213645621

Read—查

核心函数解析

1.db.First(&user,1)

获取ID为1的内容,并且写入到user中,这里需要传地址如果没有id就默认第一条。

2.db.Where("name = ?", "ttdr").Find(&users)

根据条件查询名字为ttdr的数据,语法跟英语一样,好玩捏。后面是将获得到的数据写入到切片中。

代码实现

     var user User
  var users []User
  db.First(&user)
  fmt.Println("第一个用户", user.Name)
  db.First(&user, 1) /*根据id查询*/
  db.Where("name = ?", "ttdr").Find(&users)
  fmt.Println("叫ttdr的数量为:", len(users))
  db.Where("name = ? AND age >= ?", "ttdr", 18).Find(&users)
image-20260202220137312

Change—改

核心函数解析

1.db.Save

整体保存,更新所有字段

2.Model + Update (局部更新)

只想对Age修改,不想动其他的

 db.Model(&User{}).Where("id = ?", 0).Update("age", 18)

代码实现

	var u User
db.First(&u, 1)
u.Name = "NewName"
u.Age = 99
db.Save(&u)
db.Model(&User{}).Where("id = ?", 1).Update("age", 25)
image-20260202220739203

Delete—删

核心函数解析

db.Delete(&User{}, id)

没啥好说的喵

代码实现

	db.Delete(&User{}, 1)
image-20260202221016086

?????????怎么在

设计哲学

1.对0的极端恐惧

以上面为例,如果要把Age更新为0,你会这样写

db.Model(&user).Updates(User{Name: "baby", Age: 0})

但是数据库里的AGe不会修改,因为GO语言中,0"",false,GoRM设计者认为如果字段是0谁知道你存的是0还是本身就没存,为了防止这种情况,在结构体更新时,自动忽略所有零值。

在实际开发中,如果我们一定要写入0的时候我们采用map写入,因为map没有键就是没有,有键就是有值。

db.Model(&user).Updates(map[string]interface{}{"name": "baby", "age": 0})

虽然这样确实有点烦,但是避免了前端少传字段,后端吧数据库字段洗白的事故。

2.软删除

因为数据的不可逆性,所以在企业开发中,物理删除是大忌(删库跑路),一旦物理删除,几乎不可恢复,所以GORM默认开启软删除,你查的时候查不到被删数据,再出现事故后,用SQL手动查或者用Unscoped()就能把删除的数据恢复。

RESTFUL API

引入

RESTful API是一种API设计风格,用HTTP方法来表达动作,我们来写POST,GET,PUT,DELETE增删改查四个接口

话不多说我们直接上代码

代码实现

package main

import (
"net/http"

"github.com/gin-gonic/gin"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)

type User struct {
gorm.Model
Name string
Age int
}

var DB *gorm.DB

func initDB() {
dsn := "root :123456@tcp(127.0.0.1:3306)/gin_rank?charset=stf8mb4&parseTime=True&loc=Local"
var err error
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("数据路连接失败" + err.Error())
}
DB.AutoMigrate(&User{})
}

func CreateUser(c *gin.Context) {
var user User
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"errpr": err.Error()})
return
}
/*存入数据库*/
if err := DB.Create(&user).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "创建失败"})
return
}
c.JSON(http.StatusCreated, gin.H{
"msg": "create successfully",
"data": user,
})
}

func GetUserList(c *gin.Context) {
var users []User
DB.Find(&users)
c.JSON(http.StatusOK, gin.H{
"data": users,
})
}

func UpdateUser(c *gin.Context) {
id := c.Param("id")
var user User
if err := DB.First(&user, id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "用户存在"})
return
}
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"err": err.Error()})
return
}
DB.Save(&user)
c.JSON(http.StatusOK, gin.H{
"msg": "update successfully",
"data": user,
})
}

func DeleteUser(c *gin.Context) {
id := c.Param("id")
if err := DB.Delete(&User{}, id).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "删除失败"})
return
}
c.JSON(http.StatusOK, gin.H{
"msg": "delete successfully",
})
}

func main() {
initDB()
r := gin.Default()
v1 := r.Group("/v1")
{
v1.POST("/users", CreateUser)
v1.GET("/users", GetUserList)
v1.PUT("/users/:id", UpdateUser)
v1.DELETE("/users/:id", DeleteUser)
}
r.Run(":8080")
}

可以用APIFOX测试接口

上面的代码虽然能跑,但是emm差点意思,没有一点架构的味道。

暂无评论

发送评论 编辑评论


				
上一篇
下一篇