2014-09-06 63 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