📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)

 
(同じ利用者による、間の4版が非表示)
1行目: 1行目:
== 概要 ==
== 概要 ==
Qtでデータベース操作を行う場合、QtSqlモジュールが必要である。<br>
<br>
QtSqlモジュールはデータベース操作のための機能を提供するQtのコンポーネントであり、<br>
QSqlDatabase、QSqlQuery、QSqlTableModel等のクラスにアクセスして、データベース操作を効率的に行うことができる。<br>
<br>
このモジュールは、SQLite、MySQL、PostgreSQL、Oracle、SQL Server等、多くのデータベースシステムをサポートしており、<br>
使用するデータベースに応じて、適切なドライバーを選択する必要がある。<br>
<br><br>


== データベース操作の流れ ==
# データベース接続
#: <code>QSqlDatabase</code>クラスを使用して接続を確立する。
#: ドライバの設定、ホスト名、データベース名、ユーザー名、パスワード等を指定する。
#: <br>
# クエリの実行
#: <code>QSqlQuery</code>クラスを使用してSQLクエリを実行する。
#: <br>
# 結果の取得
#: SELECTクエリの結果は<code>QSqlQuery</code>オブジェクトから取得することができる。
#: <code>next</code>メソッドを使用して、結果セットを反復処理する。
#: <br>
# トランザクション処理
#: <code>QSqlDatabase</code>クラスの<code>transaction</code>メソッド、<code>commit</code>メソッド、<code>rollback</code>メソッドを使用する。
#: <br>
# エラー処理
#: <code>QSqlError</code>クラスを使用してエラー情報を取得して処理する。
#: <br>
# モデル/ビュー
#: <code>QSqlTableModel</code>クラスや<code>QSqlRelationalTableModel</code>クラスを使用してデータをGUIに表示することも可能である。
<br><br>
<br><br>


22行目: 50行目:
<br>
<br>
<u>また、使用時にはエラーハンドリングおよびセキュリティ対策 (SQLインジェクション対策等) を適切に実装することを推奨する。</u><br>
<u>また、使用時にはエラーハンドリングおよびセキュリティ対策 (SQLインジェクション対策等) を適切に実装することを推奨する。</u><br>
* ロギング機能の追加
* リトライメカニズムの実装 (一時的なネットワーク問題などに対処するため)
* 詳細なエラータイプの定義 (例:接続エラー、クエリエラー等)
* パラメータのバリデーション
* SQLインジェクション対策の強化
<br>
<br>
<syntaxhighlight lang="cmake">
開発向けMariaDBライブラリをインストールする。<br>
  # CMakeLists.txtファイル
  # RHEL
sudo dnf install mariadb-devel
   
   
  cmake_minimum_required(VERSION 3.21)
  # SUSE
sudo zypper install libmariadbd-devel
<br>
* Qtプロジェクトファイルを使用する場合
<syntaxhighlight lang="make">
# Qtプロジェクトファイル (.pro)
   
   
  project(QtMariaDBProject LANGUAGES CXX)
  # pkg-configを使用してMariaDBの設定を取得
CONFIG    += link_pkgconfig
PKGCONFIG += libmariadb
   
   
  set(CMAKE_INCLUDE_CURRENT_DIR ON)
  # 必要に応じて、追加のコンパイルフラグやリンクフラグを指定することもできる
  set(CMAKE_AUTOUIC ON)
  #QMAKE_CXXFLAGS += $$system(pkg-config --cflags libmariadb)
  set(CMAKE_AUTOMOC ON)
  #LIBS          += $$system(pkg-config --libs libmariadb)
  set(CMAKE_AUTORCC ON)
  </syntaxhighlight>
 
<br>
set(CMAKE_CXX_STANDARD 17)
* CMakeLists.txtを使用する場合
set(CMAKE_CXX_STANDARD_REQUIRED ON)
  <syntaxhighlight lang="cmake">
  # CMakeLists.txtファイル
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Sql)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Sql)
   
  # MariaDB Connector/Cを検索
