Qt 与 SQLite 的结合是开发轻量级、跨平台嵌入式数据库应用的理想选择。SQLite 作为一种零配置、文件型数据库,无需独立的服务器进程,非常适合集成到 Qt 应用中。本文将深入探讨 Qt 与 SQLite 的嵌入式数据库开发,包括基础操作、高级特性、性能优化和实际应用案例。
一、SQLite 基础配置与连接
1. 驱动检查与数据库连接
cpp
#include <QSqlDatabase>
#include <QSqlError>
#include <QDebug>
void connectToSQLite() {
// 检查 SQLite 驱动是否可用
if (!QSqlDatabase::isDriverAvailable("QSQLITE")) {
qDebug() << "SQLite driver not available!";
return;
}
// 创建数据库连接
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("mydatabase.db"); // 数据库文件名
if (!db.open()) {
qDebug() << "Cannot open database:" << db.lastError().text();
return;
}
qDebug() << "SQLite database connected successfully!";
}
2. 创建表结构
cpp
void createTables(QSqlDatabase &db) {
QSqlQuery query(db);
// 创建用户表
query.exec(R"(
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER,
email TEXT UNIQUE
)
)");
if (query.lastError().isValid()) {
qDebug() << "Error creating table:" << query.lastError().text();
}
// 创建订单表
query.exec(R"(
CREATE TABLE IF NOT EXISTS orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
product TEXT,
price REAL,
order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
)
)");
}
二、数据操作与查询
1. 插入数据
cpp
void insertData(QSqlDatabase &db) {
QSqlQuery query(db);
// 插入用户数据
query.prepare("INSERT INTO users (name, age, email) VALUES (:name, :age, :email)");
query.bindValue(":name", "Alice");
query.bindValue(":age", 30);
query.bindValue(":email", "alice@example.com");
if (query.exec()) {
qDebug() << "User inserted successfully!";
} else {
qDebug() << "Error inserting user:" << query.lastError().text();
}
// 批量插入订单数据
db.transaction(); // 使用事务提高性能
QList<QVariantList> orders = {
{1, "Product A", 99.99},
{1, "Product B", 49.99},
{1, "Product C", 199.99}
};
query.prepare("INSERT INTO orders (user_id, product, price) VALUES (:user_id, :product, :price)");
for (const auto &order : orders) {
query.bindValue(":user_id", order[0]);
query.bindValue(":product", order[1]);
query.bindValue(":price", order[2]);
query.exec();
}
db.commit();
}
2. 查询数据
cpp
void queryData(QSqlDatabase &db) {
// 简单查询
QSqlQuery query("SELECT * FROM users", db);
while (query.next()) {
int id = query.value("id").toInt();
QString name = query.value("name").toString();
int age = query.value("age").toInt();
QString email = query.value("email").toString();
qDebug() << "User:" << id << name << age << email;
}
// 带参数的查询
query.prepare("SELECT * FROM orders WHERE user_id = :user_id AND price > :min_price");
query.bindValue(":user_id", 1);
query.bindValue(":min_price", 50.0);
if (query.exec()) {
while (query.next()) {
QString product = query.value("product").toString();
double price = query.value("price").toDouble();
QDateTime date = query.value("order_date").toDateTime();
qDebug() << "Order:" << product << price << date.toString("yyyy-MM-dd");
}
}
}
三、高级特性与优化
1. 事务处理
cpp
bool transferMoney(QSqlDatabase &db, int fromAccount, int toAccount, double amount) {
db.transaction();
try {
QSqlQuery query(db);
// 减少源账户余额
query.prepare("UPDATE accounts SET balance = balance - :amount WHERE id = :id");
query.bindValue(":amount", amount);
query.bindValue(":id", fromAccount);
if (!query.exec() || query.numRowsAffected() == 0) {
throw std::runtime_error("Failed to debit from source account");
}
// 增加目标账户余额
query.prepare("UPDATE accounts SET balance = balance + :amount WHERE id = :id");
query.bindValue(":amount", amount);
query.bindValue(":id", toAccount);
if (!query.exec() || query.numRowsAffected() == 0) {
throw std::runtime_error("Failed to credit to target account");
}
db.commit();
return true;
} catch (const std::exception &e) {
db.rollback();
qDebug() << "Transaction failed:" << e.what();
return false;
}
}
2. 索引优化
cpp
void createIndex(QSqlDatabase &db) {
QSqlQuery query(db);
// 创建索引提高查询性能
query.exec("CREATE INDEX IF NOT EXISTS idx_orders_user_id ON orders (user_id)");
query.exec("CREATE INDEX IF NOT EXISTS idx_orders_price ON orders (price)");
// 创建复合索引
query.exec("CREATE INDEX IF NOT EXISTS idx_users_name_age ON users (name, age)");
}
3. 外键约束
cpp
void enableForeignKeys(QSqlDatabase &db) {
QSqlQuery query(db);
// 启用外键约束(SQLite 默认禁用)
query.exec("PRAGMA foreign_keys = ON");
if (query.lastError().isValid()) {
qDebug() << "Failed to enable foreign keys:" << query.lastError().text();
}
}
四、SQLite 特定功能
1. 数据库加密(使用 SQLCipher)
cpp
void openEncryptedDatabase(const QString &dbPath, const QString &password) {
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(dbPath);
// 设置加密密钥
db.setConnectOptions("QSQLITE_ENABLE_REGEXP=1;QSQLITE_PWD=" + password);
if (!db.open()) {
qDebug() << "Cannot open encrypted database:" << db.lastError().text();
return;
}
qDebug() << "Encrypted database opened successfully!";
}
2. 全文搜索
cpp
void createFtsTable(QSqlDatabase &db) {
QSqlQuery query(db);
// 创建 FTS5 全文搜索表
query.exec(R"(
CREATE VIRTUAL TABLE IF NOT EXISTS notes USING fts5(
title,
content,
tags
)
)");
// 插入测试数据
query.prepare("INSERT INTO notes (title, content, tags) VALUES (:title, :content, :tags)");
query.bindValue(":title", "Qt Development");
query.bindValue(":content", "Learn Qt with SQLite for embedded applications");
query.bindValue(":tags", "Qt,SQLite,Embedded");
query.exec();
}
void searchFtsTable(QSqlDatabase &db, const QString &keyword) {
QSqlQuery query(db);
query.prepare("SELECT * FROM notes WHERE content MATCH :keyword");
query.bindValue(":keyword", keyword);
if (query.exec()) {
while (query.next()) {
QString title = query.value("title").toString();
QString content = query.value("content").toString();
qDebug() << "Search result:" << title << content.left(50) + "...";
}
}
}
五、性能优化策略
1. 批量操作优化
cpp
void batchInsertOptimized(QSqlDatabase &db, const QList<QVariantList> &data) {
db.transaction();
QSqlQuery query(db);
query.prepare("INSERT INTO large_table (col1, col2, col3) VALUES (:val1, :val2, :val3)");
// 使用预处理语句和事务进行批量插入
for (const auto &row : data) {
query.bindValue(":val1", row[0]);
query.bindValue(":val2", row[1]);
query.bindValue(":val3", row[2]);
query.exec();
}
db.commit();
}
2. 内存管理优化
cpp
void optimizeMemoryUsage(QSqlDatabase &db) {
QSqlQuery query(db);
// 设置页面大小
query.exec("PRAGMA page_size = 4096");
// 设置缓存大小(以页面为单位)
query.exec("PRAGMA cache_size = -2000"); // 负值表示 KiB,此处为 2000 KiB
// 优化写入性能
query.exec("PRAGMA synchronous = NORMAL");
// 启用 WAL 模式
query.exec("PRAGMA journal_mode = WAL");
}
六、错误处理与恢复
1. 错误处理机制
cpp
bool executeSafely(QSqlQuery &query, const QString &sql, const QVariantMap &bindValues = {}) {
query.prepare(sql);
// 绑定参数
for (auto it = bindValues.begin(); it != bindValues.end(); ++it) {
query.bindValue(it.key(), it.value());
}
if (!query.exec()) {
qDebug() << "SQL execution error:" << query.lastError().text();
qDebug() << "Failed query:" << sql;
return false;
}
return true;
}
2. 数据库恢复
cpp
bool backupDatabase(const QString &sourceDb, const QString &targetDb) {
QSqlDatabase srcDb = QSqlDatabase::addDatabase("QSQLITE", "sourceConnection");
srcDb.setDatabaseName(sourceDb);
if (!srcDb.open()) {
qDebug() << "Cannot open source database:" << srcDb.lastError().text();
return false;
}
QSqlDatabase destDb = QSqlDatabase::addDatabase("QSQLITE", "targetConnection");
destDb.setDatabaseName(targetDb);
if (!destDb.open()) {
qDebug() << "Cannot open target database:" << destDb.lastError().text();
srcDb.close();
return false;
}
// 使用 SQLite 的备份 API
QSqlQuery query(srcDb);
query.exec("BACKUP TO '" + targetDb + "'");
srcDb.close();
destDb.close();
return true;
}
七、ORM 框架集成
1. 简单 ORM 实现
cpp
class Note {
public:
int id;
QString title;
QString content;
QDateTime created;
// 从记录创建对象
static Note fromRecord(const QSqlRecord &record) {
Note note;
note.id = record.value("id").toInt();
note.title = record.value("title").toString();
note.content = record.value("content").toString();
note.created = record.value("created").toDateTime();
return note;
}
// 保存到数据库
bool save(QSqlDatabase &db) {
QSqlQuery query(db);
if (id <= 0) {
// 插入新记录
query.prepare("INSERT INTO notes (title, content, created) VALUES (:title, :content, :created)");
query.bindValue(":title", title);
query.bindValue(":content", content);
query.bindValue(":created", created.isValid() ? created : QDateTime::currentDateTime());
if (query.exec()) {
id = query.lastInsertId().toInt();
return true;
}
} else {
// 更新现有记录
query.prepare("UPDATE notes SET title = :title, content = :content WHERE id = :id");
query.bindValue(":title", title);
query.bindValue(":content", content);
query.bindValue(":id", id);
return query.exec();
}
return false;
}
};
八、实际应用案例
1. 嵌入式设备数据记录
cpp
class DataLogger {
public:
DataLogger(const QString &dbPath) {
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(dbPath);
if (!db.open()) {
qDebug() << "Failed to open database:" << db.lastError().text();
} else {
createTables();
}
}
void logSensorData(const QString &sensorId, double value) {
if (!db.isOpen()) return;
QSqlQuery query(db);
query.prepare("INSERT INTO sensor_data (sensor_id, value, timestamp) VALUES (:sensor_id, :value, :timestamp)");
query.bindValue(":sensor_id", sensorId);
query.bindValue(":value", value);
query.bindValue(":timestamp", QDateTime::currentDateTime());
query.exec();
}
private:
QSqlDatabase db;
void createTables() {
QSqlQuery query(db);
query.exec(R"(
CREATE TABLE IF NOT EXISTS sensor_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sensor_id TEXT NOT NULL,
value REAL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
)");
}
};
九、总结
Qt 与 SQLite 的结合为嵌入式数据库应用开发提供了强大而灵活的解决方案。SQLite 的轻量级特性与 Qt 的跨平台能力相得益彰,使开发者能够轻松构建高效、可靠的数据驱动应用。通过合理使用事务、索引、全文搜索等高级特性,并采用适当的性能优化策略,可以充分发挥 SQLite 在嵌入式环境中的优势。