Golang中使用MongoDB驱动的最佳实践与建议

Golang中使用MongoDB驱动的最佳实践与建议 你好,

我正在开发一个用于追踪药物、剂量、处方、续药日期等信息的应用程序。我使用了MongoDB,并尝试使用MongoDB Go驱动程序。

我遇到的问题是,我在网上能找到的所有使用此驱动程序的示例都在main函数中完成所有操作,而我的应用程序需要在Web服务器的处理函数中管理数据。我很难理解,当连接在main函数中打开时,如何保持其开放状态,以便我后续可以添加数据、搜索、更新或删除。我宁愿不创建全局变量,但如果确实没有其他选择,我也会这么做。目前,我通过每次需要引用数据库时都打开和关闭连接来使其工作。对于少量数据,这在开销方面问题不大,但随着数据录入需求的增加,这将成为一个问题。有人知道管理这个问题的最佳方法吗?或者能指点我找到展示如何做到这一点的资源吗?

非常感谢任何关于此事的建议,因为我对使用Mongo还比较陌生。


更多关于Golang中使用MongoDB驱动的最佳实践与建议的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

嗨,

非常感谢,我不确定为什么我完全没想到这一点。非常感谢。

更多关于Golang中使用MongoDB驱动的最佳实践与建议的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我有一个 connectdb 函数,它只返回主会话,并将其附加到全局变量 Session 上。这个函数只在 main 函数中调用一次。

然后,在数据访问层(DAL)或处理程序(handlers)中,你只需使用 Session.Copy().DB(dbName),并在每个使用的地方都记得关闭它。

在Go中管理MongoDB连接的最佳实践是使用连接池,并通过依赖注入将数据库实例传递给处理函数。以下是一个完整的示例:

package main

import (
    "context"
    "log"
    "net/http"
    "time"

    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
    "go.mongodb.org/mongo-driver/bson"
)

// 定义应用结构体,包含数据库连接
type App struct {
    DB *mongo.Database
}

// 初始化MongoDB连接
func NewApp() (*App, error) {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    // 配置连接池
    clientOptions := options.Client().
        ApplyURI("mongodb://localhost:27017").
        SetMaxPoolSize(100).                    // 最大连接数
        SetMinPoolSize(10).                     // 最小连接数
        SetMaxConnIdleTime(5 * time.Minute)     // 连接最大空闲时间

    client, err := mongo.Connect(ctx, clientOptions)
    if err != nil {
        return nil, err
    }

    // 测试连接
    err = client.Ping(ctx, nil)
    if err != nil {
        return nil, err
    }

    db := client.Database("medication_tracker")
    return &App{DB: db}, nil
}

// 药物模型
type Medication struct {
    ID           string    `bson:"_id,omitempty"`
    Name         string    `bson:"name"`
    Dosage       string    `bson:"dosage"`
    Prescription string    `bson:"prescription"`
    RefillDate   time.Time `bson:"refill_date"`
    CreatedAt    time.Time `bson:"created_at"`
}

// 添加药物的处理函数
func (app *App) addMedicationHandler(w http.ResponseWriter, r *http.Request) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    collection := app.DB.Collection("medications")
    
    med := Medication{
        Name:         "Aspirin",
        Dosage:       "100mg",
        Prescription: "Dr. Smith",
        RefillDate:   time.Now().AddDate(0, 1, 0),
        CreatedAt:    time.Now(),
    }

    result, err := collection.InsertOne(ctx, med)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    log.Printf("Inserted medication with ID: %v", result.InsertedID)
    w.WriteHeader(http.StatusCreated)
}

// 搜索药物的处理函数
func (app *App) searchMedicationsHandler(w http.ResponseWriter, r *http.Request) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    collection := app.DB.Collection("medications")
    
    // 示例:搜索特定名称的药物
    filter := bson.M{"name": "Aspirin"}
    cursor, err := collection.Find(ctx, filter)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    defer cursor.Close(ctx)

    var medications []Medication
    if err = cursor.All(ctx, &medications); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    log.Printf("Found %d medications", len(medications))
    w.WriteHeader(http.StatusOK)
}

// 更新续药日期的处理函数
func (app *App) updateRefillDateHandler(w http.ResponseWriter, r *http.Request) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    collection := app.DB.Collection("medications")
    
    // 示例:更新特定药物的续药日期
    filter := bson.M{"name": "Aspirin"}
    update := bson.M{
        "$set": bson.M{
            "refill_date": time.Now().AddDate(0, 2, 0),
            "updated_at":  time.Now(),
        },
    }

    result, err := collection.UpdateOne(ctx, filter, update)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    log.Printf("Updated %d medications", result.ModifiedCount)
    w.WriteHeader(http.StatusOK)
}

func main() {
    // 初始化应用
    app, err := NewApp()
    if err != nil {
        log.Fatal("Failed to connect to MongoDB:", err)
    }
    defer func() {
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
        defer cancel()
        if err := app.DB.Client().Disconnect(ctx); err != nil {
            log.Fatal("Failed to disconnect from MongoDB:", err)
        }
    }()

    // 注册路由
    http.HandleFunc("/medications/add", app.addMedicationHandler)
    http.HandleFunc("/medications/search", app.searchMedicationsHandler)
    http.HandleFunc("/medications/update", app.updateRefillDateHandler)

    log.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

对于更复杂的应用,可以使用依赖注入容器:

// 使用依赖注入的仓库模式
type MedicationRepository struct {
    Collection *mongo.Collection
}

func NewMedicationRepository(db *mongo.Database) *MedicationRepository {
    return &MedicationRepository{
        Collection: db.Collection("medications"),
    }
}

func (r *MedicationRepository) Create(ctx context.Context, med *Medication) error {
    med.CreatedAt = time.Now()
    _, err := r.Collection.InsertOne(ctx, med)
    return err
}

func (r *MedicationRepository) FindByName(ctx context.Context, name string) ([]Medication, error) {
    filter := bson.M{"name": name}
    cursor, err := r.Collection.Find(ctx, filter)
    if err != nil {
        return nil, err
    }
    defer cursor.Close(ctx)

    var medications []Medication
    err = cursor.All(ctx, &medications)
    return medications, err
}

// 在App结构体中包含仓库
type App struct {
    DB                *mongo.Database
    MedicationRepo    *MedicationRepository
}

func NewApp() (*App, error) {
    // ... 连接数据库代码同上
    
    app := &App{
        DB: db,
        MedicationRepo: NewMedicationRepository(db),
    }
    return app, nil
}

// 使用仓库的处理函数
func (app *App) addMedicationHandler(w http.ResponseWriter, r *http.Request) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    med := &Medication{
        Name:         "Aspirin",
        Dosage:       "100mg",
        Prescription: "Dr. Smith",
        RefillDate:   time.Now().AddDate(0, 1, 0),
    }

    if err := app.MedicationRepo.Create(ctx, med); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    w.WriteHeader(http.StatusCreated)
}

这个实现避免了全局变量,通过依赖注入传递数据库连接,使用连接池管理连接,并在应用生命周期内保持连接开放。

回到顶部