2014-09-06 27 views
1

我想分离与主线程的Db交互。连接将子类QRunnable并开始在run()函数中打开连接,连接管理器将保存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; 



} 

回答

2

你正在做一些不正确的事情。首先,如果你想同时连接多个数据库连接,你需要给它们唯一的名字。

从文档:

警告:如果您添加具有相同名称的现有连接的连接,新连接取代旧的。如果您在不指定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; 
} 

我相当肯定你会看到程序没有做太多。该程序通过创建数据库连接开始执行,然后创建一条文本消息,然后退出。

这正是你的辅助线程所做的。您必须像QRunnable::run()QThread::run()一样对待您的入口点功能main()。只要函数退出,线程就可以被视为被销毁。 (脚注:与QRunnableQThreadPool这不完全是实际发生的事情,它可以想象成如此)。

如果您希望线程保持活动状态,则需要保持run()函数不会退出。有很多方法可以做到这一点:你可以使用forever循环,while循环,或者,如果你想像你在主线程中那样处理线程中的信号和插槽,你可以使用事件循环。

MyRunnable::run() 
{ 
    QEventLoop loop; 
    // ... 
    loop.exec(); 
} 

您通过连接到QEventLoop::quit()插槽退出事件循环。 (供参考:这是QCoreApplication::exec()函数内部发生了什么)


第三,@JKSH注意,你不应该跨线程边界使用SQL类。

这意味着你应该重新设计你的班级,以便你没有DbConnection班,而是DBQuery班。它的接口应该允许您传递需要执行的SQL查询,然后生成结果。 QSqlDatabaseQSqlQuery的实例应该保持私有和内部的,并且只能在run()函数内部创建,或者从run()中调用一个函数,以确保它们在工作线程中。

一个移动SQL查询和结果线程之间的方法是使用多重继承与QObject

+0

谢谢指出:)非常丰富的:)目前,我实现了一个类似于你的'DBQuery'的'Worker'类子类'QThread'。完成后我会再次反馈:) – Tiana987642 2014-09-07 17:10:59

0

的官方文档(http://qt-project.org/doc/qt-5/threads-modules.html)说,这大约Qt SQL:“连接只能在创建它的线程中使用。”您必须打开数据库连接并在同一个线程中执行所有查询。

通常,QThreadPool将在每次运行QRunnable时使用不同的线程。这与Qt中的数据库访问不兼容。

改为使用QThread,以确保您只使用1个线程。这里是它的文档:http://qt-project.org/doc/qt-5/qthread.html

+0

好吧,我会尽力实现'QThread'。但是我认为当调用'DBConnManager :: start(DbConnection *)'时,它会驻留在另一个线程中,并且它调用的所有其他函数也会在那里生存呢?所以这个错误应该不会发生:( – Tiana987642 2014-09-07 02:16:08