做货代的可以在哪些网站打广告,网站导航栏目设计内容依据,网站dns,北京网站建设天下公司导读#xff1a;
在 Go 项目中#xff0c;虽然没有像 MyBatis Plus 这样特定的 ORM 插件#xff0c;但可以使用功能相似的 Go ORM 框架#xff0c;比如 GORM#xff0c;它支持链式查询、自动迁移、预加载等功能#xff0c;与 MyBatis Plus 有相似之处。通过一些插件或扩…导读
在 Go 项目中虽然没有像 MyBatis Plus 这样特定的 ORM 插件但可以使用功能相似的 Go ORM 框架比如 GORM它支持链式查询、自动迁移、预加载等功能与 MyBatis Plus 有相似之处。通过一些插件或扩展可以实现更丰富的功能比如软删除、分页查询等。下面是 GORM 集成的一些步骤和相关插件的推荐本文以之前集成的项目Go语言IDEA能支持吗增删查走起_go idea-CSDN博客为模版在此基础上进行迭代。
目录 一、引入GORM
二、GORM能力
2.1分页查询
2.2软删除
2.3分页拓展 2.4链式查询
2.5自定义拓展
三、源码改造
3.1改造数据库连接类database.go
3.2改造中间件DBMiddleware
3.3 改造userDao.go
3.4 改造userService.go
3.5 改造router.go
3.6 改造main.go
四、启动 五、异常情况
5.1解决思路 六、复杂查询拓展
6.1实体类Orders.go 6.2新增OrderInfoDTO.go
6.3新增GetUserWithOrders方法 一、引入GORM
先在项目中引入 GORM
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql # 根据你的数据库类型选择驱动二、GORM能力
GORM 提供了一些类似 MyBatis Plus 的功能比如
软删除通过 gorm.Model 或自定义字段来实现软删除。条件查询可以通过链式查询构建条件查询。分页插件可以手动实现分页或使用封装的分页插件。
2.1分页查询
go 一遍集成GORM 的 Limit 和 Offset 方法实现分页查询
var users []User
db.Limit(10).Offset(20).Find(users) // 查询第 21-30 条数据2.2软删除
例如通过 gorm.Model 或自定义字段来实现软删除。
type User struct {gorm.ModelName stringAge int
}db.Delete(user) // 软删除2.3分页拓展
可以手动实现分页或使用封装的分页插件比如分页器 github.com/biezhi/gorm-paginator 。 2.4链式查询
GORM 支持链式调用来构建复杂的查询
var users []User
db.Where(age ?, 18).Find(users)2.5自定义拓展
如果需要类似 MyBatis Plus 的扩展功能你可以通过自定义 GORM 的钩子函数Hooks或中间件来实现数据插入、更新时自动添加字段等逻辑。GORM 提供了生命周期回调接口可以在执行操作前后进行拦截和处理。
三、源码改造
3.1改造数据库连接类database.go 引入 GORM通过 gorm.io/gorm 和 gorm.io/driver/mysql 代替原来的 database/sql。 初始化数据库连接使用 gorm.Open() 方法替代 sql.Open()并且支持更多的配置参数比如 charsetutf8mb4 和 parseTimeTrue。 GetDB 函数返回 *gorm.DB 类型的数据库实例供其他模块使用。 这边删除了原先的func GetDB() *sql.DB { return DB } 方法
package configimport (fmtgorm.io/driver/mysqlgorm.io/gormgorm.io/gorm/loggerlog
)var DB *gorm.DB// InitGormDatabase 初始化 GORM 数据库连接并返回 *gorm.DB
func InitGormDatabase() *gorm.DB {dsn : fmt.Sprintf(%s:%stcp(%s:%s)/%s?charsetutf8mb4parseTimeTruelocLocal,AppConfig.Database.User,AppConfig.Database.Password,AppConfig.Database.Host,AppConfig.Database.Port,AppConfig.Database.DBName,)var err errorDB, err gorm.Open(mysql.Open(dsn), gorm.Config{Logger: logger.Default.LogMode(logger.Info),})if err ! nil {log.Fatalf(Failed to connect to database: %v, err)return nil}log.Println(GORM database successfully connected)return DB
}3.2改造中间件DBMiddleware
这边说明一下中间件的引入是刚搭建项目时想从java中的切面编程来实现跨功能关注点的处理。比如数据库的连接。但是呢go没有内置切面的支持。所以就引入了中间件模式每次调用数据库操作都通过中间件来处理。下面是优化前的中间件
package middlewareimport (database/sqlgo_tas/dao/databaselog
)type DBMiddleware struct {db *sql.DB
}func NewDBMiddleware() *DBMiddleware {return DBMiddleware{db: database.GetDB(),}
}func (mw *DBMiddleware) ExecuteQuery(query string, args ...interface{}) (*sql.Rows, error) {rows, err : mw.db.Query(query, args...) // 使用中间件中保存的 dbif err ! nil {return nil, mw.logError(Failed to execute query, err)}return rows, nil
}func (mw *DBMiddleware) ExecuteExec(query string, args ...interface{}) (int64, error) {result, err : mw.db.Exec(query, args...) // 使用中间件中保存的 dbif err ! nil {return 0, mw.logError(Failed to execute exec, err)}insertID, err : result.LastInsertId()if err ! nil {return 0, mw.logError(Failed to get last insert ID, err)}return insertID, nil
}func (mw *DBMiddleware) BeginTransaction() (*sql.Tx, error) {tx, err : mw.db.Begin() // 使用中间件中保存的 dbif err ! nil {return nil, mw.logError(Failed to begin transaction, err)}return tx, nil
}func (mw *DBMiddleware) logError(message string, err error) error {log.Printf(%s: %v, message, err)return err
}
使用原生的 database/sql 改造为使用 GORM可以直接使用 GORM 的功能来处理查询、事务等操作。GORM 已经封装了这些常见的操作并且支持更简洁的 API。
下面是改造后的 middleware 模块使用 GORM 替代 database/sql gorm.DB 替换 sql.DBDBMiddleware 中的 db 现在使用 *gorm.DB所有的数据库操作通过 GORM 进行。 ExecuteQuery 方法 使用 GORM 的 Raw 方法执行原生 SQL 查询并通过 Scan 将结果映射到传入的 model 结构体。原生 SQL 查询返回的结果可以映射到结构体切片中。var users []User
middleware.ExecuteQuery(users, SELECT * FROM users WHERE age ?, 18)ExecuteExec 方法 使用 GORM 的 Exec 方法执行修改插入、更新、删除语句。返回执行后影响的行数而不是插入 ID。 事务管理 使用 GORM 的 Begin 方法开启事务返回事务对象 *gorm.DB。tx, err : middleware.BeginTransaction()
if err ! nil {// handle error
}
// 在事务中执行操作
err tx.Create(user).Error
if err ! nil {tx.Rollback()
} else {tx.Commit()
}中间件现在可以使用 GORM 的功能简化数据库操作并支持复杂的 ORM 功能。
package middlewareimport (go_tas/dao/databaseloggorm.io/gorm
)type DBMiddleware struct {db *gorm.DB
}func NewDBMiddleware() *DBMiddleware {return DBMiddleware{db: database.GetDB(), // 使用 GORM 的 DB 实例}
}// ExecuteQuery 执行查询语句
func (mw *DBMiddleware) ExecuteQuery(model interface{}, query string, args ...interface{}) error {result : mw.db.Raw(query, args...).Scan(model) // 使用 GORM 的 Raw 方法if result.Error ! nil {return mw.logError(Failed to execute query, result.Error)}return nil
}// ExecuteExec 执行修改语句插入、更新、删除
func (mw *DBMiddleware) ExecuteExec(query string, args ...interface{}) (int64, error) {result : mw.db.Exec(query, args...) // 使用 GORM 的 Exec 方法if result.Error ! nil {return 0, mw.logError(Failed to execute exec, result.Error)}return result.RowsAffected, nil // 返回影响的行数
}// BeginTransaction 开启事务
func (mw *DBMiddleware) BeginTransaction() (*gorm.DB, error) {tx : mw.db.Begin() // 使用 GORM 的事务支持if tx.Error ! nil {return nil, mw.logError(Failed to begin transaction, tx.Error)}return tx, nil
}// logError 记录错误信息
func (mw *DBMiddleware) logError(message string, err error) error {log.Printf(%s: %v, message, err)return err
}3.3 改造userDao.go
使用原生 SQL 改造为使用 GORM可以直接利用 GORM 的模型操作方法来简化数据库操作。GORM 允许直接对结构体进行增删改查而不需要手动编写 SQL 查询
package daoimport (go_tas/entitygorm.io/gormlog
)type UserDAO struct {db *gorm.DB
}// NewUserDAO 创建新的 UserDAO 实例
func NewUserDAO(db *gorm.DB) *UserDAO {return UserDAO{db: db}
}// GetAllUsers 获取所有用户
func (dao *UserDAO) GetAllUsers() ([]entity.User, error) {var users []entity.User// 使用 GORM 查询所有用户if err : dao.db.Find(users).Error; err ! nil {log.Printf(Failed to get users: %v, err)return nil, err}return users, nil
}// CreateUser 创建新用户
func (dao *UserDAO) CreateUser(user *entity.User) (int64, error) {// 使用 GORM 创建用户if err : dao.db.Create(user).Error; err ! nil {log.Printf(Failed to create user: %v, err)return 0, err}// 返回创建的用户 IDreturn int64(user.ID), nil
}3.4 改造userService.go
使用 GORM 替代原生 SQL 操作来进行数据处理。可以将事务处理和数据库操作使用 GORM 的特性进行简化和改造
package serviceimport (encoding/jsongo_tas/daogo_tas/entitygo_tas/utilsgorm.io/gormnet/http
)// GetUsers 获取所有用户
func GetUsers(w http.ResponseWriter, r *http.Request, db *gorm.DB) {userDAO : dao.NewUserDAO(db)users, err : userDAO.GetAllUsers()if err ! nil {utils.HandleError(w, http.StatusInternalServerError, Failed to retrieve users, err)return}respondWithJSON(w, http.StatusOK, users)
}// CreateUser 创建新用户
func CreateUser(w http.ResponseWriter, r *http.Request, db *gorm.DB) {var user entity.Userif err : json.NewDecoder(r.Body).Decode(user); err ! nil {utils.HandleError(w, http.StatusBadRequest, Invalid request payload, err)return}// 开始一个事务tx : db.Begin()if tx.Error ! nil {utils.HandleError(w, http.StatusInternalServerError, Failed to begin transaction, tx.Error)return}defer func() {if r : recover(); r ! nil {tx.Rollback() // 回滚事务utils.HandleError(w, http.StatusInternalServerError, Internal server error, nil)}}()// 插入用户数据userDAO : dao.NewUserDAO(tx)if _, err : userDAO.CreateUser(user); err ! nil {tx.Rollback() // 回滚事务utils.HandleError(w, http.StatusInternalServerError, Failed to create user, err)return}// 提交事务if err : tx.Commit().Error; err ! nil {utils.HandleError(w, http.StatusInternalServerError, Failed to commit transaction, err)return}respondWithJSON(w, http.StatusCreated, user)
}// respondWithJSON 处理JSON响应
func respondWithJSON(w http.ResponseWriter, status int, payload interface{}) {w.Header().Set(Content-Type, application/json)w.WriteHeader(status)if err : json.NewEncoder(w).Encode(payload); err ! nil {utils.HandleError(w, http.StatusInternalServerError, Failed to encode response data, err)}
}3.5 改造router.go
将 router 中的数据库操作从 dbMiddleware 改为使用 GORM 数据库连接并清理代码逻辑
package routerimport (github.com/gorilla/muxhttpSwagger github.com/swaggo/http-swaggergo_tas/controllergo_tas/middlewarego_tas/servicegorm.io/gormnet/http
)func InitRouter(authMiddleware *middleware.CustomAuthMiddleware, db *gorm.DB) *mux.Router {r : mux.NewRouter()// 添加身份验证中间件r.Use(authMiddleware.Middleware)// 使用 GORM 数据库连接r.HandleFunc(/users, func(w http.ResponseWriter, r *http.Request) {service.GetUsers(w, r, db)}).Methods(GET)r.HandleFunc(/users, func(w http.ResponseWriter, r *http.Request) {service.CreateUser(w, r, db)}).Methods(POST)// 设备控制接口r.HandleFunc(/tas/control, controller.Control).Methods(POST)r.HandleFunc(/tas/adminControl, controller.AdminControl).Methods(POST)// Swagger Handlerr.PathPrefix(/swagger/).Handler(httpSwagger.WrapHandler)return r
}3.6 改造main.go
main 函数中可以将原本使用的 dbMiddleware 改为直接使用 GORM 初始化的数据库连接以便与前面改造的代码保持一致
package mainimport (go_tas/configgo_tas/loggergo_tas/middlewarego_tas/routerlognet/http
)func main() {// 初始化配置config.InitConfig()log.Println(Config initialized)// 初始化数据库获取 GORM 数据库连接db : config.InitGormDatabase()if db nil {log.Fatal(Failed to initialize database)}log.Println(Database initialized with GORM)// 初始化 Redisconfig.InitRedis()log.Println(Redis initialized)// 初始化日志logger.InitLogger()// 初始化认证中间件appCode : config.AppConfig.AppCodeappSecret : config.AppConfig.AppSecretauthMiddleware : middleware.NewCustomAuthMiddleware(appCode, appSecret)// 初始化路由并传递 GORM 数据库连接r : router.InitRouter(authMiddleware, db)log.Println(authMiddleware initialized)// 启动服务器log.Println(Starting server on :8080)log.Fatal(http.ListenAndServe(:8080, r))
}四、启动 五、异常情况
如果提示Table go_data_base.users doesnt exist这种情况可以查看一下实体类原因可能是由于查询时表名不匹配造成的。GORM 默认会使用复数形式表名进行查询因此它在查找 users 表而你的实际表名是 user。
5.1解决思路
可以通过在 GORM 模型中设置表名
package entitytype User struct {ID int json:idName string json:nameEmail string json:email
}// TableName 指定表名为 user
func (User) TableName() string {return user
}六、复杂查询拓展
如果涉及多表关联查询则可使用Joins来进行SQL联表查询下面举个栗子
6.1实体类Orders.go
package entitytype Orders struct {ID int json:idUserId int json:user_idAmount int json:amountDescription string json:description
}func (Orders) TableName() string {return orders
} 6.2新增OrderInfoDTO.go
package dtotype OrderInfoDTO struct {ID int json:idName string json:nameEmail string json:emailAmount int json:amountDescription string json:description
}6.3新增GetUserWithOrders方法
在userDao.go 新增一个GetUserWithOrders方法Joins 用于执行 SQL 联表查Select(users.*, orders.amount) 指定返回的字段你可以选择返回哪些表的字段Scan(users) 将查询结果扫描到 users 切片中。
func (dao *UserDAO) GetUsersWithOrderInfo() ([]dto.OrderInfoDTO, error) {var userInfo []dto.OrderInfoDTOerr : dao.db.Table(user).Joins(LEFT JOIN orders ON orders.user_id user.id).Select(user.*, orders.*).Scan(userInfo).Errorif err ! nil {log.Printf(Failed to get users with order info: %v, err)return nil, err}return userInfo, nil
}
最后将数据库的语句
DROP TABLE IF EXISTS orders;
CREATE TABLE orders (id bigint(20) NOT NULL AUTO_INCREMENT,user_id bigint(20) NULL DEFAULT NULL COMMENT 用户ID,amount int(10) NULL DEFAULT NULL COMMENT 支付金额/分,description varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 商品描述,PRIMARY KEY (id) USING BTREE
) ENGINE InnoDB AUTO_INCREMENT 7 CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci ROW_FORMAT Dynamic;-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS user;
CREATE TABLE user (id bigint(20) NOT NULL AUTO_INCREMENT,name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,email varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,PRIMARY KEY (id) USING BTREE
) ENGINE InnoDB AUTO_INCREMENT 5 CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci ROW_FORMAT Dynamic;SET FOREIGN_KEY_CHECKS 1;
如果需要源码的同学评论区把邮箱放置一下哈