2017-02-03 2274 views
0

我正在將.net/c#應用程序轉換爲Qt。我每天都不用Qt/C++工作,所以請耐心等待。Qt - 同時處理多個數據庫連接

該應用程序同時使用SQLite和MS SQL Server。建立連接後,它將保持開放狀態,因爲兩個數據存儲的數據交易都很高,我不想浪費時間打開和關閉事務之間的連接。

我的目標是創建一個可用於兩種數據庫類型的通用管理器類。當任何一個連接打開而另一個連接打開時,該類就像應該那樣工作。然而,當我試圖同時建立兩個連接,我收到錯誤:

QSqlDatabasePrivate :: removeDatabase:連接「qt_sql_default_connection」仍然在使用,所有的查詢將停止工作。

QSqlDatabasePrivate :: addDatabase:重複連接名稱 'qt_sql_default_connection',舊的連接刪除。

因此,我已閱讀約connection naming然而,然後,我遇到訪問數據庫的問題。我發佈了我的數據庫管理器類的內容,但沒有連接命名。我正在尋找建議,我應該如何處理這個乾淨和通用的方式。謝謝!

h文件

#ifndef DATABASEMGR_H 
#define DATABASEMGR_H 

#include <QObject> 
#include <QSqlDatabase> 
#include <QVariant> 
#include <QSqlQuery> 
#include <QSqlQueryModel> 
#include <QSqlRecord> 

class DatabaseMgr : public QObject 
{ 
public: 
    DatabaseMgr(const QString &databaseType, const QString &connectionString, QObject *parent = 0); 
    ~DatabaseMgr(); 
    QVariant ExecuteScalar(const QString &cmd); 
    bool ExecuteNonQuery(const QString &cmd); 
    bool IsOpen() const; 
    bool Connect(); 
    bool Disconnect(); 
    QSqlQueryModel *GetQueryModel(const QString &cmd); 
    QSqlQueryModel *GetQueryModel(const QString &cmd, const QMap<QString, QVariant> &params); 

private: 
    QSqlDatabase mDb; 
}; 

#endif // DATABASEMGR_H 

的.cpp

#include "databasemgr.h" 

DatabaseMgr::DatabaseMgr(const QString &databaseType, const QString &connectionString, QObject *parent) { 
    mDb = QSqlDatabase::addDatabase(databaseType); 
    mDb.setDatabaseName(connectionString); 
} 

DatabaseMgr::~DatabaseMgr() { 
    if (mDb.open()) { 
     mDb.close(); 
    } 
} 

QVariant DatabaseMgr::ExecuteScalar(const QString &cmd) { 
    QVariant mVariant; 
    if (mDb.isOpen()) { 
     QSqlQuery query; 
     if (query.exec(cmd)) { 
      while (query.next()) { 
       mVariant = query.value(0); 
      } 
     } 
    } 
    return mVariant; 
} 

bool DatabaseMgr::ExecuteNonQuery(const QString &cmd) { 
    if (mDb.isOpen()) { 
     QSqlQuery query; 
     if (query.exec(cmd)) { 
      return true; 
     } else { 
      //todo handle error 
     } 
    } else { 
     //todo handle error 
    } 
    return false; 
} 

bool DatabaseMgr::IsOpen() const { 
    return mDb.isOpen(); 
} 

bool DatabaseMgr::Connect(){ 
    if (!mDb.open()) { 
     //todo error opening database?? 
     return false; 
    } 
    return true; 
} 

bool DatabaseMgr::Disconnect() { 
    return mDb.isOpen(); 
} 

QSqlQueryModel *DatabaseMgr::GetQueryModel(const QString &cmd) { 
    QSqlQueryModel *model = new QSqlQueryModel; 

    if (mDb.isOpen()) { 
     model->setQuery(cmd, mDb); 
    } 

    return model; 
} 

QSqlQueryModel *DatabaseMgr::GetQueryModel(const QString &cmd, const QMap<QString, QVariant> &params) { 
    QSqlQueryModel *model = new QSqlQueryModel; 

    if (mDb.isOpen()) { 
     QSqlQuery query; 
     query.prepare(cmd); 

     if (params.count() > 0) { 
      QMapIterator<QString, QVariant> i(params); 
      while (i.hasNext()) { 
       i.next(); 
       query.bindValue(i.key(), i.value()); 
      } 
     } 

     model->setQuery(query); 
    } 

    return model; 
} 

實施例以下用途:

main.cpp中(SQLite的分貝用於存儲全球應用設置)

DatabaseMgr mSqliteDb("QSQLITE", app.applicationName() + ".db", &app); 
mSqliteDb.Connect(); 
Program prgm(mSqliteDb, &app); 

program.cpp

mMsSqlDb = new DatabaseMgr("QODBC3", GetMsSqlConnectionString()); 
if (mMsSqlDb->Connect()) { 
    AddToActivityLog("Connected to MS SQL DB"); 
} else { 
    AddToActivityLog("Error connecting to MS SQL DB"); 
} 

