使用 SQLC 时如何使数据库连接在运行时可切换
How to make database connection switchable at runtime when using SQLC
我知道这个问题已经被问过很多次了,但我没有找到适合我的情况的答案。我正在使用 SQLC 生成查询数据库的方法。使用一开始就初始化的连接时一切正常。现在我需要在多租户环境中设置它,每个租户都有一个单独的数据库。现在我想从连接租户与数据库连接的连接映射 (map[string]*sql.DB) 开始。我的问题是关于 overriding/selecting 运行时的连接。通过一个连接,存储库被初始化为:
type Repository interface {
GetCustomerById(ctx context.Context, id int64) (Customer, error)
ListCustomers(ctx context.Context) ([]Customer, error)
}
type repoSvc struct {
*Queries
db *sql.DB
}
func NewRepository(dbconn *sql.DB) Repository {
return &repoSvc{
Queries: New(dbconn),
db: dbconn,
}
}
customerRepo := customerRepo.NewRepository(conn)
GetCustomerById 是 SQLC 生成的方法
conn 是数据库连接
如何根据参数(来自 cookie 或上下文)建立连接?
假设您使用单独的数据库,最简单的方法是维护一个 map[tenantID]Repository
,其中 tenantID
是您区分租户的方式(例如 string
或 uint
包含租户 ID)。
这样你就可以在运行时做任何事情:
- 当您需要添加租户时,只需为该租户实例化
Repository
并将其添加到地图中
- 当您需要删除租户时,只需从映射中删除其
Repository
并关闭数据库连接
- 当需要查询某个租户时,在map中查找对应的
Repository
,并使用它来对该租户进行查询
如果上述操作可能同时发生,请确保您在访问地图时使用某种同步机制来避免数据竞争(例如 sync.Map
,或 sync.RWMutex
)。
如果你有一个数据库table存储租户和他们的数据库连接URI,你仍然可以使用这种方法:当你需要执行查询检查Repository
是否存在于map
:如果缺少,查询租户 table 并将该租户的 Repository
添加到地图。然后您可以定期扫描 map
并删除任何一段时间未使用的 Repository
。
为了使所有这一切更容易,您还可以将整个机器包装到一个 MultitenantRepository
接口中,它与 Repository
接口相同,但它接受一个额外的 tenantID
参数每种方法:
type MultitenantRepository interface {
GetCustomerById(ctx context.Context, tenant tenantID, id int64) (Customer, error)
ListCustomers(ctx context.Context, tenant tenantID) ([]Customer, error)
}
这将避免将多租户设置的所有复杂性暴露给您的业务逻辑。
我知道这个问题已经被问过很多次了,但我没有找到适合我的情况的答案。我正在使用 SQLC 生成查询数据库的方法。使用一开始就初始化的连接时一切正常。现在我需要在多租户环境中设置它,每个租户都有一个单独的数据库。现在我想从连接租户与数据库连接的连接映射 (map[string]*sql.DB) 开始。我的问题是关于 overriding/selecting 运行时的连接。通过一个连接,存储库被初始化为:
type Repository interface {
GetCustomerById(ctx context.Context, id int64) (Customer, error)
ListCustomers(ctx context.Context) ([]Customer, error)
}
type repoSvc struct {
*Queries
db *sql.DB
}
func NewRepository(dbconn *sql.DB) Repository {
return &repoSvc{
Queries: New(dbconn),
db: dbconn,
}
}
customerRepo := customerRepo.NewRepository(conn)
GetCustomerById 是 SQLC 生成的方法 conn 是数据库连接
如何根据参数(来自 cookie 或上下文)建立连接?
假设您使用单独的数据库,最简单的方法是维护一个 map[tenantID]Repository
,其中 tenantID
是您区分租户的方式(例如 string
或 uint
包含租户 ID)。
这样你就可以在运行时做任何事情:
- 当您需要添加租户时,只需为该租户实例化
Repository
并将其添加到地图中 - 当您需要删除租户时,只需从映射中删除其
Repository
并关闭数据库连接 - 当需要查询某个租户时,在map中查找对应的
Repository
,并使用它来对该租户进行查询
如果上述操作可能同时发生,请确保您在访问地图时使用某种同步机制来避免数据竞争(例如 sync.Map
,或 sync.RWMutex
)。
如果你有一个数据库table存储租户和他们的数据库连接URI,你仍然可以使用这种方法:当你需要执行查询检查Repository
是否存在于map
:如果缺少,查询租户 table 并将该租户的 Repository
添加到地图。然后您可以定期扫描 map
并删除任何一段时间未使用的 Repository
。
为了使所有这一切更容易,您还可以将整个机器包装到一个 MultitenantRepository
接口中,它与 Repository
接口相同,但它接受一个额外的 tenantID
参数每种方法:
type MultitenantRepository interface {
GetCustomerById(ctx context.Context, tenant tenantID, id int64) (Customer, error)
ListCustomers(ctx context.Context, tenant tenantID) ([]Customer, error)
}
这将避免将多租户设置的所有复杂性暴露给您的业务逻辑。