Qt でデータベースを使う方法(1)
Qt でリレーショナルデータベースを扱う場合は、Qt SQL モジュールを使います。
MySQL や PostgreSQL、SQLITE などのドライバ が用意されており、様々なデータベースを共通のインターフェースで扱うことができるようになっています。
(このうち、QTDS は Qt 4.7 で廃止済み、SQLITE2 も Qt 5.14 で廃止 の予定です。)
今回は、SQLITE3 を利用し、データベースを扱う基本的な方法を紹介します。
ヘッダファイルのインクルード
#include <QtSql/QSqlQuery>
#include <QtSql/QSqlError>
データベースにアクセスするためには通常上記の3つのクラスを利用します。
データベース接続の作成
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", connectionName);
データベースの接続は QSqlDatabase クラスで管理され、addDatabase() メソッドで作成します。利用するデータベースのドライバを指定し、後から参照できるように connectionName で接続に名前をつけています。
データベースの接続
if (!db.open()) {
qWarning() << db.lastError();
}
と、接続するデータベースの情報を指定します。
SQLITE の場合は、setDatabaseName() に、接続するデータベースのファイル名を指定します。
ファイルではなく メモリ上にデータベースを作成する場合は、”:memory:” と指定 することができます。
ここでは、スタンドアロンの SQLITE に接続するため、ホスト名やログインの設定はしていません。
MySQL や PostgreSQL などのデータベースに接続するには、以下のような設定になります。
db.setDatabaseName("test");
db.setUserName("user");
db.setPassword("password");
テーブルの作成
if (query.prepare("CREATE TABLE account("
" id INTEGER PRIMARY KEY"
", name TEXT NOT NULL"
", password TEXT NOT NULL"
")")) {
if (!query.exec()) {
qWarning() << query.lastError();
qInfo() << query.lastQuery() << query.boundValues();
}
} else {
qWarning() << query.lastError();
}
テーブルの作成など、SQL の実行には QSqlQuery クラスを利用します。
インスタンスの作成時にデータベースを指定し、prepare() メソッドで SQL を設定し、exec() で実行をしています。
各メソッドが失敗した場合には、lastError() メソッドでエラーの詳細が取得できます。
レコードの追加
if (query.prepare("INSERT INTO account(name, password)"
" VALUES (?, ?)")) {
query.addBindValue("user0");
query.addBindValue("password0"); // TODO: do not store plain password
if (query.exec()) {
qInfo() << query.lastInsertId().toLongLong() << "added";
} else {
qWarning() <<query.lastError();
qInfo() << query.lastQuery() << query.boundValues();
}
} else {
qWarning() << query.lastError();
}
SQL のプレースホルダーを利用し、addBindValue() メソッドでデータを指定しています。
トランザクションとレコードの一括追加
QSqlQuery query(db);
if (query.prepare("INSERT INTO account(name, password)"
" VALUES (?, ?)")) {
for (int i = 1; i < 10; i++) {
query.addBindValue(QString("user%1").arg(i));
query.addBindValue(QString("password%1").arg(i));
if (!query.exec()) {
qWarning() << query.lastError();
qInfo() << query.lastQuery() << query.boundValues();
}
}
if (!db.commit()) {
qWarning() << "commit failed" << db.lastError();
}
} else {
qWarning() << query.lastError();
}
} else {
qWarning() << "transaction failed" << db.lastError();
}
トランザクションの制御は transaction() と commit() 、rollback() で行うことが可能です。
複数追加は、prepare() をした後、addBindValue() -> exec() の繰り返しで行っています。
レコードの編集と削除
if (query.prepare("UPDATE account"
" SET password = ?"
" WHERE name = ?")) {
query.addBindValue("passwordX");
query.addBindValue("user7");
if (!query.exec()) {
qWarning() << query.lastError();
qInfo() << query.lastQuery() << query.boundValues();
}
} else {
qWarning() << query.lastError();
}
if (query.prepare("DELETE FROM account"
" WHERE name = ?")) {
query.addBindValue("user4");
if (!query.exec()) {
qWarning() << query.lastError();
qInfo() << query.lastQuery() << query.boundValues();
}
} else {
qWarning() << query.lastError();
}
prepare(), addBindValue(), exec() の流れは同じです。
レコードの取得
if (query.prepare("SELECT id, name, password"
" FROM account")) {
if (query.exec()) {
while (query.next()) {
qlonglong id = query.value(0).toLongLong();
QString name = query.value(1).toString();
QString password = query.value(2).toString();
qDebug() << id << name << password;
}
} else {
qWarning() << query.lastError();
qInfo() << query.lastQuery() << query.boundValues();
}
} else {
qWarning() << query.lastError();
}
prepare(), exec() でクエリを発行しています。
WHERE 文などで条件を指定する場合は、プレースホルダを利用し、addBindValue() で値を設定してください。
SQL の実行後に、query.next() でレコードを移動させながら、query.value() でフィールドの値を取得しています。
テーブル一覧の取得
tables() メソッドでデータベースに存在するテーブルの一覧の取得が可能です。
接続の切断
接続情報の破棄
removeDatabase() を実行する際には気をつけるべきことがあります。
削除するものは、connectionName で指定します。また、その接続自体の QSqlDatabase のインスタンスや、それを利用している QSqlQuery のインスタンスが残っている場合にこの処理は失敗します。
このため、実際のコードでは、そういったインスタンスの管理のためのスコープを取り入れるなどの工夫が必要となります。
その辺りを含めた全体のソースコードを https://github.com/task-jp/qsqlite に置きましたので、興味のある方は見てみてください。