📢 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> | ||
開発向けMariaDBライブラリをインストールする。<br> | |||
# | # RHEL | ||
sudo dnf install mariadb-devel | |||
# SUSE | |||
sudo zypper install libmariadbd-devel | |||
<br> | |||
* Qtプロジェクトファイルを使用する場合 | |||
<syntaxhighlight lang="make"> | |||
# Qtプロジェクトファイル (.pro) | |||
# pkg-configを使用してMariaDBの設定を取得 | |||
CONFIG += link_pkgconfig | |||
PKGCONFIG += libmariadb | |||
# 必要に応じて、追加のコンパイルフラグやリンクフラグを指定することもできる | |||
#QMAKE_CXXFLAGS += $$system(pkg-config --cflags libmariadb) | |||
#LIBS += $$system(pkg-config --libs libmariadb) | |||
</syntaxhighlight> | |||
<br> | |||
* CMakeLists.txtを使用する場合 | |||
<syntaxhighlight lang="cmake"> | |||
# CMakeLists.txtファイル | |||
# | |||
# pkg-configを使用してMariaDBライブラリを検索 | |||
find_package(PkgConfig REQUIRED) | |||
pkg_check_modules(MARIADB REQUIRED libmariadb) | |||
# QtライブラリとMariaDBライブラリをリンク | # QtライブラリとMariaDBライブラリをリンク | ||
target_link_libraries( | target_link_libraries(<ターゲット名> PRIVATE | ||
Qt${QT_VERSION_MAJOR}::Sql | Qt${QT_VERSION_MAJOR}::Sql | ||
${ | ${MARIADB_LIBRARIES} | ||
) | ) | ||
# MariaDBのヘッダファイルのディレクトリを追加 | # MariaDBのヘッダファイルのディレクトリを追加 | ||
target_include_directories( | target_include_directories(<ターゲット名> PRIVATE | ||
${ | ${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 | 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()) { | ||
throw DatabaseException("Failed to open database connection: " + db.lastError().text()); | |||
} | } | ||
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. | 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()); | |||
} | } | ||
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()) { | ||
handleDatabaseError("Insert record", query.lastError()); | |||
} | } | ||
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()) { | ||
handleDatabaseError("Update record", query.lastError()); | |||
} | |||
if (query.numRowsAffected() == 0) { | |||
throw DatabaseException(QString("No record found with id: %1").arg(id)); | |||
} | } | ||
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 ( | if (query.numRowsAffected() == 0) { | ||
throw DatabaseException(QString("No record found with id: %1").arg(id)); | |||
} | } | ||
qDebug() << "Record deleted successfully"; | |||
} | } | ||
// 全てのレコードを読み込む | // 全てのレコードを読み込む | ||
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()) { | ||
QMap<QString, QVariant> record; | |||
record["id"] = query.value(0); | |||
record["name"] = query.value(1); | |||
record["age"] = query.value(2); | |||
results.append(record); | |||
} | } | ||
return results; | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| 219行目: | 355行目: | ||
DatabaseManager dbManager; | DatabaseManager dbManager; | ||
try { | |||
// データーベースの接続 | |||
dbManager.openConnection(); | |||
// テーブルの作成 | // テーブルの作成 | ||
dbManager.createTable(); | dbManager.createTable(); | ||
// | // トランザクションを使用した複雑な操作 | ||
dbManager. | dbManager.performComplexOperation("Alice", 28, "Bob", 32); | ||
// 既存のレコードの更新 | // 個別のトランザクション管理の例 | ||
dbManager.beginTransaction(); | |||
try { | |||
// 既存のレコードの更新 | |||
dbManager.updateRecord(1, "John Doe Updated", 31); | |||
// 任意のレコードの削除 | |||
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() | ||
<< "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> | ||