find_path(MARIADB_INCLUDE_DIR mysql.h
    /usr/include/mysql
)
find_library(MARIADB_LIBRARY NAMES mariadb
    PATHS
    /usr/lib
    /usr/lib64
)
   
   
  if(NOT MARIADB_INCLUDE_DIR OR NOT MARIADB_LIBRARY)
  # pkg-configを使用してMariaDBライブラリを検索
    message(FATAL_ERROR "MariaDB Connector/C not found!")
  find_package(PkgConfig REQUIRED)
  endif()
  pkg_check_modules(MARIADB REQUIRED libmariadb)
   
add_executable(QtMariaDBProject
    main.cpp
    DataBaseManager.h
    DataBaseManager.cpp
)
   
   
  # QtライブラリとMariaDBライブラリをリンク
  # QtライブラリとMariaDBライブラリをリンク
  target_link_libraries(QtMariaDBProject PRIVATE  
  target_link_libraries(<ターゲット名> PRIVATE  
    Qt${QT_VERSION_MAJOR}::Core
     Qt${QT_VERSION_MAJOR}::Sql
     Qt${QT_VERSION_MAJOR}::Sql
     ${MARIADB_LIBRARY}
     ${MARIADB_LIBRARIES}
  )
  )
   
   
  # MariaDBのヘッダファイルのディレクトリを追加
  # MariaDBのヘッダファイルのディレクトリを追加
  target_include_directories(QtMariaDBProject PRIVATE
  target_include_directories(<ターゲット名> PRIVATE
     ${MARIADB_INCLUDE_DIR}
     ${MARIADB_INCLUDE_DIRS}
)
# MariaDBのコンパイルフラグを追加
target_compile_options(<ターゲット名> PRIVATE
    ${MARIADB_CFLAGS_OTHER}
  )
  )
  </syntaxhighlight>
  </syntaxhighlight>
79行目: 105行目:
   
   
  #include <QtSql>
  #include <QtSql>
#include <QString>
#include <QSqlError>
#include <stdexcept>
  #include <QDebug>
  #include <QDebug>
class DatabaseException : public std::runtime_error
{
public:
    DatabaseException(const QString& message) : std::runtime_error(message.toStdString()) {}
};
   
   
  class DatabaseManager
  class DatabaseManager
93行目: 128行目:
     bool updateRecord(int id, const QString &name, int age);
     bool updateRecord(int id, const QString &name, int age);
     bool deleteRecord(int id);
     bool deleteRecord(int id);
     void readAllRecords();
    QList<QMap<QString, QVariant>> readAllRecords();
     void beginTransaction();
    void commitTransaction();
    void rollbackTransaction();
    // 複数の操作をトランザクションで実行する場合のメソッド
    void performComplexOperation(const QString &name1, int age1, const QString &name2, int age2);
   
   
  private:
  private:
     QSqlDatabase db;
     QSqlDatabase db;
    void checkConnection();
    void handleDatabaseError(const QString &operation, const QSqlError &error);
  };
  };
  </syntaxhighlight>
  </syntaxhighlight>
123行目: 167行目:
  {
  {
     if (!db.open()) {
     if (!db.open()) {
       qDebug() << "Error: connection with database failed";
       throw DatabaseException("Failed to open database connection: " + db.lastError().text());
      return false;
    }
    else {
      qDebug() << "Database: connection ok";
      return true;
     }
     }
    qDebug() << "Database connection opened successfully";
  }
  }
   
   
135行目: 176行目:
  void DatabaseManager::closeConnection()
  void DatabaseManager::closeConnection()
  {
  {
     db.close();
     if (db.isOpen()) {
      db.close();
      qDebug() << "Database connection closed";
    }
}
// 接続の確認
void DatabaseManager::checkConnection()
{
    if (!db.isOpen()) {
      throw DatabaseException("Database connection is not open");
    }
}
// エラーハンドリング処理
void DatabaseManager::handleDatabaseError(const QString &operation, const QSqlError &error)
{
    QString errorMessage = QString("%1 failed: %2").arg(operation, error.text());
    throw DatabaseException(errorMessage);
}
//  トランザクションを開始
void DatabaseManager::beginTransaction()
{
    checkConnection();
    if (!db.transaction()) {
      throw DatabaseException("Failed to start transaction: " + db.lastError().text());
    }
    qDebug() << "Transaction started";
}
// トランザクションをコミット
void DatabaseManager::commitTransaction()
{
    checkConnection();
    if (!db.commit()) {
      throw DatabaseException("Failed to commit transaction: " + db.lastError().text());
    }
    qDebug() << "Transaction committed";
}
// トランザクションをロールバック
void DatabaseManager::rollbackTransaction()
{
    checkConnection();
    if (!db.rollback()) {
      throw DatabaseException("Failed to rollback transaction: " + db.lastError().text());
    }
    qDebug() << "Transaction rolled back";
}
// 複数の操作を1つのトランザクションとして実行するメソッド
void DatabaseManager::performComplexOperation(const QString &name1, int age1, const QString &name2, int age2)
{
    try {
      beginTransaction();
      insertRecord(name1, age1);
      insertRecord(name2, age2);
      commitTransaction();
      qDebug() << "Complex operation completed successfully";
    }
    catch (const DatabaseException& e) {
      rollbackTransaction();
      throw DatabaseException("Complex operation failed: " + QString(e.what()));
    }
  }
  }
   
   
