Golang中使用MongoDB驱动的最佳实践与建议
Golang中使用MongoDB驱动的最佳实践与建议 你好,
我正在开发一个用于追踪药物、剂量、处方、续药日期等信息的应用程序。我使用了MongoDB,并尝试使用MongoDB Go驱动程序。
我遇到的问题是,我在网上能找到的所有使用此驱动程序的示例都在main函数中完成所有操作,而我的应用程序需要在Web服务器的处理函数中管理数据。我很难理解,当连接在main函数中打开时,如何保持其开放状态,以便我后续可以添加数据、搜索、更新或删除。我宁愿不创建全局变量,但如果确实没有其他选择,我也会这么做。目前,我通过每次需要引用数据库时都打开和关闭连接来使其工作。对于少量数据,这在开销方面问题不大,但随着数据录入需求的增加,这将成为一个问题。有人知道管理这个问题的最佳方法吗?或者能指点我找到展示如何做到这一点的资源吗?
非常感谢任何关于此事的建议,因为我对使用Mongo还比较陌生。
更多关于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)
}
这个实现避免了全局变量,通过依赖注入传递数据库连接,使用连接池管理连接,并在应用生命周期内保持连接开放。


