在Golang中将MySQL连接作为中间件用于其他包

在Golang中将MySQL连接作为中间件用于其他包 我希望只使用一次MySQL连接,并在其他函数或包中复用。在这种情况下,将连接导出,以便在其他函数或端点中作为中间件使用。

你可以在这里查看代码:https://github.com/illud/go-api-mysql

image

11 回复

我回家后会检查一下。谢谢。

更多关于在Golang中将MySQL连接作为中间件用于其他包的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


遇到了这个错误 语法错误:顶级声明后出现意外的 (

很抱歉犯了错误。我已经修正了代码。谢谢。

尝试从其他函数中使用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 指令,并更改了在其上调用 QueryPrepare 方法的变量名。这是修改后的文件内容:

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
}

这个实现方案提供了两种方式:

  1. 依赖注入:通过构造函数将数据库连接传递给处理器或仓库
  2. 中间件模式:通过请求上下文传递数据库连接

两种方式都能确保整个应用程序共享同一个数据库连接池,避免了重复创建连接的开销。sync.Once 保证了连接只初始化一次,连接池参数可以根据实际需求调整。

回到顶部