141行目: 255行目:
  bool DatabaseManager::createTable()
  bool DatabaseManager::createTable()
  {
  {
    checkConnection();
     QSqlQuery query;
     QSqlQuery query;
     query.prepare("CREATE TABLE IF NOT EXISTS people (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(40) NOT NULL, age INT)");
     if (!query.exec("CREATE TABLE IF NOT EXISTS people (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(40) NOT NULL, age INT)")) {
       handleDatabaseError("Create table", query.lastError());
    if (!query.exec()) {
       qDebug() << "Error creating table:" << query.lastError().text();
      return false;
     }
     }
   
   
     return true;
     qDebug() << "Table 'people' created or already exists";
  }
  }
   
   
155行目: 268行目:
  bool DatabaseManager::insertRecord(const QString& name, int age)
  bool DatabaseManager::insertRecord(const QString& name, int age)
  {
  {
    checkConnection();
     QSqlQuery query;
     QSqlQuery query;
     query.prepare("INSERT INTO people (name, age) VALUES (:name, :age)");
     query.prepare("INSERT INTO people (name, age) VALUES (:name, :age)");
161行目: 276行目:
   
   
     if (!query.exec()) {
     if (!query.exec()) {
       qDebug() << "Error inserting record:" << query.lastError().text();
       handleDatabaseError("Insert record", query.lastError());
      return false;
     }
     }
   
   
     return true;
     qDebug() << "Record inserted successfully";
  }
  }
   
   
171行目: 285行目:
  bool DatabaseManager::updateRecord(int id, const QString& name, int age)
  bool DatabaseManager::updateRecord(int id, const QString& name, int age)
  {
  {
    checkConnection();
     QSqlQuery query;
     QSqlQuery query;
     query.prepare("UPDATE people SET name = :name, age = :age WHERE id = :id");
     query.prepare("UPDATE people SET name = :name, age = :age WHERE id = :id");
178行目: 294行目:
   
   
     if (!query.exec()) {
     if (!query.exec()) {
       qDebug() << "Error updating record:" << query.lastError().text();
       handleDatabaseError("Update record", query.lastError());
       return false;
    }
    if (query.numRowsAffected() == 0) {
       throw DatabaseException(QString("No record found with id: %1").arg(id));
     }
     }
   
   
     return true;
     qDebug() << "Record updated successfully";
  }
  }
   
   
