QxOrm MongoDB多线程多数据库适配问题及方案咨询
问题描述
根据QxOrm手册3.2和3.22章节,理论上可在每个线程中单独调用以下代码实现多线程操作多数据库:
qx::QxSqlDatabase::getSingleton()->setDatabaseName("./test_qxorm.db"); qx::QxSqlDatabase::getSingleton()->setHostName("localhost"); qx::QxSqlDatabase::getSingleton()->setUserName("root"); qx::QxSqlDatabase::getSingleton()->setPassword("");
但实际使用MongoDB驱动(QXMONGODB)时该方式无效,且qx::dao::call_query的实现与其他SQL驱动不同。
核心疑问
- 使用QXMONGODB驱动是否有专属实现方式,还是QxOrm未支持该功能?
- 若未支持,如何自定义连接池与QxOrm适配?MongoCXX已具备多数据库连接能力。
- 能否在不大量修改源码的前提下,让QxOrm与MongoCXX/MongoC共享连接?
测试现象
通过Qt创建两个db_interactor实例并分别移入不同线程,对应不同配置文件(连接不同MongoDB数据库),测试发现:
qx::QxSqlDatabase::getSingleton()->getDatabase()返回错误和空数据库;- 初始化
di1后查询可正常写入对应数据库,但初始化di2后,q1和q2的查询均写入di2配置的数据库,配置被覆盖。
解决方案与说明
1. QXMONGODB驱动的多库支持现状
QxOrm的qx::QxSqlDatabase单例模式是为SQL驱动设计的,QXMONGODB驱动并未针对多线程多数据库场景做适配——它没有将连接与线程绑定,也未为不同线程维护独立的数据库配置,导致后续线程的配置会覆盖全局单例配置,这就是测试中出现配置被覆盖的原因。目前官方并未提供QXMONGODB驱动的专属多库操作方式,该场景属于未支持状态。
2. 自定义连接池适配QxOrm的思路
利用MongoCXX自带的连接池能力,绕过QxOrm全局单例的数据库管理,实现线程独立的连接管理:
- 用线程局部存储(TLS)维护每个线程的MongoCXX连接池实例,每个线程对应目标数据库;
- 重写QxOrm中MongoDB驱动的
qx::dao::call_query相关逻辑,执行查询时从当前线程的连接池获取连接,而非全局单例; - 封装自定义DAO工具类,对外保持类似QxOrm的调用方式,内部处理线程连接的获取与释放。
示例代码片段:
// 线程局部存储的MongoCXX连接池 thread_local std::unique_ptr<mongocxx::pool> g_mongo_pool; // 初始化当前线程的MongoDB连接池 void init_thread_mongo_pool(const std::string& uri) { mongocxx::instance instance{}; auto client_uri = mongocxx::uri{uri}; g_mongo_pool = std::make_unique<mongocxx::pool>(client_uri); } // 自定义查询执行函数,替代qx::dao::call_query template<typename T> void custom_call_query(const T& query) { auto client = g_mongo_pool->acquire(); // 基于client执行MongoDB操作,适配QxOrm的查询结构 // ... }
3. 不修改QxOrm源码的共享连接方案
通过扩展QxOrm的数据库适配器实现,无需大量修改源码:
- 继承
qx::QxSqlDatabase类,重写getDatabase()等核心方法,使其返回当前线程对应的MongoCXX连接; - 调用QxOrm的
qx::QxSqlDatabase::registerCustomDatabase()方法,将自定义适配器注册为默认的MongoDB驱动适配器; - 每个线程初始化时,为自定义适配器设置当前线程的MongoCXX连接或连接池,QxOrm的DAO操作会自动使用线程对应的连接。
示例代码片段:
class CustomMongoDb : public qx::QxSqlDatabase { public: static std::shared_ptr<CustomMongoDb> getInstance() { static thread_local std::shared_ptr<CustomMongoDb> instance = std::make_shared<CustomMongoDb>(); return instance; } void setMongoClient(std::shared_ptr<mongocxx::client> client) { m_client = client; } mongocxx::client* getDatabase() override { return m_client.get(); } private: std::shared_ptr<mongocxx::client> m_client; }; // 在主线程注册自定义适配器 qx::QxSqlDatabase::registerCustomDatabase("QXMONGODB", [](){ return CustomMongoDb::getInstance(); }); // 在工作线程初始化 void worker_thread_func(const std::string& db_uri) { mongocxx::instance instance{}; auto client = std::make_shared<mongocxx::client>(mongocxx::uri{db_uri}); CustomMongoDb::getInstance()->setMongoClient(client); // 后续QxOrm的DAO操作会使用当前线程的client }
内容的提问来源于stack exchange,提问作者Ulterno
相关产品推荐
相关产品推荐

