2017-02-03 29 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のDBがグローバル・アプリケーションを格納するために使用されています設定)

プログラムのメンバ関数内から
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

2つのデータベースのための2つのオブジェクトを作成するには、このクラスを使用しているご提示ください。私は問題なくMySQLとSQLiteでこれを行いました。 –

+0

上記のポストされた例を参照して、私はプログラム内でマネージャクラスのインスタンスを2つ作成しました.1つはsqlite用、もう1つはms sql用です。 sqliteインスタンスは参照渡しされ、アプリケーションを持つすべてのクラスで使用できます。 ms sqlインスタンスは、プログラムクラス内のプライベートメンバポインタです。インスタンス化と接続の後、私はその時必要なDBマネージャークラスの公開関数を呼び出します。つまり、レコードの更新、レコードの取得などです。 – DonJoe

+1

コードと私の違いは、接続名で送信したことです。 myDB = QSqlDatabase :: addDatabase(databaseType、connectionName);私はこれがあなたの問題に対処するかもしれないと思います。 (dbごとに異なる接続名) –

答えて

2

あなたが作っている最初の根本的な間違いは、データベース接続マネージャが冗長であるということです。あなたが達成しようとしているのは、既にQtSqlフレームワークに組み込まれています。

QSqlDatabaseの新しいインスタンスをQSqlDatabase::addDatabaseで作成すると、QSqlDatabase::removeDatabaseを呼び出すまで、オブジェクトはQtの内部に保持されます。変数を接続したままにする必要はありません。あなたはその間に接続にアクセスする必要がある場合は、単にQSqlDatabase::databaseと呼んでください。

第2の基本的な間違いは、connectionNameconnectionStringを混同していることです。

connectionNameは、データベース接続の内部コレクション内の別個の一意のオブジェクトとして識別するために接続に与えた任意の名前です。これは変数名のように考えることができます。

connectionStringは、データベース名、ユーザー名、パスワードなどを含む、データベースエンジン自体に接続するために使用される一連の命令です。これについては理解しているようですので、これ以上説明しません。

QtSqlフレームワークは、接続名を指定しないで呼び出したときのデフォルト接続として使用する匿名接続を作成します。

名前を付けずに2番目の接続を作成しようとすると、既定の接続が既に存在する場合は、最初の接続を強制的に破棄して新しい接続に置き換えます。これはあなたがそのメッセージを受け取ったときに起こることです。

あなたの間違い:

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"); 

私が述べたように、あなたがQSqlDatabase::removeDatabaseを呼び出すまで、これらの接続はQtSql内部に保持されます。そうするには

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

お返事ありがとうございます。あなたの回答に先立ち、私は自分の問題を解決しました。あなたは正しいですが、接続名を提供するために必要なドキュメントを少し深く読んで見つけました。また、デフォルトインスタンスを使用しないようにデータベースインスタンスをqsqlqueryに渡す必要がありました。冗長性のためにマネージャクラスを必要としないため、私はQtに比較的新しいものであると主張しません。しかし、私はまだマネージャークラスで定義されているものに似た組み込み関数を見つけることはできません。言い換えれば、私のデータベースコードは、使用されているクラスとは別のものにしておきます。 – DonJoe

+0

'QSqlDatabase :: database()'(スレッドセーフとマークされています)を呼び出すと、毎回同じ接続インスタンスが返されますか、接続を追加した後に毎回新しい接続インスタンスを返すか'QSqlDatabase :: addDatabase()'(スレッドセーフでもある)を使用していて、返されるインスタンスを使用しない場合、メモリー違反のない別のスレッドから同じ設定で新しい接続を取得できますか?つまり、 'QSqlDatabase :: database()'はファクトリメソッドの一種ですか? –

+0

'QSqlDatabase :: database()'を呼び出すと、指定されたconnectionName(またはデフォルトの接続)に対して 'QSqlDatabase :: addDatabase()'で作成されたインスタンスとまったく同じインスタンスが返されます。 [Qtのスレッドサポート](http://doc.qt.io/qt-5/threads-modules。html)では、スレッド境界を越えて接続を使用することはできません。スレッドごとに新しい接続を作成する必要があります。 – RobbieE

関連する問題