// レコードの削除
  bool DatabaseManager::deleteRecord(int id)
  bool DatabaseManager::deleteRecord(int id)
  {
  {
    checkConnection();
     QSqlQuery query;
     QSqlQuery query;
     query.prepare("DELETE FROM people WHERE id = :id");
     query.prepare("DELETE FROM people WHERE id = :id");
     query.bindValue(":id", id);
     query.bindValue(":id", id);
   
    if (!query.exec()) {
      handleDatabaseError("Delete record", query.lastError());
    }
   
   
     if (!query.exec()) {
     if (query.numRowsAffected() == 0) {
       qDebug() << "Error deleting record:" << query.lastError().text();
       throw DatabaseException(QString("No record found with id: %1").arg(id));
      return false;
     }
     }
   
   
     return true;
     qDebug() << "Record deleted successfully";
  }
  }
   
   
  // 全てのレコードを読み込む
  // 全てのレコードを読み込む
  void DatabaseManager::readAllRecords()
  QList<QMap<QString, QVariant>> DatabaseManager::readAllRecords()
  {
  {
    checkConnection();
     QSqlQuery query("SELECT * FROM people");
     QSqlQuery query("SELECT * FROM people");
    QList<QMap<QString, QVariant>> results;
   
    if (!query.exec()) {
      handleDatabaseError("Read all records", query.lastError());
    }
     while (query.next()) {
     while (query.next()) {
       int id = query.value(0).toInt();
       QMap<QString, QVariant> record;
       QString name = query.value(1).toString();
      record["id"] = query.value(0);
       int age = query.value(2).toInt();
       record["name"] = query.value(1);
       qDebug() << "ID:" << id << "Name:" << name << "Age:" << age;
       record["age"] = query.value(2);
       results.append(record);
     }
     }
    return results;
  }
  }
  </syntaxhighlight>
  </syntaxhighlight>
219行目: 355行目:
     DatabaseManager dbManager;
     DatabaseManager dbManager;
   
   
     if (dbManager.openConnection()) {
     try {
      // データーベースの接続
      dbManager.openConnection();
     
       // テーブルの作成
       // テーブルの作成
       dbManager.createTable();
       dbManager.createTable();
   
   
       // レコードの追加
       // トランザクションを使用した複雑な操作
       dbManager.insertRecord("John Doe", 30);
       dbManager.performComplexOperation("Alice", 28, "Bob", 32);
      dbManager.insertRecord("Jane Smith", 25);
      // 全てのレコードを読み込む
      dbManager.readAllRecords();
   
   
       // 既存のレコードの更新
       // 個別のトランザクション管理の例
      dbManager.updateRecord(1, "John Doe Updated", 31);
      dbManager.beginTransaction();
      try {
          // 既存のレコードの更新
          dbManager.updateRecord(1, "John Doe Updated", 31);
   
   
      // 任意のレコードの削除
          // 任意のレコードの削除
      dbManager.deleteRecord(2);
          dbManager.deleteRecord(2);
          dbManager.commitTransaction();
      }
      catch (const DatabaseException& e) {
          dbManager.rollbackTransaction();
          qCritical() << "Transaction failed:" << e.what();
      }
   
   
       // 全てのレコードを読み込む
       // 全てのレコードを読み込む
       dbManager.readAllRecords();
       QList<QMap<QString, QVariant>> records = dbManager.readAllRecords();
      for (const auto &record : records) {
       // データーベースを切断
          qDebug() << "ID:" << record["id"].toInt()
       dbManager.closeConnection();
                  << "Name:" << record["name"].toString()
                  << "Age:" << record["age"].toInt();
       }
 
        // 全てのレコードを読み込む
        records = dbManager.readAllRecords();
        for (const auto& record : records) {
          qDebug() << "ID:" << record["id"].toInt()
                    << "Name:" << record["name"].toString()
                    << "Age:" << record["age"].toInt();
        }
    }
    catch (const DatabaseException &e) {
      // データベース関連のエラー
      qCritical() << "Database error: " << e.what();
    }
    catch (const std::exception &e) {
       // その他のエラー
      qCritical() << "Unexpected error: " << e.what();
     }
     }
   
   
246行目: 408行目:
  }
  }
  </syntaxhighlight>
  </syntaxhighlight>
<br>
トランザクションを使用するメリットを以下に示す。<br>
* データの一貫性
*: 複数の操作を1つの単位として扱うことができる。
* エラー回復
*: 問題が発生した場合、簡単に全ての変更を元に戻すことができる。
* パフォーマンス
*: 多数の操作を一度のトランザクションで行うことにより、データベースとの通信回数を減らし、パフォーマンスを向上させることができる。
<br>
ただし、トランザクション管理を使用する場合は、以下に示す事柄に注意すること。<br>
* トランザクションは必要な場合にのみ使用して、不必要に長く保持しないようにする。
* ネストされたトランザクションの扱いには注意が必要である。<br><code>QSQLDatabase</code>クラスは、一般的にネストされたトランザクションをサポートしていない。
* 大規模なデータ操作を行う場合は、適切なチェックポイントを設定することを検討する。
<br><br>
<br><br>