在Golang中将MySQL连接作为中间件用于其他包
在Golang中将MySQL连接作为中间件用于其他包 我希望只使用一次MySQL连接,并在其他函数或包中复用。在这种情况下,将连接导出,以便在其他函数或端点中作为中间件使用。
你可以在这里查看代码:https://github.com/illud/go-api-mysql

遇到了这个错误 语法错误:顶级声明后出现意外的 (
很抱歉犯了错误。我已经修正了代码。谢谢。
尝试从其他函数中使用MySQL连接,但我想我已经解决了这个问题,非常感谢。
我今晚回家后会检查一下,然后告诉你它是否有效,谢谢
感谢各位。
能否请您更详细地告诉我们您想要做什么?在您的示例代码中,当您在某处调用 Conn 时,您将无法通过第 26 行使用该连接。
我修复了这个问题,原来是这样的 var Mdb MustConnectDB(),然后我添加了等号,变成了 var Mdb = MustConnectDB(),这样它就正常工作了。如果你能编辑你的答案,对其他人会很有帮助。
var Mdb MustConnectDB()
var Mdb = MustConnectDB()
另一种在应用程序中管理数据库的解决方案,也是我在所有项目中都使用的,是一个上下文包装函数:
withDBContext(f func(db *DB) error {
db.open()
defer db.Close() // 不建议每次都关闭和打开数据库连接。
f(db)
})
withDBContext(func(db *Db) error){
//运行你的查询
})
第26行的 defer 指令会在 Conn 函数终止时关闭数据库连接。这个函数的另一个问题是 db 变量没有被返回。由于你需要 db 变量来执行 SQL 查询,你将无法在 Conn 函数外部执行任何操作。
最简单直接的解决方案是创建一个全局变量 db,用于保存 sql.Open 返回的 db 值。
在你的 db 包中实现以下代码:
// Mdb 是 MySQL 数据库的全局变量
var Mdb = MustConnectDB()
// MustConnectDB 返回一个指向 MySQL 数据库的指针,如果出错则 panic。
func MustConnectDB() *sql.DB {
db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/tasks")
if err != nil {
fmt.Println("ERROR:", err)
panic(err)
}
return db
}
在文件 taksRoute.go 中(我猜它应该命名为 taskRoute.go),我简单地添加了 db 包的导入语句。我还移除了 sql.Open 指令,并更改了在其上调用 Query 和 Prepare 方法的变量名。这是修改后的文件内容:
import (
...
db "../db"
...
)
func NewTask(c *gin.Context) {
currentTime := time.Now()
//var task []models.Task
var usersBody models.Task
c.BindJSON(&usersBody)
// 执行 db.Query 插入操作
insert, err := db.Mdb.Prepare("INSERT INTO task(title, description, date) VALUES(?, ?, ?)")
// 如果插入时出错,处理它
if err != nil {
panic(err.Error())
} else {
insert.Exec(usersBody.TITLE, usersBody.DESCRIPTION, currentTime.Format("2006-01-02 15:04:05"))
c.JSON(200, gin.H{
"response": "Inserted",
})
}
// 如果你在使用事务,请谨慎使用 defer 来关闭查询
defer insert.Close()
}
func GetTasks(c *gin.Context) {
// 执行查询
results, err := db.Mdb.Query("SELECT * FROM task ORDER BY id DESC")
if err != nil {
panic(err.Error()) // 在你的应用中应使用适当的错误处理,而不是 panic
}
var task models.Task
var tasks []models.Task
for results.Next() {
// 对于每一行,将结果扫描到我们的 tag 复合对象中
err = results.Scan(&task.ID, &task.TITLE, &task.DESCRIPTION, &task.DATE)
if err != nil {
panic(err.Error()) // 在你的应用中应使用适当的错误处理,而不是 panic
}
// 然后打印出 tag 的 Name 属性
//log.Println(task.ID, task.NAME)
tasks = append(tasks, models.Task{task.ID, task.TITLE, task.DESCRIPTION, task.DATE})
}
c.JSON(200, gin.H{
"response": tasks,
})
}
注意:我没有测试这段代码。我假设它是正确的。
在Golang中,将MySQL连接作为共享资源供多个包使用的最佳实践是通过依赖注入的方式。以下是实现方案:
1. 创建数据库连接池
// database/database.go
package database
import (
"database/sql"
"fmt"
"log"
"sync"
_ "github.com/go-sql-driver/mysql"
)
var (
db *sql.DB
once sync.Once
)
// InitDB 初始化数据库连接池
func InitDB(dataSourceName string) error {
var err error
once.Do(func() {
db, err = sql.Open("mysql", dataSourceName)
if err != nil {
return
}
// 设置连接池参数
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(5 * time.Minute)
// 测试连接
if err = db.Ping(); err != nil {
db.Close()
return
}
log.Println("Database connection established")
})
return err
}
// GetDB 获取数据库连接实例
func GetDB() *sql.DB {
return db
}
// CloseDB 关闭数据库连接
func CloseDB() error {
if db != nil {
return db.Close()
}
return nil
}
2. 创建中间件处理程序
// middleware/db_middleware.go
package middleware
import (
"context"
"database/sql"
"net/http"
"yourproject/database"
)
type contextKey string
const dbContextKey contextKey = "db"
// DBMiddleware 将数据库连接注入到请求上下文中
func DBMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
db := database.GetDB()
ctx := context.WithValue(r.Context(), dbContextKey, db)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// GetDBFromContext 从上下文中获取数据库连接
func GetDBFromContext(ctx context.Context) *sql.DB {
if db, ok := ctx.Value(dbContextKey).(*sql.DB); ok {
return db
}
return nil
}
3. 在HTTP处理器中使用
// handlers/user_handler.go
package handlers
import (
"database/sql"
"encoding/json"
"net/http"
"yourproject/middleware"
)
type UserHandler struct {
db *sql.DB
}
// NewUserHandler 构造函数,支持依赖注入
func NewUserHandler(db *sql.DB) *UserHandler {
return &UserHandler{db: db}
}
// GetUsers 使用注入的数据库连接
func (h *UserHandler) GetUsers(w http.ResponseWriter, r *http.Request) {
rows, err := h.db.Query("SELECT id, name, email FROM users")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer rows.Close()
// 处理查询结果...
}
// GetUsersFromContext 从上下文中获取数据库连接
func GetUsersFromContext(w http.ResponseWriter, r *http.Request) {
db := middleware.GetDBFromContext(r.Context())
if db == nil {
http.Error(w, "Database connection not found", http.StatusInternalServerError)
return
}
rows, err := db.Query("SELECT id, name, email FROM users")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer rows.Close()
// 处理查询结果...
}
4. 主程序配置
// main.go
package main
import (
"log"
"net/http"
"yourproject/database"
"yourproject/handlers"
"yourproject/middleware"
)
func main() {
// 初始化数据库连接
err := database.InitDB("user:password@tcp(localhost:3306)/dbname?parseTime=true")
if err != nil {
log.Fatal("Failed to connect to database:", err)
}
defer database.CloseDB()
// 创建处理器实例
db := database.GetDB()
userHandler := handlers.NewUserHandler(db)
// 设置路由
mux := http.NewServeMux()
// 使用依赖注入的方式
mux.HandleFunc("/users", userHandler.GetUsers)
// 使用中间件的方式
mux.Handle("/users-context", middleware.DBMiddleware(
http.HandlerFunc(handlers.GetUsersFromContext),
))
// 启动服务器
log.Println("Server starting on :8080")
if err := http.ListenAndServe(":8080", mux); err != nil {
log.Fatal("Server failed:", err)
}
}
5. 仓库模式示例
// repositories/user_repository.go
package repositories
import (
"database/sql"
"yourproject/models"
)
type UserRepository struct {
db *sql.DB
}
func NewUserRepository(db *sql.DB) *UserRepository {
return &UserRepository{db: db}
}
func (r *UserRepository) FindAll() ([]models.User, error) {
rows, err := r.db.Query("SELECT id, name, email FROM users")
if err != nil {
return nil, err
}
defer rows.Close()
var users []models.User
for rows.Next() {
var user models.User
if err := rows.Scan(&user.ID, &user.Name, &user.Email); err != nil {
return nil, err
}
users = append(users, user)
}
return users, nil
}
func (r *UserRepository) FindByID(id int) (*models.User, error) {
var user models.User
err := r.db.QueryRow("SELECT id, name, email FROM users WHERE id = ?", id).
Scan(&user.ID, &user.Name, &user.Email)
if err != nil {
return nil, err
}
return &user, nil
}
这个实现方案提供了两种方式:
- 依赖注入:通过构造函数将数据库连接传递给处理器或仓库
- 中间件模式:通过请求上下文传递数据库连接
两种方式都能确保整个应用程序共享同一个数据库连接池,避免了重复创建连接的开销。sync.Once 保证了连接只初始化一次,连接池参数可以根据实际需求调整。

