4

sqlite データベースとの間のすべてのデータを処理するクラスを作成しようとしています。ただし、私は QT と C++ にかなり慣れていないため、クラス内のデータベース オブジェクトの宣言について疑問に思っています。私が行っている正しいことと間違っていること、および通常はどのように行うべきか、または行うことができるかについて、いくつかのヒントが必要になる場合があります。私の目標は、クラス用に単一の QSqlDatabase を作成し、それをクラス内のすべての関数に使用することでした。

現時点では、次のコードがあります。

main.cpp

#include "mainwindow.h"
#include "database.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    Database db;
    MainWindow w;

    if(db.createStructure())
    {
        w.show();
    }

    return a.exec();
}

データベース.h

#ifndef DATABASE_H
#define DATABASE_H

#include <QObject>
#include <QSqlDatabase>

class Database : public QObject
{
    Q_OBJECT
public:
    explicit Database(QObject *parent = 0);

    // FUNCTIONS
    bool createStructure();

signals:

public slots:

private:
    // VARIABLES
    QSqlDatabase m_db;

    // FUNCTIONS
    bool open();
    void close();
    bool transaction();
    bool commit();
};

#endif // DATABASE_H

データベース.cpp

#include "database.h"
#include <QCoreApplication>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QList>

Database::Database(QObject *parent) :
    QObject(parent)
{
    m_db = QSqlDatabase::addDatabase("QSQLITE");
    m_db.setHostName("localhost");
    m_db.setDatabaseName(QCoreApplication::applicationDirPath() + "/events.db");
}

// PRIVATE

bool Database::open()
{
    return m_db.open();
}

void Database::close()
{
    return m_db.close();
}

bool Database::transaction()
{
    return m_db.transaction();
}

bool Database::commit()
{
    return m_db.commit();
}

// PUBLIC

bool Database::createStructure()
{
    bool prepared;
    QList<QString> commands;

    commands.append("CREATE TABLE...;");
    commands.append("CREATE TABLE...;");
    commands.append("CREATE TABLE...;");

    if (!Database::open())
    {
        return false;
    }
    else
    {
        if (!Database::transaction())
        {
            Database::close();
            return false;
        }
        else
        {
            foreach(QString command, commands)
            {
                QSqlQuery query;
                prepared = query.prepare(command);

                if(!prepared)
                {
                    if (!Database::commit())
                    {
                        Database::close();
                        return false;
                    }
                    else
                    {
                        Database::close();
                        return false;
                    }
                }
                else
                {
                    if(!query.exec())
                    {
                        if (!Database::commit())
                        {
                            Database::close();
                            return false;
                        }
                        else
                        {
                            Database::close();
                            return false;
                        }
                    }
                }
            }

            if (!Database::commit())
            {
                Database::close();
                return false;
            }
            else
            {
                Database::close();
                return true;
            }
        }
    }
}

このコードは機能しています。

ただし、QSQLITE データベースは m_db オブジェクトに 1 回追加されるのではなく、クラス内の関数が呼び出されるたびに追加されます。

Database::Database(QObject *parent) :
    QObject(parent)
{
    m_db = QSqlDatabase::addDatabase("QSQLITE");
    m_db.setHostName("localhost");
    m_db.setDatabaseName(QCoreApplication::applicationDirPath() + "/events.db");
}

...コードブロックは毎回実行されます。現在のデフォルトの接続が置き換えられただけで、新しい接続は同じであるため、プログラムには何の影響もありませんが、きちんとした解決策のようには見えません。

そこで、このコードブロックを main.cpp から一度呼び出すことができる宣言関数に置き換えようとしました...

main.cpp

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    Database db;
    MainWindow w;

    db.declare(“QSQLITE”, “localhost”, QCoreApplication::applicationDirPath() + "/events.db");

    if(db.createStructure())
    {
        w.show();
    }

    return a.exec();
}

データベース.cpp

void Database::declare(QString driver, QString host, QString path)
{
    m_db = QSqlDatabase::addDatabase(driver);
    m_db.setHostName(host);
    m_db.setDatabaseName(path);
}

...しかし、m_db オブジェクトの値はもちろん、宣言関数内でのみ使用でき、後で呼び出す他の関数では使用できません。

私の最善の解決策は、main.cpp で QSqlDatabase を宣言し、呼び出す関数に渡すことです。

main.cpp

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QSqlDatabase qdb = QSqlDatabase::addDatabase("QSQLITE");
    qdb.setHostName("localhost");
    qdb.setDatabaseName(QCoreApplication::applicationDirPath() + "/events.db");

    Database db;
    MainWindow w;

    if(db.createStructure(qdb))
    {
        w.show();
    }

    return a.exec();
}

データベース.cpp

bool Database::open(QSqlDatabase qdb)
{
    return qdb.open();
}

void Database::close(QSqlDatabase qdb)
{
    return qdb.close();
}

bool Database::transaction(QSqlDatabase qdb)
{
    return qdb.transaction();
}