從計劃成員函數中獲取結果:

QString cmd = "SELECT DISTINCT some_things FROM the_table ORDER BY these_columns"; 

QSqlQueryModel *model = mMsSqlDb->GetQueryModel(cmd); 

if (model->rowCount() > 0) { 
    for (int i = 0; i < model->rowCount(); ++i) { 
     //Do stuff... 
    } 
} 
+0

請出示您使用這個類來創建這兩個數據庫的兩個對象。我已經用MySQL和SQLite完成了這個任務,沒有任何問題。 –

+0

查看上面發佈的例子,我在程序中創建了兩個manager類的實例,一個用於sqlite,另一個用於ms sql。 sqlite實例通過引用傳遞,並可用於應用程序的所有類。 ms sql實例是程序類中的私有成員指針。在實例化和連接之後,我隨後調用當時需要的db管理器類的任何公共函數,例如:用於更新記錄,檢索記錄等。 – DonJoe

+1

你的代碼和我的代碼之間的區別在於我用連接名稱發送; myDB = QSqlDatabase :: addDatabase(databaseType,connectionName);我認爲這可能會解決您的問題。 (每個數據庫的連接名稱不同) –

回答

2

你正在做的第一個根本的錯誤就是你的數據庫連接管理器是多餘的。您期望實現的內容已經內置於QtSql框架中。

當您創建的QSqlDatabaseQSqlDatabase::addDatabase一個新實例該對象在Qt內部,直到調用QSqlDatabase::removeDatabase被持久化。你不需要保持一個連接到它的變量。如果您需要訪問中間的連接,只需撥打QSqlDatabase::database即可。

第二個根本的錯誤是,您很難將connectionNameconnectionString混淆。

connectionName是您爲連接指定的任意名稱,以將其標識爲內部數據庫連接集合中的單獨唯一對象。這可以被認爲是一個變量名稱。

connectionString是用於連接到數據庫引擎本身的指令集,包含數據庫名稱,用戶名,密碼等等。您似乎對此有所瞭解,因此我不會進一步解釋。

您可以創建一個到數據庫的連接而不給它一個數據庫名稱,QtSql框架將創建一個匿名連接,它將用作任何未指定連接名稱的任何調用的默認連接。

如果您嘗試創建沒有名稱的第二個連接,而默認連接已經存在,您將強制將第一個連接銷燬並替換爲新連接。這是發生這種情況時會發生的情況。

你的錯誤:

QSqlDatabase sqliteConnection = QSqlDatabase::addDatabase("QSQLITE"); 
sqliteConnection.setConnectionString("blah blah"); 

QSqlDatabase sqlServerConnection = QSqlDatabase::addDatabase("QODBC3"); // no name specified means overwrite default connection, generates warning message 
sqlServerConnection.setConnectionString("blah blah"); 

你應該做的:

QSqlDatabase sqliteConnection = QSqlDatabase::addDatabase("QSQLITE", "myUniqueSQLITEConnection"); 
sqliteConnection.setConnectionString("blah blah"); 

QSqlDatabase sqlServerConnection = QSqlDatabase::addDatabase("QODBC3", "myUniqueMSSQLConnection"); 
sqlServerConnection.setConnectionString("blah blah"); 

正如我所說,這些連接將在QtSql內部繼續下去,直到你打電話QSqlDatabase::removeDatabase

要做到這一點:

QSqlDatabase myAlreadyExistingMSSqlConnection = QSqlDatabase::database("myUniqueMSSQLConnection"); 
+0

感謝您的回覆。在回覆之前,我已經解決了我的問題。你是正確的,我發現讀得更深一些,到我需要提供一個連接名稱的文件,我也需要對數據庫實例傳遞給qsqlquery所以它不會使用默認實例。至於由於冗餘而不需要經理級,我不會認爲我對Qt相對較新。但是,我還沒有找到類似於經理類中定義的函數的內置函數。換句話說,保持我所有的數據庫代碼與使用它的類不同。 – DonJoe

+0

是否調用'QSqlDatabase ::數據庫()'(其中被標記爲線程安全的)返回一個連接的同一個實例每一次,或每一次它返回一個連接的新實例,這樣你已經添加了一個連接後用'QSqlDatabase :: addDatabase()'(線程安全的爲好),而且你不使用它返回的情況下,你可以從不同的線程相同的設置一個新的連接,而無需記憶違規?換句話說,QSqlDatabase :: database()是一種工廠方法嗎? –

+0

調用'QSqlDatabase ::數據庫()'將返回與創建完全相同的情況下'QSqlDatabase :: addDatabase()'對於一個給定的connectionName(或默認的連接)。正如在[Qt中的線程支持]中提到的(http://doc.qt.io/qt-5/threads-modules。html),你不能使用跨越線程邊界的連接,你應該爲每個線程創建新的連接。 – RobbieE