QSqlQuery 是 Qt SQL 模块中最基础也最强大的数据库操作类,它提供了直接执行任意 SQL 语句的能力。与 QSqlTableModel 和 QSqlQueryModel 这类高级封装不同,QSqlQuery 更适合需要精细控制 SQL 操作的场景。
QSqlQuery 支持两种基本使用方式:
cpp复制// 方式一:直接执行SQL语句
QSqlQuery query;
query.exec("SELECT * FROM employee");
// 方式二:预处理语句(推荐)
QSqlQuery query;
query.prepare("INSERT INTO employee (name, age) VALUES (?, ?)");
query.addBindValue("张三");
query.addBindValue(25);
query.exec();
预处理语句的优势在于:
QSqlQuery 提供三种参数绑定方式:
cpp复制query.prepare("UPDATE employee SET dept=?, salary=? WHERE id=?");
query.addBindValue("技术部");
query.addBindValue(8000);
query.addBindValue(1001);
cpp复制query.prepare("SELECT * FROM employee WHERE dept=:dept AND salary>:salary");
query.bindValue(":dept", "市场部");
query.bindValue(":salary", 5000);
cpp复制query.prepare("CALL sp_employee_update(?, :salary)");
query.bindValue(0, 1001);
query.bindValue(":salary", 7500);
提示:对于需要多次执行的相同语句,预处理后只需改变绑定值即可重复执行,这在批量操作时能显著提升性能。
当执行 SELECT 语句后,可以通过以下方式遍历结果集:
cpp复制QSqlQuery query("SELECT id, name FROM employee");
while (query.next()) {
int id = query.value(0).toInt(); // 按列索引获取
QString name = query.value("name").toString(); // 按列名获取
qDebug() << id << name;
}
关键导航方法:
first()/last():移动到首/末记录next()/previous():前后移动seek(index):定位到指定位置处理大量数据时需要注意:
cpp复制QSqlQuery query;
query.setForwardOnly(true); // 设置为仅向前遍历
query.exec("SELECT * FROM large_table");
while (query.next()) {
// 处理数据...
}
设置 setForwardOnly(true) 可以:
cpp复制QSqlDatabase::database().transaction(); // 开始事务
QSqlQuery query;
query.prepare("UPDATE account SET balance=balance-? WHERE id=?");
query.addBindValue(amount);
query.addBindValue(fromId);
if (!query.exec()) {
QSqlDatabase::database().rollback();
return false;
}
// 更多操作...
QSqlDatabase::database().commit(); // 提交事务
cpp复制QSqlQuery query;
if (!query.exec("SELECT * FROM non_existent_table")) {
QSqlError error = query.lastError();
qDebug() << "Error type:" << error.type();
qDebug() << "Database text:" << error.databaseText();
qDebug() << "Driver text:" << error.driverText();
if (error.type() == QSqlError::ConnectionError) {
// 处理连接错误
}
}
常见错误类型:
NoError:操作成功ConnectionError:数据库连接问题StatementError:SQL语法错误TransactionError:事务处理错误cpp复制QSqlQuery query;
query.prepare("CALL sp_get_employee_by_dept(?, @total)");
query.bindValue(0, "研发部");
query.exec();
// 获取输出参数
query.exec("SELECT @total");
if (query.next()) {
int total = query.value(0).toInt();
qDebug() << "Total employees:" << total;
}
cpp复制// 插入BLOB数据
QPixmap photo("employee.jpg");
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
photo.save(&buffer, "JPEG");
QSqlQuery query;
query.prepare("UPDATE employee SET photo=? WHERE id=?");
query.addBindValue(ba, QSql::Binary);
query.addBindValue(1001);
query.exec();
// 读取BLOB数据
query.exec("SELECT photo FROM employee WHERE id=1001");
if (query.next()) {
QByteArray data = query.value(0).toByteArray();
QPixmap pixmap;
pixmap.loadFromData(data);
// 显示图片...
}
cpp复制QSqlDatabase::database().transaction();
QSqlQuery query;
query.prepare("INSERT INTO employee (name, dept) VALUES (?, ?)");
for (int i = 0; i < 1000; ++i) {
query.addBindValue(QString("员工%1").arg(i));
query.addBindValue("部门" + QString::number(i % 5));
query.exec();
}
QSqlDatabase::database().commit();
SELECT *LIMIT 分页cpp复制// 在程序启动时配置
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", "connection1");
db.setHostName("localhost");
db.setDatabaseName("testdb");
// 其他参数...
// 使用时获取连接
QSqlDatabase db = QSqlDatabase::database("connection1");
确保数据库连接使用正确的编码:
cpp复制QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
db.setConnectOptions("MYSQL_OPT_SET_CHARSET_NAME=utf8mb4");
// 或对于SQLite
db.exec("PRAGMA encoding = 'UTF-8'");
cpp复制// 设置连接超时
db.setConnectOptions("CONNECT_TIMEOUT=5");
// 重连机制
int retry = 0;
while (retry < 3) {
if (db.open()) break;
QThread::sleep(1);
retry++;
}
cpp复制// 线程安全的数据库连接获取
void WorkerThread::run() {
QSqlDatabase db = QSqlDatabase::database("connection_pool", false);
if (!db.isOpen()) {
db = QSqlDatabase::addDatabase("QMYSQL", "connection_pool");
// 配置连接...
db.open();
}
QSqlQuery query(db);
// 执行查询...
}
在最近的一个项目中,我们需要实现跨数据库迁移功能。核心代码如下:
cpp复制bool migrateData(QSqlDatabase &source, QSqlDatabase &target, const QString &table) {
QSqlQuery sourceQuery(source);
sourceQuery.exec(QString("SELECT * FROM %1").arg(table));
QSqlQuery targetQuery(target);
target.driver()->beginTransaction();
// 获取目标表结构
QSqlRecord targetRecord = target.record(table);
// 构建INSERT语句
QStringList fields;
for (int i = 0; i < targetRecord.count(); ++i) {
fields << targetRecord.fieldName(i);
}
QString insertSql = QString("INSERT INTO %1 (%2) VALUES (%3)")
.arg(table)
.arg(fields.join(","))
.arg(QString("?").repeated(fields.size()).split("").join(","));
targetQuery.prepare(insertSql);
int batchSize = 0;
while (sourceQuery.next()) {
for (int i = 0; i < targetRecord.count(); ++i) {
targetQuery.addBindValue(sourceQuery.value(i));
}
if (!targetQuery.exec()) {
target.driver()->rollbackTransaction();
return false;
}
// 分批提交
if (++batchSize % 100 == 0) {
target.driver()->commitTransaction();
target.driver()->beginTransaction();
}
}
target.driver()->commitTransaction();
return true;
}
在需要构建动态查询时,可以采用以下模式:
cpp复制QString buildDynamicQuery(const QString &table,
const QMap<QString, QVariant> &filters) {
QStringList whereClauses;
QList<QVariant> bindValues;
for (auto it = filters.constBegin(); it != filters.constEnd(); ++it) {
whereClauses << QString("%1 = ?").arg(it.key());
bindValues << it.value();
}
QString sql = QString("SELECT * FROM %1").arg(table);
if (!whereClauses.isEmpty()) {
sql += " WHERE " + whereClauses.join(" AND ");
}
QSqlQuery query;
query.prepare(sql);
for (const QVariant &value : bindValues) {
query.addBindValue(value);
}
return sql;
}
可以通过以下方式监控SQL性能:
cpp复制class QueryProfiler {
public:
QueryProfiler(const QString &sql) : m_sql(sql) {
m_timer.start();
}
~QueryProfiler() {
qDebug() << "Query:" << m_sql
<< "took" << m_timer.elapsed() << "ms";
}
private:
QElapsedTimer m_timer;
QString m_sql;
};
// 使用示例
QSqlQuery query;
{
QueryProfiler profiler("SELECT * FROM large_table");
query.exec("SELECT * FROM large_table");
}
不同数据库类型系统存在差异,需要特别注意:
| 数据类型 | MySQL | SQLite | PostgreSQL | Oracle |
|---|---|---|---|---|
| 整数 | INT | INTEGER | INTEGER | NUMBER |
| 字符串 | VARCHAR | TEXT | VARCHAR | VARCHAR2 |
| 日期时间 | DATETIME | TEXT | TIMESTAMP | DATE |
| 二进制 | BLOB | BLOB | BYTEA | BLOB |
处理建议:
cpp复制// 使用标准类型名或检测数据库类型
QString typeName;
if (db.driverName().contains("MYSQL")) {
typeName = "DATETIME";
} else {
typeName = "TIMESTAMP";
}
不同数据库的分页语法:
cpp复制QString buildPagedQuery(const QString &baseSql, int page, int pageSize,
const QSqlDatabase &db) {
if (db.driverName().contains("MYSQL")) {
return QString("%1 LIMIT %2 OFFSET %3")
.arg(baseSql)
.arg(pageSize)
.arg((page - 1) * pageSize);
} else if (db.driverName().contains("SQLITE")) {
// 同MySQL
} else if (db.driverName().contains("PSQL")) {
return QString("%1 LIMIT %2 OFFSET %3")
.arg(baseSql)
.arg(pageSize)
.arg((page - 1) * pageSize);
} else if (db.driverName().contains("OCI")) { // Oracle
return QString("SELECT * FROM (SELECT a.*, ROWNUM rn FROM (%1) a "
"WHERE ROWNUM <= %2) WHERE rn > %3")
.arg(baseSql)
.arg(page * pageSize)
.arg((page - 1) * pageSize);
}
return baseSql;
}
除了使用参数化查询外,还需注意:
cpp复制QString sanitizeIdentifier(const QString &input) {
static QRegularExpression re("^[a-zA-Z_][a-zA-Z0-9_]*$");
if (!re.match(input).hasMatch()) {
throw std::runtime_error("Invalid identifier");
}
return input;
}
cpp复制// 加密存储敏感数据
QString encryptData(const QString &data, const QByteArray &key) {
// 使用AES等加密算法实现
// ...
}
// 在查询中使用
QSqlQuery query;
query.prepare("INSERT INTO users (username, password) VALUES (?, ?)");
query.addBindValue(username);
query.addBindValue(encryptData(password, appKey));
query.exec();
cpp复制// 自定义QSqlQuery子类记录所有SQL
class LoggingSqlQuery : public QSqlQuery {
public:
LoggingSqlQuery(QSqlDatabase db) : QSqlQuery(db) {}
bool exec(const QString &query) override {
qDebug() << "Executing SQL:" << query;
QElapsedTimer timer;
timer.start();
bool result = QSqlQuery::exec(query);
qDebug() << "Execution time:" << timer.elapsed() << "ms";
return result;
}
bool exec() override {
qDebug() << "Executing prepared SQL:" << lastQuery();
QElapsedTimer timer;
timer.start();
bool result = QSqlQuery::exec();
qDebug() << "Execution time:" << timer.elapsed() << "ms";
return result;
}
};
cpp复制void logDatabaseOperation(const QString &operation,
const QString &table,
const QVariantMap &data) {
QFile logFile("db_operations.log");
if (logFile.open(QIODevice::Append)) {
QTextStream stream(&logFile);
stream << QDateTime::currentDateTime().toString(Qt::ISODate)
<< " | " << operation
<< " | " << table
<< " | " << QJsonDocument::fromVariant(data).toJson()
<< "\n";
}
}
// 使用示例
QVariantMap data;
data.insert("id", 1001);
data.insert("name", "张三");
logDatabaseOperation("UPDATE", "employee", data);
在实际项目开发中,我发现将 QSqlQuery 与这些最佳实践结合使用,可以构建出既高效又安全的数据库访问层。特别是在处理复杂业务逻辑时,直接使用 QSqlQuery 比 ORM 框架提供了更大的灵活性。