在三层架构中实现线程:无法打开数据库
Implement thread in Three-tier architectures: can't open database
我想分离Db与主线程的交互。连接将在run()
函数中对QRunnable
进行子类并开始打开连接,连接管理器将保存QthreadPool
并在需要查询时启动任务。
但问题是它无法打开数据库,如果我在一个简单的main()
中使用相同的代码,它会很好地工作。所以我不知道?
任何想法都值得赞赏:)
这是我的工具:
#include <Qt/QtSql>
#include <QRunnable>
class DbConnection : public QRunnable
{
private:
QSqlDatabase db;
bool isConnectToDB;
public:
DbConnection();
QSqlDatabase getDb() const;
void setDb(const QSqlDatabase &value);
bool getIsConnectToDB() const;
void setIsConnectToDB(bool value);
void run();
void openConnToDB();
};
QSqlDatabase DbConnection::getDb() const
{
return db;
}
void DbConnection::setDb(const QSqlDatabase &value)
{
db = value;
}
bool DbConnection::getIsConnectToDB() const
{
return isConnectToDB;
}
void DbConnection::setIsConnectToDB(bool value)
{
isConnectToDB = value;
}
void DbConnection::run()
{
openConnToDB();
qDebug()<< "Open a connection from thread" << QThread::currentThread();
}
void DbConnection::openConnToDB() //=> work well in a simple test program
{
db = QSqlDatabase::addDatabase("QPSQL");
db.setHostName("localhost");
db.setDatabaseName("test");
db.setUserName("postgres");
db.setPassword("1");
db.setPort(5432);
isConnectToDB = db.open("postgres","1");;
//usleep(100000);
}
DbConnection::DbConnection()
{
}
class DBConnManager
{
private:
DBConnManager();
static DBConnManager *m_Instance;
QThreadPool *threadPool;
QList<DbConnection *> connList;
DbConnection* conn;
public:
static DBConnManager *getInstance();
QList<DbConnection *> getConnList() const;
void setConnList(const QList<DbConnection *> &value);
QSqlDatabase acquireDb();
DbConnection *getConn() const;
void setConn(DbConnection *value);
void closeDb();
};
DBConnManager *DBConnManager::m_Instance = 0;
DBConnManager::DBConnManager()
{
threadPool = QThreadPool::globalInstance();
}
DbConnection *DBConnManager::getConn() const
{
return conn;
}
void DBConnManager::setConn(DbConnection *value)
{
conn = value;
}
void DBConnManager::closeDb()
{
if (conn==NULL) {
qDebug()<< "NULL connection pointer";
return;
}
conn->getDb().close();
}
QList<DbConnection *> DBConnManager::getConnList() const
{
return connList;
}
void DBConnManager::setConnList(const QList<DbConnection *> &value)
{
connList = value;
}
QSqlDatabase DBConnManager::acquireDb()
{
conn = new DbConnection;
connList.append(conn);
threadPool->start(conn);
// QSqlDatabase tmp;
// return tmp;
return conn->getDb();
}
DBConnManager *DBConnManager::getInstance()
{
if (!m_Instance) {
m_Instance = new DBConnManager;
}
return m_Instance;
}
这就是一切的开始:
QList<arcEntity> arcBL::getAll()
{
QList <arcEntity> listResult;
QSqlDatabase db = DBConnManager::getInstance()->acquireDb();
bool result = m_arcDAL.getAll(&db,listResult);
if (result==false) {
qDebug()<<"Query get all fail";
}
DBConnManager::getInstance()->closeDb();
return listResult;
}
您做了很多错误的事情。
首先,如果您希望同时连接多个数据库,则需要为它们提供唯一的名称。
来自文件:
警告:如果添加与现有连接同名的连接,则新连接将替换旧连接。如果您多次调用此函数而未指定connectionName,则默认连接将是被替换的连接。
你可以选择你想要的任何名称,但我用来保证唯一性的一个简单方法是使用从对象的内存地址派生的名称,记住存储连接名称,以便以后不再需要连接时可以删除它。
然后,您可以修改openConnToDB()
函数,如下所示:
connectionName = QString("PSQL-%1").arg(reinterpret_cast<int>(this)); // store connection name
db = QSqlDatabase::addDatabase("QPSQL", connectionName);
然后你需要添加一种方法来删除连接,一旦你完成了它
void DbConnection::closeConnToDB()
{
if (db.isOpen())
db.close();
QSqlDatabase::removeDatabase(connectionName);
}
其次,您还没有完全掌握如何对多个线程进行编程。想象一下阅读以下代码:
int main(int argc, char **argv)
{
openConnToDB();
qDebug()<< "Open a connection from thread" << QThread::currentThread();
return 0;
}
我相信你会看到这个程序做得不多。该程序通过创建数据库连接开始执行,然后创建一条文本消息,然后退出。
这正是您对次要线程所做的操作。您必须像对待入口点函数main()
一样对待QRunnable::run()
或QThread::run()
。一旦函数退出,就可以认为线程被破坏了。(脚注:对于QRunnable
和QThreadPool
,这并不是所有实际目的都会发生的事情,可以想象是这样的)。
如果希望线程保持活动状态,则需要防止run()
函数退出。有很多方法可以做到这一点:您可以使用forever
循环、while
循环,或者,如果您想像在主线程中那样处理线程中的信号和槽,您可以使用事件循环。
MyRunnable::run()
{
QEventLoop loop;
// ...
loop.exec();
}
您可以通过连接到QEventLoop::quit()
插槽来退出事件循环。(仅供参考:这是QCoreApplication::exec()
函数内部发生的情况)
第三,正如@JKSH所指出的,不应该跨线程使用SQL类。
这意味着你应该重新设计你的类,这样你就没有DbConnection
类,而是一个DBQuery
类。它的接口应该允许您传递需要执行的SQL查询,然后生成结果。QSqlDatabase
和QSqlQuery
的实例应保持私有和内部,并且只能在run()
函数或从run()
调用的函数内部创建,以确保它们在工作线程中。
在线程之间移动SQL查询和结果的一种方法是使用QObject
:的多重继承
class DBQuery: public QObject, public QRunnable
{
// ...
public slots:
void enqueueSQL(QString const &sql);
signals:
void emitResults(QList<QVariant> const &records);
// ...
};
官方文档(http://qt-project.org/doc/qt-5/threads-modules.html)关于Qt SQL:"连接只能在创建它的线程内使用。"您必须打开数据库连接,并在同一线程中执行所有查询。
通常,每次运行QRunnable
时,QThreadPool
都会使用不同的线程。这与Qt中的数据库访问不兼容。
请改用QThread
,以确保只使用1个线程。以下是其文档:http://qt-project.org/doc/qt-5/qthread.html
- 从不同线程使用int64的不同字节安全吗
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 在C++中使用cURL和多线程
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 在cuda线程之间共享大量常量数据
- 如何将元素添加到数组的线程安全函数?
- 如何在多个线程中创建 QSql数据库连接时防止名称冲突
- 两个线程尝试将一些数据读/写到数据库中表的同一行中
- 如何使用QT/C 中的多线程用API访问我的数据库
- Qt:使用一个连接从多个线程写入数据库
- 使用Qt框架减少多线程应用程序中数据库连接的最佳策略
- 在三层架构中实现线程:无法打开数据库
- c++线程数据库类内存混淆
- 线程在检查数据库时抛出错误
- 在多线程环境中管理sqlite数据库
- c++中的SQLite.数据库是BUSY(多线程)
- 使用Qt将所有数据库操作放在一个特定的线程中
- 在Qt中为数据库创建线程:合理的设计或无意义
- 在并行线程中访问数据库,可行的选项
- 伯克利数据库,多线程致命错误