我目前正在运行 Go API,其中有存储库,允许我从 MySQL 数据库获取数据。
但是我想实现相同的存储库来从 MongoDB 数据库获取相同的数据,这对我来说目前很困难。
这是我的代码:
// My vehicle repository file
// Repository handling Vehicle
type VehicleRepository struct {
conn *sqlx.DB
}
func (r *VehicleRepository) GetVehicles(ctx context.Context, brand string) (vehicle.Vehicle, error) {
var vehicles []*vehicle.Vehicle
param := map[string]interface{}{"brand": brand}
query := "SELECT * FROM vehicles WHERE brand = :brand"
rows, err := r.conn.NamedQueryContext(ctx, query, param)
if err != nil {
return nil, errors.Wrap(err, "query execution")
}
return vehicles, nil
}
// and in another file I create a repository object with all my repository (here only "Vehicle" but in reality I have plenty)
type Repositories struct {
Vehicle *VehicleRepository
}
func CreateRepositories(conn c.Connections) *Repositories {
return &Repositories{
Vehicles: &VehicleRepository{conn, l},
}
}
这段代码运行良好,但它一次只能处理每种情况(无论是在 MySQL 模式还是在 MongoDB 模式下),但我现在需要处理 MySQL 和 MongoDB,并根据我的 API 配置使用 MongoDB 存储库即时更改。
我尝试了多种方法:
我尝试重命名我的
vehicle.go
存储库 vehicle.mysql.go
并创建一个 vehicle.mongodb.go
实现 mongodb 存储库。我将 VehicleRepository
结构重命名为 VehicleMySQLRepository
并将 VehicleMongoDBRepository
创建为 vehicle.mongodb.go
,但是当我尝试将其链接到我的 Repositories
中的 CreateRepositories
时,它中断了:
type Repositories struct {
// Here I have `VehicleRepository` but it didn't exist anymore, and I can't set Vehicle to `VehicleMySQLRepository | VehicleMongoDBRepository`
Vehicle *VehicleRepository
}
func CreateRepositories(conn c.Connections, condition bool) *Repositories {
if condition == true {
return &Repositories{
// Here I could do this:
// Vehicles: &VehicleMySQLRepository{conn.Mysql, l},
// Vehicles: &VehicleMongoDBRepository{conn.Mongo, l},
// but it won't work because `Vehicles` will be declared multiple time, and I can't
// have multiple `Vehicles` like `VehiclesMongo` & `VehiclesMysql`, etc...
// because it will break all my calls on `Vehicles` in +50 files, and in reality
// I've got over 30 repositories, if I need to duplicate each ones of them it won't do...
Vehicles: &VehicleRepository{conn.Mongo, l},
}
}
return &Repositories{
Vehicles: &VehicleRepository{conn.Mysql, l},
}
}
我想到了我可以创建
vehicle.mysql.go
和 vehicle.mongodb.go
,实现它们各自的存储库,并且仅使用 go build -tags mongo
和 co 来编译它,它可以工作,但我需要同时处理这两个存储库,在我的前端用户可以选择使用 MySQL 还是 MongoDB 数据库,所以我无法每次都用良好的实现重新编译我的后端。
我还尝试不更改存储库,并在函数本身中处理 MySQL 和 MongoDB 连接,就像在
GetVehicles()
方法中一样,query
字符串有条件地设置为 MySQL 查询或 MongoDB 查询,它可以工作,但实现太恶心了,我真的很想让选项 1) 起作用。
我不知道我的问题是否足够清楚,如果需要更多信息,请随时在评论中索取。
为了处理您的情况,您可以在 Go 中使用多态性和接口概念。
type VehicleRepository interface {
GetVehicles(ctx context.Context, brand string) ([]*vehicle.Vehicle, error)
}
type MySQLVehicleRepository struct {
conn *sqlx.DB
}
func (r *MySQLVehicleRepository) GetVehicles(ctx context.Context, brand string) ([]*vehicle.Vehicle, error) {
// Logic for retrieving data from MySQL
}
type MongoDBVehicleRepository struct {
session *mgo.Session
}
func (r *MongoDBVehicleRepository) GetVehicles(ctx context.Context, brand string) ([]*vehicle.Vehicle, error) {
// Logic to fetch data from MongoDB
}
func CreateVehicleRepository(conn c.Connections, condition bool) VehicleRepository {
if condition {
return &MongoDBVehicleRepository{conn.Mongo}
}
return &MySQLVehicleRepository{conn.Mysql}
}
通过这种方法,您可以根据 API 配置创建 MySQL 或 MongoDB 存储库。您可以使用
GetVehicles
接口中的 VehicleRepository
方法,而不必担心底层实现。
这种设计有助于保持存储库逻辑干净并与特定数据库实现分开。这也使得将来在不同数据库后端之间切换更加容易。