gorm之项目实战
ER图
关系整理
-
一对一关系:
- User 和 UserLog: 一个用户对应一个用户日志,通过
User
模型的主键与UserLog
模型的外键建立一对一关系。
- User 和 UserLog: 一个用户对应一个用户日志,通过
-
一对多关系:
- User 和 Teacher: 一个用户可以对应多个老师,通过
Teacher
模型的外键(UserID
)与User
模型的主键建立一对多关系。 - User 和 Student: 一个用户可以对应多个学生,通过
Student
模型的外键(UserID
)与User
模型的主键建立一对多关系。 - Teacher 和 Class: 一个老师可以有多个班级,通过
Class
模型的外键(TeacherID
)与Teacher
模型的主键建立一对多关系。 - Student 和 StudentClass: 一个学生可以有多个班级,通过
StudentClass
模型的外键(StudentID
)与Student
模型的主键建立一对多关系。 - Student 和 Attendance: 一个学生可以有多条考勤记录,通过
Attendance
模型的外键(StudentID
)与Student
模型的主键建立一对多关系。
- User 和 Teacher: 一个用户可以对应多个老师,通过
-
多对多关系:
- Student 和 Class: 一个学生可以属于多个班级,一个班级可以有多个学生,通过
StudentClass
模型作为中间表,建立多对多关系。 - Teacher 和 Class: 一个老师可以教授多个班级,一个班级可以有多个老师,通过
Class
模型的外键(TeacherID
)与Teacher
模型的主键,建立多对多关系。
- Student 和 Class: 一个学生可以属于多个班级,一个班级可以有多个学生,通过
-
一对多逆向关系:
- Parent 和 Student: 一个家长可以有多个子女,通过
Student
模型的外键(StudentID
)与Parent
模型的主键建立一对多逆向关系。
- Parent 和 Student: 一个家长可以有多个子女,通过
使用gen自动化生成代码
首先使用gen工具生成代码
package main// gorm gen configureimport ("fmt""gorm.io/driver/mysql""gorm.io/gorm""gorm.io/gen"
)const MySQLDSN = "root:root@tcp(127.0.0.1:3306)/school?charset=utf8mb4&parseTime=True"func connectDB(dsn string) *gorm.DB {db, err := gorm.Open(mysql.Open(dsn))if err != nil {panic(fmt.Errorf("connect db fail: %w", err))}return db
}func main() {// 指定生成代码的具体相对目录(相对当前文件),默认为:./query// 默认生成需要使用WithContext之后才可以查询的代码,但可以通过设置gen.WithoutContext禁用该模式g := gen.NewGenerator(gen.Config{// 默认会在 OutPath 目录生成CRUD代码,并且同目录下生成 model 包// 所以OutPath最终package不能设置为model,在有数据库表同步的情况下会产生冲突// 若一定要使用可以通过ModelPkgPath单独指定model package的名称OutPath: "dao/query",/* ModelPkgPath: "dal/model"*/// gen.WithoutContext:禁用WithContext模式// gen.WithDefaultQuery:生成一个全局Query对象Q// gen.WithQueryInterface:生成Query接口Mode: gen.WithDefaultQuery | gen.WithQueryInterface,})// 通常复用项目中已有的SQL连接配置db(*gorm.DB)// 非必需,但如果需要复用连接时的gorm.Config或需要连接数据库同步表信息则必须设置g.UseDB(connectDB(MySQLDSN))// 从连接的数据库为所有表生成Model结构体和CRUD代码// 也可以手动指定需要生成代码的数据表g.ApplyBasic(g.GenerateAllTable()...)// 执行并生成代码g.Execute()
}
在gen生成的model中定义外键联系
生成的model代码在dao/model
我们需要在这些Model中定义外键关系,先把User表and Teacher表与Student表关系定义
一对一关系
首先先确定主表和附表,主表
-
主表:User,逐渐:UserID
-
附表:Student,外键:UserID
-
附表:Teacher,外键:UserID
-
在model.User中添加两个
-
把generate改下,我们要用改过的模型生成query
-
g.ApplyBasic(model.Student{},model.Teacher{},model.User{},model.UserLog{},model.Class{},model.Course{},model.Attendance{},model.StudentClass{},model.Parent{},)// 执行并生成代码g.Execute()
增加用户业务逻辑
这边按UserType新建对应的学生和教师
func CreateUser(c *gin.Context) {var req request.CreateUserRequestif err := c.ShouldBindJSON(&req); err != nil {c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "error": "创建用户请求失败,无效的请求参数"})return}var user model.Userswitch req.UserType {case "Student":user = model.User{Username: req.Username,Password: req.Password,OpenID: req.OpenID,Avatar: req.Avatar,LastLogin: time.Now(),UserType: req.UserType,IsValid: req.IsValid,Student: model.Student{StudentName: req.Username,},}case "Teacher":user = model.User{Username: req.Username,Password: req.Password,OpenID: req.OpenID,Avatar: req.Avatar,LastLogin: time.Now(),UserType: req.UserType,IsValid: req.IsValid,Teacher: model.Teacher{TeacherName: req.Username,},}default:c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "error": "无效的用户类型"})return}err := query.User.WithContext(context.Background()).Create(&user)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"code": http.StatusInternalServerError, "error": fmt.Sprintf("创建用户请求失败,无法创建用户: %v", err)})return}c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": "用户创建成功"})
}
删除用户业务逻辑
// DeleteUser 处理删除用户请求的函数
func DeleteUser(c *gin.Context) {userIDStr := c.Param("id") // Assuming the route has "id" parametervar User model.UseruserID, err := strconv.ParseInt(userIDStr, 10, 64)if err != nil {c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "error": "Failed to delete user, invalid user ID"})return}//var user model.Userconfig.GVA_DB.Take(&User, userID)ret := config.GVA_DB.Select("Student").Delete(&User)if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"code": http.StatusInternalServerError, "error": fmt.Sprintf("Failed to delete user, unable to delete user: %v", err)})return}c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": fmt.Sprintf("User deleted successfully, RowsAffected: %v", ret.RowsAffected)})
}
更新
这里因为是主表,逻辑上之更新注表就好,后面改相应表的在编写逻辑
主要是改改头像什么的
查找
// GetUser 处理获取单个用户请求的函数
func GetUser(c *gin.Context) {userIDStr := c.Param("id") // Assuming the route has "id" parameteru := query.UseruserID, err := strconv.ParseInt(userIDStr, 10, 64)if err != nil {c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest, "error": "Failed to get user, invalid user ID"})return}user, err := query.User.WithContext(context.Background()).Where(query.User.UserID.Eq(int32(userID))).Preload(u.Student, u.Teacher).First()if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"code": http.StatusInternalServerError, "error": fmt.Sprintf("Failed to get user, unable to get user: %v", err)})return}c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "user": user})
}// GetAllUsers 处理获取所有用户请求的函数
func GetAllUsers(c *gin.Context) {u := query.Userusers, err := query.User.WithContext(context.Background()).Preload(u.Student, u.Teacher).Find()if err != nil {c.JSON(http.StatusInternalServerError, gin.H{"code": http.StatusInternalServerError, "error": fmt.Sprintf("Failed to get all users, unable to get user list: %v", err)})return}c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "users": users})
}