1

我想将 Db 交互与主线程分开。连接将子类化并开始在函数QRunnable中打开连接,连接管理器将在需要查询时保存并启动任务。run()QthreadPool

但问题是keep report无法打开数据库,如果我在一个简单的代码中使用相同的代码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;



}
4

2 回答 2

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 查询,然后应该产生结果。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);

    // ...
};
于 2014-09-07T15:42:23.123 回答
0

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

通常,QThreadPool每次运行QRunnable. 这与 Qt 中的数据库访问不兼容。

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

于 2014-09-07T00:31:16.817 回答