bool Database::commit(QSqlDatabase qdb)
{
    return qdb.commit();
}

bool Database::createStructure(QSqlDatabase qdb)
{
    bool prepared;
    QList<QString> commands;

    commands.append("CREATE TABLE...;");
    commands.append("CREATE TABLE...;");
    commands.append("CREATE TABLE...;");

    if (!Database::open(qdb))
    {
        return false;
    }
    else
    {
        if (!Database::transaction(qdb))
        {
            Database::close(qdb);
            return false;
        }
        else
        {
            foreach(QString command, commands)
            {
                QSqlQuery query;
                prepared = query.prepare(command);

                if(!prepared)
                {
                    if (!Database::commit(qdb))
                    {
                        Database::close(qdb);
                        return false;
                    }
                    else
                    {
                        Database::close(qdb);
                        return false;
                    }
                }
                else
                {
                    if(!query.exec())
                    {
                        if (!Database::commit(qdb))
                        {
                            Database::close(qdb);
                            return false;
                        }
                        else
                        {
                            Database::close(qdb);
                            return false;
                        }
                    }
                }
            }

            if (!Database::commit(qdb))
            {
                Database::close(qdb);
                return false;
            }
            else
            {
                Database::close(qdb);
                return true;
            }
        }
    }
}

再利用可能な QSqlDatabase オブジェクトを何らかの形でクラスに格納することは可能ですか? もしそうなら、どのように?本当にありがとうございました!

編集1

関数を使用しているデザイナーから作成されたコード。

mainwindows.cpp

void MainWindow::on_pushButton_24_clicked()
{
    Database db;
    bool b = db.createStructure();

    QMessageBox::information(this, "test", QString(b));
}
4

1 に答える 1

1

説明のために元のコードに固執します。


免責事項:提案をコンパイルしていません。構文エラーがある場合はご容赦ください。


まず第一に、おそらくあなたが探しているのはSingleton パターンです (私はもうあまり好きではありませんが、あなたの目的のためには、それが適切であると考えることができます):

クラス定義には次のものが必要です。

class Database : public QObject
{
    Q_OBJECT

public:
    static Database* instance();
private:
    static Database* m_instance;
    Database();
    ~Database() {}; // it can be necessary to have this public in some cases, if 
                    // you ever get a linker error related to deletion, this is 
                    // probably the reason.

public: 

    // FUNCTIONS
    ...
};

そして、.cpp ファイルに以下を追加します。

// init singleton pointer to NULL
Database* Database::m_instance = NULL;

Database* Database::instance()
{
    if( !m_instance )
    {
        m_instance = new Database();
    }
    return m_instance;
}

次に、たとえばを使用してそのシングルトンにアクセスできます

if( Database::instance()->createStructure() )
{
    w.show();
}

これは何をしますか?プログラムの開始時に、行

Database* Database::m_instance = NULL;

m_instance 変数を に初期化しますNULL。初めて呼び出すDatabase::instance()と、 m_instance がまだ NULL であることがわかり、新しいオブジェクトが作成m_instanceされ、そのオブジェクトが示されます。その時点から、そのオブジェクトへのポインターは常に返されますが、それ以上のDatabaseオブジェクトは作成されません。


createStructure()関数commit()では、エラーが発生した場合でもデータベースを使用します。通常の手順はcommit()、成功した場合とrollback()失敗した場合です。それを修正する前に、必ず次の点を読んでください。


私がお勧めする 3 番目のことは、同じ行が複数回出現するたびに疑わしいことに慣れることです。それは通常、サブ機能を求めて泣きます。

私は話している

Database::close();
return false;

createStructure()別のメソッドを導入しelse{ }、不要な場所を除外して、メソッドをどのように書き直したかを見てください。

bool Database::createStructure()
{
    QStringList commands;

    commands.append("CREATE TABLE...;");
    commands.append("CREATE TABLE...;");
    commands.append("CREATE TABLE...;");

    if (!Database::open()) return false;

    // at this point you can be sure the database is open

    if (!Database::transaction())
    { 
        Database::close();
        return false;
    }

    // at this point you can be sure the database is open and a transaction was started

    if (!Database::executeCommands(commands))
    {
        // an error occurred - we need to rollback what we did so far
        Database::rollback();
        Database::close();
        return false;
    }

    // everything was executed properly, but the transaction is still active
    // => commit the changes we've made
    bool committed = Database::commit();

    // no matter if the commit was successful or not, close the database,
    // then return the result we've stored
    Database::close();
    return committed;
}

bool Database::executeCommands(const QStringList& commands)
{
    // This method simply executes the queries and is relieved from
    // transaction-related code.

    foreach(QString command, commands)
    {
        QSqlQuery query;
        bool prepared = query.prepare(command);

        if(!prepared) return false;

        if(!query.exec()) return false;        
    }
    return true;
}

これはさらにリファクタリングすることができます。これは、コードをより簡単に追跡できるようにするための単なる例であり、通常はエラーが発生しにくくなります。

于 2013-02-15T14:46:31.830 に答える