青训营学习笔记 | 青训营

77 阅读5分钟

路由组

在Go语言中,路由组是一种将多个相关的路由路径进行组织和管理的方式。路由组可以帮助开发者更好地组织和维护应用程序的路由逻辑。

使用路由组,可以将具有相同前缀的路由路径进行分组,并在组级别上应用中间件或其他共享逻辑。这样可以提高代码的可读性和可维护性,同时也可以减少代码的重复。

在Go语言中,常用的路由组实现方式是使用第三方库,如Gin、Echo等。这些库提供了一套简洁而强大的API,使得创建和管理路由组变得非常简单。

通过使用路由组,可以将应用程序的路由逻辑划分为不同的模块或功能区域,并为每个模块或功能区域创建一个独立的路由组。这样可以提高代码的可维护性,并使得不同模块之间的路由逻辑更加清晰和可理解。

r := gin.Default()
	//引用静态文件
	r.Static("/static", "./static")
	//引用模板文件
	r.LoadHTMLGlob("templates/*")
	//设置路由
	r.GET("/", controller.IndexHandler)
	v1 := r.Group("v1")
	{
		todo := v1.Group("todo")
		//增加某个todo
		todo.POST("", controller.AddTodo)
		//查看全部todo
		todo.GET("", controller.GetTodoList)
		//修改某个todo
		todo.PUT(":id", controller.EditTodo)
		//删除某个todo
		todo.DELETE(":id", controller.DeleteTodo)
	}

// GET       	/
// GET       	/v1/todo
// POST   	 	/v1/todo
// PUT      	/v1/todo/:id
// DELETE       /v1/todo/:id 

GORM

连接数据库

GORM会维护一个连接池,它会在初始化数据库之后自动管理所有的数据库连接。这意味着使用者不需要手动关闭数据库连接,GORM会在需要时从连接池中获取连接,并在使用完毕后将其返回给连接池。这样可以提高数据库连接的效率,并且减少了使用者的负担。

创建

模型定义

type User struct {
	ID           int64
	Name         string
	Age          int64
}

使用使用NewRecord()查询主键是否存在,主键为空使用Create()创建记录:

user := User{Name: "xxx", Age: 18}
db.NewRecord(user) // 主键为空返回`true`
db.Create(&user)   // 创建user

Debug():用来查看详细sql

默认值

可以通过 tag 定义字段的默认值,比如:

type User struct {
  ID   int64
  Name string `gorm:"default:'xxx'"`
  Age  int64
}

在Gorm中,当创建记录时,生成的SQL语句会排除没有值或值为零值的字段。这意味着如果某个字段没有被赋值或者被赋值为零值(如0、false、空字符串等),那么该字段不会出现在生成的SQL语句中。

当将记录插入到数据库后,Gorm会从数据库加载那些字段的默认值。这是因为数据库在插入记录时,可能会对某些字段设置了默认值,而Gorm会在加载记录时将这些默认值加载到对应的字段中。

需要注意的是,Gorm只会加载那些在数据库中有默认值的字段,如果某个字段在数据库中没有设置默认值,则Gorm会将其加载为该字段类型的零值。

总结起来,Gorm在创建记录时会排除没有值或值为零值的字段,而在从数据库加载记录时会加载那些字段的默认值。

var user = User{Name: "", Age: 99}
db.Create(&user)

上面代码实际执行的SQL语句是INSERT INTO users("age") values('99');,排除了零值字段Name,而在数据库中这一条数据会使用设置的默认值xxx作为Name字段的值。

所有字段的零值, 比如0, "",false或者其它零值,都不会保存到数据库内,但会使用他们的默认值。

如果想避免这种情况,可以考虑使用指针或实现 Scanner/Valuer接口,比如:

使用指针方式实现零值存入数据库

// 使用指针
type User struct {
  ID   int64
  Name *string `gorm:"default:'xxx'"`
  Age  int64
}
user := User{Name: new(string), Age: 18))}
db.Create(&user)  // 此时数据库中该条记录name字段的值就是''

使用Scanner/Valuer接口方式实现零值存入数据库

// 使用 Scanner/Valuer
type User struct {
	ID int64
	Name sql.NullString `gorm:"default:'xxx'"` // sql.NullString 实现了Scanner/Valuer接口
	Age  int64
}
user := User{Name: sql.NullString{"", true}, Age:18}
db.Create(&user)  // 此时数据库中该条记录name字段的值就是''

map[string]interface{}:实现字符串类型的map接口

Demo注意事项

代码优化

  • 线性原理,处理逻辑尽量走直线
  • 避免复杂的嵌套分支
  • 正常流程代码沿着屏幕向下移动
  • 提升代码可维护性和可读性
  • 故障问题大多出现在复杂的条件语句和循环语句中
//func EditTodo(c *gin.Context) {
//	id := c.Params.ByName("id")
//	var todo Todo
//	//条件查询,判断是否查询成功
//	err := models.EditTodoById(id, (*models.Todo)(&todo))
//	if err != nil {
//		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
//		return
//	} else {
//		//绑定数据
//		err := c.BindJSON(&todo)
//		if err != nil {
//			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
//			return
//		} else {
//			//数据保存到数据库中
//			err := models.SaveTodo((*models.Todo)(&todo))
//			if err != nil {
//				c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
//				return
//			} else {
//				c.JSON(http.StatusOK, todo)
//			}
//		}
//	}
//}

如果后期增加功能的时候要添加新的函数,那么又需要嵌套一层,写起来是很麻烦的 可以考虑使用下面的写法:

if err:=  ;err!=nil{
	//错误处理
}

这样上面的代码段就可以优化成这样,看起来更方便,逻辑也显得更清晰,改动代码的时候也不用一层一层去翻嵌套了

func EditTodo(c *gin.Context) {
	id := c.Params.ByName("id")
	var todo Todo
	if err := models.EditTodoById(id, (*models.Todo)(&todo)); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	if err := c.BindJSON(&todo); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	if err := models.SaveTodo((*models.Todo)(&todo)); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, todo)
}