Qt里有很多类和函数我也不知道,只能利用好Qt Assistant当场查找。
实现 TaskModel 类
TaskModel 类继承自 QSqlTableModel,用于处理与数据库中任务表的交互,以及对任务数据的显示和操作。
头文件
class TaskModel : public QSqlTableModel
{
Q_OBJECT
public:
// 列索引枚举类型,是模型中表的列名
enum Columns
{
IdColumn = 0,
TitleColumn,
DescriptionColumn,
PriorityColumn,
DueDateColumn,
CompletedColumn,
CreatedAtColumn
};
explicit TaskModel(QObject* parent = nullptr);
// 自定义数据显示
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
// 任务操作
bool addTask(const Task& task);
bool updateTask(const Task& task);
bool deleteTask(int taskId);
QList<Task> getAllTasks() const;
// 过滤方法
void setFilterByStatus(bool showCompleted, bool showOverdue);
void setSearchText(const QString& text);
};
源文件
TaskModel::TaskModel(QObject* parent) : QSqlTableModel(parent)
{
setTable("tasks");
setEditStrategy(QSqlTableModel::OnManualSubmit);
select(); //更新模型里的数据
}
QVariant TaskModel::data(const QModelIndex& index, int role) const
{
if (role == Qt::DisplayRole)
{
if (index.column() == PriorityColumn)
{
int priority = QSqlTableModel::data(index, role).toInt();
switch (priority)
{
case Task::Low: return "Low";
case Task::Medium: return "Medium";
case Task::High: return "High";
default: return "";
}
}
}
return QSqlTableModel::data(index, role);
}
bool TaskModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
return QSqlTableModel::setData(index, value, role);
}
bool TaskModel::addTask(const Task& task)
{
QSqlQuery query; // Ensure QSqlQuery is fully included and initialized
// Prepare the SQL query to insert a new task
query.prepare("INSERT INTO tasks (title, description, priority, due_date, completed) "
"VALUES (:title, :description, :priority, :due_date, :completed)");
// Bind the values to the query parameters
query.bindValue(":title", task.title());
query.bindValue(":description", task.description());
query.bindValue(":priority", task.priority());
query.bindValue(":due_date", task.dueDate().toString("yyyy-MM-dd"));
query.bindValue(":completed", task.completed());
if (query.exec())
{
select();
return true;
}
else
{
qDebug() << "Failed to add task:" << query.lastError().text();
return false;
}
}
bool TaskModel::updateTask(const Task& task)
{
QSqlQuery query; // Ensure QSqlQuery is fully included and initialized
query.prepare("UPDATE tasks SET title = :title, description = :description, "
"priority = :priority, due_date = :due_date, completed = :completed "
"WHERE id = :id");
query.bindValue(":id", task.id());
query.bindValue(":title", task.title());
query.bindValue(":description", task.description());
query.bindValue(":priority", task.priority());
query.bindValue(":due_date", task.dueDate().toString("yyyy-MM-dd"));
query.bindValue(":completed", task.completed());
if (query.exec())
{
select();
return true;
}
else
{
qDebug() << "Failed to update task:" << query.lastError().text();
return false;
}
}
bool TaskModel::deleteTask(int taskId)
{
QSqlQuery query; // Ensure QSqlQuery is fully included and initialized
query.prepare("DELETE FROM tasks WHERE id = :id");
query.bindValue(":id", taskId);
if (query.exec())
{
select();
return true;
}
else
{
qDebug() << "Failed to delete task:" << query.lastError().text();
return false;
}
}
QList<Task> TaskModel::getAllTasks() const
{
QList<Task> tasks;
for (int i = 0; i < rowCount(); ++i)
{
int id = data(index(i, IdColumn)).toInt();
QString title = data(index(i, TitleColumn)).toString();
QString description = data(index(i, DescriptionColumn)).toString();
int priority = data(index(i, PriorityColumn)).toInt();
QDate dueDate = QDate::fromString(data(index(i, DueDateColumn)).toString(), "yyyy-MM-dd");
bool completed = data(index(i, CompletedColumn)).toBool();
tasks.append(Task(id, title, description, static_cast<Task::Priority>(priority), dueDate, completed));
}
return tasks;
}
void TaskModel::setFilterByStatus(bool showCompleted, bool showOverdue)
{
QString filter;
if (!showCompleted)
{
filter += "completed = 0";
}
if (showOverdue)
{
if (!filter.isEmpty())
{
filter += " AND ";
}
filter += "due_date < CURDATE() AND completed = 0";
}
setFilter(filter);
select();
}
void TaskModel::setSearchText(const QString& text)
{
QString filter = QString("title LIKE '%%1%' OR description LIKE '%%1%'").arg(text);
setFilter(filter);
select();
}
补充
QSqlTableModel
是 Qt 框架中用于数据库操作的模型类,它继承自 QAbstractTableModel
,专为表格数据与 SQL 数据库之间的交互设计。以下是它与普通 QAbstractTableModel
(或其子类)的主要区别及使用方法:
QSqlTableModel介绍
数据来源
- QSqlTableModel:直接连接数据库表,数据自动从 SQL 查询获取。
- 普通 TableModel:需要手动管理内存中的数据(如
QList
、QVector
)。
数据操作
- QSqlTableModel:支持数据库级的增删改查(CRUD),通过
insertRow()
、setData()
、removeRow()
等方法自动生成 SQL 语句。 - 普通 TableModel:需手动实现数据操作逻辑,如在内存中添加/删除项,并手动触发数据变更信号(如
dataChanged()
)。
- QSqlTableModel:支持数据库级的增删改查(CRUD),通过
性能
- QSqlTableModel:适合中小型数据集,大数据量时可能因频繁数据库查询导致性能下降。
- 普通 TableModel:适合完全在内存中处理的数据,性能取决于实现方式。
同步机制
- QSqlTableModel:支持自动或手动提交更改到数据库(通过
submitAll()
、revertAll()
)。 - 普通 TableModel:无内置同步机制,需自行实现与外部数据源的同步。
- QSqlTableModel:支持自动或手动提交更改到数据库(通过
QSqlTableModel 使用方法
1. 基本初始化
#include <QSqlTableModel>
#include <QTableView>
// 创建模型并连接到数据库表
QSqlTableModel *model = new QSqlTableModel(this);
model->setTable("tasks"); // 指定数据库表名
model->setEditStrategy(QSqlTableModel::OnManualSubmit); // 设置编辑策略
model->select(); // 执行 SQL 查询,加载数据
// 将模型应用到视图
QTableView *view = new QTableView;
view->setModel(model);
view->show();
2. 编辑策略
控制数据何时提交到数据库:
QSqlTableModel::OnFieldChange
:每次单元格修改立即提交。QSqlTableModel::OnRowChange
:切换行时提交当前行的修改。QSqlTableModel::OnManualSubmit
:手动调用submitAll()
提交所有更改。
3. 数据操作
// 添加新行
model->insertRow(model->rowCount());
model->setData(model->index(model->rowCount()-1, 0), "New Task"); // 设置数据
model->submitAll(); // 提交更改到数据库
// 修改数据
model->setData(model->index(0, 1), "Updated Description");
model->submitAll();
// 删除选中行
QModelIndexList selected = view->selectionModel()->selectedRows();
for (int i = 0; i < selected.size(); ++i) {
model->removeRow(selected.at(i).row());
}
model->submitAll();
4. 过滤与排序
// 过滤数据(相当于 SQL WHERE 子句)
model->setFilter("completed = 1"); // 只显示已完成的任务
model->select();
// 排序数据(相当于 SQL ORDER BY)
model->setSort(2, Qt::AscendingOrder); // 按第3列升序排序
model->select();
5. 高级用法
- 自定义数据显示:重写
data()
方法,自定义单元格显示格式。 - 关联多表:使用
QSqlRelationalTableModel
处理外键关联。 - 事务处理:结合
QSqlDatabase::transaction()
和submitAll()
确保数据一致性。
何时选择 QSqlTableModel?
- 当数据主要存储在数据库中,且需要直接操作数据库表时。
- 当需要快速实现数据库与界面的双向同步时。
- 当业务逻辑简单,无需复杂的数据处理时。
何时选择普通 TableModel?
- 当数据来源非数据库(如网络、文件)时。
- 当需要复杂的数据处理或自定义逻辑时。
- 当性能是关键因素,需要优化内存使用时。
总结
QSqlTableModel
是数据库操作的便捷工具,适合快速开发数据库应用;而普通 TableModel
提供更高的灵活性,适合复杂场景。根据项目需求选择合适的模型,可大幅提高开发效率。
在 Qt 的模型/视图(Model/View)架构中,QAbstractItemModel::data()
是核心虚函数,负责为视图提供指定索引(QModelIndex
)和角色(Qt::ItemDataRole
)的数据。它是模型与视图通信的桥梁,决定了数据如何在界面上显示和交互。
data函数
一、函数原型与参数
virtual QVariant QAbstractItemModel::data(const QModelIndex &index, int role = Qt::DisplayRole) const;
- 参数:
index
:指定数据项的位置(行、列、父项)。role
:指定数据的用途(如显示文本、编辑文本、图标等)。
- 返回值:
QVariant
:存储任意类型的数据(需与角色类型匹配),无效数据返回QVariant()
。
二、核心作用
数据复用:同一数据项可根据不同角色提供不同形式的数据。
例如:DisplayRole
:显示为"$100.00"
(字符串)。UserRole
:存储为100.0
(浮点数,用于计算)。
视图渲染控制:视图根据角色获取数据并渲染。
例如:DecorationRole
的QIcon
显示在项前。FontRole
的QFont
决定文本字体。
交互逻辑分离:
- 编辑操作(如
QLineEdit
)使用EditRole
的数据,而非DisplayRole
,避免格式干扰。
- 编辑操作(如
三、常用角色与返回值类型
角色 | 用途 | 常见返回类型 |
---|---|---|
Qt::DisplayRole |
显示文本 | QString |
Qt::EditRole |
编辑文本 | QString |
Qt::DecorationRole |
图标/图像 | QIcon , QPixmap , QColor |
Qt::ToolTipRole |
鼠标悬停提示 | QString |
Qt::FontRole |
字体 | QFont |
Qt::TextAlignmentRole |
文本对齐方式 | Qt::AlignmentFlag |
Qt::BackgroundColorRole |
背景颜色 | QBrush |
Qt::ForegroundRole |
前景颜色(文本颜色) | QBrush |
Qt::CheckStateRole |
复选框状态 | Qt::CheckState |
Qt::UserRole |
用户自定义数据 | 任意类型(如 int , QDate ) |
四、实现示例
1. 简单表格模型(QAbstractTableModel
子类)
class MyTableModel : public QAbstractTableModel {
public:
// 必须实现的三个虚函数
int rowCount(const QModelIndex &parent = QModelIndex()) const override {
return dataList.size();
}
int columnCount(const QModelIndex &parent = QModelIndex()) const override {
return 3; // 3列
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
if (!index.isValid() || index.row() >= dataList.size())
return QVariant();
const auto &item = dataList[index.row()];
switch (role) {
case Qt::DisplayRole:
case Qt::EditRole:
switch (index.column()) {
case 0: return item.name; // 第1列:名称
case 1: return item.age; // 第2列:年龄
case 2: return item.score; // 第3列:分数
default: return QVariant();
}
case Qt::ForegroundRole:
if (item.score < 60)
return QBrush(Qt::red); // 分数不及格:红色文本
return QBrush(Qt::black);
case Qt::ToolTipRole:
return QString("Name: %1\nAge: %2\nScore: %3")
.arg(item.name).arg(item.age).arg(item.score);
case Qt::UserRole:
return QVariant::fromValue(item); // 存储完整数据对象
default:
return QVariant();
}
}
private:
struct Item {
QString name;
int age;
double score;
};
QList<Item> dataList;
};
2. 树形模型(QAbstractItemModel
子类)
QVariant MyTreeModel::data(const QModelIndex &index, int role) const {
if (!index.isValid())
return QVariant();
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
switch (role) {
case Qt::DisplayRole:
return item->data(index.column()); // 返回节点数据
case Qt::DecorationRole:
if (index.column() == 0) { // 第1列显示图标
if (item->hasChildren())
return folderIcon;
else
return fileIcon;
}
return QVariant();
case Qt::CheckStateRole:
if (index.column() == 0) { // 第1列支持复选框
return item->isChecked() ? Qt::Checked : Qt::Unchecked;
}
return QVariant();
default:
return QVariant();
}
}
五、使用技巧
角色组合:
- 同一索引可针对不同角色返回不同数据。例如:
if (role == Qt::DisplayRole) return QString::number(value); else if (role == Qt::EditRole) return value; // 编辑时返回原始数值,而非字符串
- 同一索引可针对不同角色返回不同数据。例如:
自定义角色:
- 使用
Qt::UserRole + n
定义自定义角色,存储特殊数据(如对象指针、ID)。enum CustomRoles { SortRole = Qt::UserRole + 1, // 用于排序的数值 IdRole = Qt::UserRole + 2 // 存储唯一ID }; // 在data()中实现 case SortRole: return item.sortValue; case IdRole: return item.id;
- 使用
性能优化:
- 避免在
data()
中执行耗时操作(如文件读取、网络请求)。 - 缓存频繁访问的数据,减少重复计算。
- 避免在
六、常见问题
视图不显示数据:
- 检查
data()
是否为Qt::DisplayRole
返回了有效数据。 - 确保
rowCount()
和columnCount()
返回正确值。
- 检查
编辑异常:
- 若编辑框显示异常,检查
EditRole
是否与DisplayRole
兼容。
- 若编辑框显示异常,检查
图标/颜色不生效:
- 确保
DecorationRole
返回QIcon
或QPixmap
,ForegroundRole
返回QBrush
。
- 确保
总结
data()
函数是 Qt 模型的核心,通过角色机制实现了数据的多维度表达。理解并正确实现它,是开发灵活、高效 Qt 应用的关键。
QSqlQuery
是 Qt 框架中用于执行 SQL 语句和处理查询结果的核心类。它支持多种数据库操作,包括查询、插入、更新和删除等。以下是 QSqlQuery
的详细用法和最佳实践:
QSqlQuery
一、基本使用流程
1. 初始化与执行 SQL
// 假设已创建并打开数据库连接(如使用 QSqlDatabase::addDatabase())
QSqlQuery query; // 默认使用默认数据库连接
// 执行简单查询
bool success = query.exec("SELECT * FROM users");
if (!success) {
qDebug() << "Query error:" << query.lastError().text();
}
2. 遍历查询结果
while (query.next()) { // 逐行移动光标
QString name = query.value(0).toString(); // 第1列数据(索引0)
int age = query.value(1).toInt(); // 第2列数据
qDebug() << "Name:" << name << "Age:" << age;
}
3. 使用参数化查询(防止 SQL 注入)
// 方式1:命名参数
query.prepare("INSERT INTO users (name, age) VALUES (:name, :age)");
query.bindValue(":name", "Alice");
query.bindValue(":age", 30);
query.exec();
// 方式2:位置参数(使用 ? 占位符)
query.prepare("INSERT INTO users (name, age) VALUES (?, ?)");
query.addBindValue("Bob");
query.addBindValue(25);
query.exec();
二、高级功能
1. 批量插入数据
// 使用 ODBC 风格的批量插入(高效处理大量数据)
query.prepare("INSERT INTO users (name, age) VALUES (?, ?)");
QVariantList names;
names << "Alice" << "Bob" << "Charlie";
QVariantList ages;
ages << 30 << 25 << 35;
query.addBindValue(names);
query.addBindValue(ages);
if (!query.execBatch()) {
qDebug() << "Batch error:" << query.lastError().text();
}
2. 执行 DDL 语句(创建表、索引等)
query.exec(R"(
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT,
priority INTEGER DEFAULT 1,
due_date DATE,
completed BOOLEAN DEFAULT 0
)
)");
3. 获取查询结果元数据
if (query.exec("SELECT * FROM tasks")) {
QSqlRecord record = query.record();
int columnCount = record.count();
for (int i = 0; i < columnCount; ++i) {
qDebug() << "Column" << i << "name:" << record.fieldName(i);
qDebug() << "Column" << i << "type:" << record.field(i).typeName();
}
}
三、错误处理
if (!query.exec("SELECT * FROM non_existent_table")) {
QSqlError error = query.lastError();
qDebug() << "Error:" << error.text();
qDebug() << "SQL State:" << error.sqlState();
qDebug() << "Database Text:" << error.databaseText();
}
四、事务处理
QSqlDatabase db = QSqlDatabase::database();
db.transaction(); // 开始事务
QSqlQuery query;
query.exec("INSERT INTO logs (message) VALUES ('Transaction started')");
if (/* 条件判断 */) {
db.commit(); // 提交事务
} else {
db.rollback(); // 回滚事务
}
五、在 TODO 系统中的应用示例
1. 添加任务
bool TodoManager::addTask(const QString &title, const QString &description, int priority, const QDate &dueDate) {
QSqlQuery query;
query.prepare("INSERT INTO tasks (title, description, priority, due_date) "
"VALUES (:title, :desc, :priority, :dueDate)");
query.bindValue(":title", title);
query.bindValue(":desc", description);
query.bindValue(":priority", priority);
query.bindValue(":dueDate", dueDate);
return query.exec();
}
2. 查询所有任务
QList<Task> TodoManager::getAllTasks() {
QList<Task> tasks;
QSqlQuery query("SELECT * FROM tasks ORDER BY due_date");
while (query.next()) {
Task task;
task.id = query.value("id").toInt();
task.title = query.value("title").toString();
task.description = query.value("description").toString();
task.priority = query.value("priority").toInt();
task.dueDate = query.value("due_date").toDate();
task.completed = query.value("completed").toBool();
tasks.append(task);
}
return tasks;
}
3. 更新任务状态
bool TodoManager::markTaskCompleted(int taskId, bool completed) {
QSqlQuery query;
query.prepare("UPDATE tasks SET completed = :completed WHERE id = :id");
query.bindValue(":completed", completed);
query.bindValue(":id", taskId);
return query.exec();
}
六、性能优化建议
使用预编译语句(Prepared Statements)
避免重复解析 SQL 语句,提高批量操作效率:query.prepare("INSERT INTO items (name, price) VALUES (?, ?)"); for (const auto &item : itemsList) { query.addBindValue(item.name); query.addBindValue(item.price); query.exec(); }
批量提交数据
在事务中执行多个操作,减少数据库提交次数:db.transaction(); // 执行多个INSERT/UPDATE db.commit();
使用索引
对查询频繁的字段(如日期、优先级)创建索引:CREATE INDEX idx_due_date ON tasks (due_date);
七、注意事项
数据类型转换
QSqlQuery::value()
返回QVariant
,需正确转换类型:QDate date = query.value("due_date").toDate(); // 正确 QDate date = query.value("due_date").toString(); // 错误!
跨平台兼容性
不同数据库(如 SQLite、MySQL、PostgreSQL)对 SQL 语法支持略有差异,需注意兼容性。资源管理
QSqlQuery
对象析构时会自动释放相关资源,无需手动关闭。
总结
QSqlQuery
是 Qt 数据库编程的核心工具,通过以下步骤可高效使用:
- 执行 SQL 语句(
exec()
/prepare()
) - 处理查询结果(
next()
/value()
) - 绑定参数(
bindValue()
/addBindValue()
) - 错误处理(
lastError()
) - 事务管理(
transaction()
/commit()
/rollback()
)
结合参数化查询和事务处理,可确保数据操作的安全性和高效性。在你的 TODO 系统中,QSqlQuery
将是连接 UI 与数据库的关键桥梁。
QSqlQuery::bindValue()
是 Qt 中用于参数化查询的核心函数,它通过将 SQL 语句中的占位符与实际值绑定,实现安全高效的数据查询与操作。以下是对该函数的详细解析:
QSqlQuery::bindvalue函数
一、函数原型与参数
void QSqlQuery::bindValue(const QString &placeholder, const QVariant &val, QSql::ParamType paramType = QSql::In);
- 参数:
placeholder
:SQL 语句中的占位符名称(如:name
、?
)。val
:要绑定的值,使用QVariant
支持任意数据类型。paramType
:参数类型(可选),默认为QSql::In
(输入参数)。QSql::In
:输入参数(用于查询条件)。QSql::Out
:输出参数(用于存储存储过程的返回值)。QSql::InOut
:输入输出参数。QSql::Binary
:二进制数据(如图片、文件)。
二、核心作用与优势
1. 防止 SQL 注入攻击
通过参数化查询,数据库会自动处理值中的特殊字符,避免恶意注入:
// 不安全的拼接(存在 SQL 注入风险)
QString name = userInput; // 假设用户输入 "Robert'; DROP TABLE users; --"
query.exec("SELECT * FROM users WHERE name = '" + name + "'");
// 安全的参数化查询
query.prepare("SELECT * FROM users WHERE name = :name");
query.bindValue(":name", userInput); // 自动转义特殊字符
query.exec();
2. 自动类型转换
QVariant
会自动处理数据类型,无需手动格式化:
// 绑定日期类型
QDate dueDate = QDate::currentDate();
query.prepare("INSERT INTO tasks (title, due_date) VALUES (:title, :date)");
query.bindValue(":title", "Buy groceries");
query.bindValue(":date", dueDate); // 自动转换为数据库支持的日期格式
3. 提高查询性能
预编译 SQL 语句后重复使用,避免数据库重复解析相同结构的 SQL:
query.prepare("INSERT INTO logs (message, timestamp) VALUES (:msg, :time)");
for (const auto &msg : messages) {
query.bindValue(":msg", msg);
query.bindValue(":time", QDateTime::currentDateTime());
query.exec(); // 仅参数变化,SQL 结构不变
}
三、使用示例
1. 命名占位符(推荐方式)
// SQL 语句中使用 :name 形式的占位符
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");
query.exec();
2. 位置占位符(使用 ?)
// SQL 语句中使用 ? 占位符
query.prepare("UPDATE tasks SET title = ?, completed = ? WHERE id = ?");
query.bindValue(0, "Revise report"); // 第1个 ?
query.bindValue(1, true); // 第2个 ?
query.bindValue(2, 5); // 第3个 ?
query.exec();
3. 处理 NULL 值
// 绑定可能为 NULL 的值
QString optionalDescription; // 可能为空
query.prepare("INSERT INTO notes (content, attachment) VALUES (:content, :attach)");
query.bindValue(":content", "Meeting minutes");
query.bindValue(":attach", optionalDescription.isNull() ? QVariant(QVariant::String) : optionalDescription);
4. 存储过程中的输出参数
// 调用存储过程获取用户数量
query.prepare("CALL GetUserCount(:count)");
query.bindValue(":count", 0, QSql::Out); // 输出参数
query.exec();
int userCount = query.boundValue(":count").toInt();
四、在 TODO 系统中的应用
1. 添加新任务
bool TodoModel::addTask(const Task &task) {
QSqlQuery query;
query.prepare("INSERT INTO tasks (title, description, priority, due_date, completed) "
"VALUES (:title, :desc, :priority, :dueDate, :completed)");
query.bindValue(":title", task.title);
query.bindValue(":desc", task.description);
query.bindValue(":priority", static_cast<int>(task.priority)); // 枚举转 int
query.bindValue(":dueDate", task.dueDate);
query.bindValue(":completed", task.completed);
return query.exec();
}
2. 查询带条件的任务
QList<Task> TodoModel::searchTasks(const QString &keyword, bool showCompleted) {
QList<Task> results;
QSqlQuery query;
query.prepare("SELECT * FROM tasks "
"WHERE title LIKE :keyword "
"AND (completed = :completed OR :showAll = 1)");
query.bindValue(":keyword", "%" + keyword + "%");
query.bindValue(":completed", showCompleted);
query.bindValue(":showAll", showCompleted ? 1 : 0);
if (query.exec()) {
// 解析结果...
}
return results;
}
五、注意事项
占位符语法差异:
- 命名占位符(
:name
):适用于大多数数据库(如 SQLite、PostgreSQL)。 - 位置占位符(
?
):适用于所有数据库,但需严格按顺序绑定值。
- 命名占位符(
预编译与执行分离:
// 正确流程:先 prepare,再 bindValue,最后 exec query.prepare("SELECT * FROM users WHERE age > :min"); query.bindValue(":min", 18); query.exec();
二进制数据处理:
QByteArray imageData; // 图片数据 query.bindValue(":image", imageData, QSql::Binary); // 指定二进制类型
多次执行同一查询:
query.prepare("INSERT INTO items (name) VALUES (:name)"); for (const auto &name : itemNames) { query.bindValue(":name", name); query.exec(); // 每次执行前重新绑定值 query.finish(); // 可选:清除当前结果集 }
六、常见问题与解决方案
问题 | 原因分析 | 解决方案 |
---|---|---|
查询失败但无明确错误 | 占位符名称与 bindValue() 不匹配2. 数据类型不兼容 |
1. 检查占位符拼写和数量 2. 使用 QVariant::type() 确认类型兼容性 |
中文或特殊字符乱码 | 未正确设置数据库连接编码 | 在连接数据库前设置编码:db.setConnectOptions("MYSQL_OPT_CHARSET_NAME=utf8") |
存储过程输出参数无值 | 1. 未正确标记为 QSql::Out 2. 未在 exec() 后获取值 |
1. 确保参数类型正确 2. 通过 boundValue() 获取输出值 |
总结
bindValue()
是 Qt 数据库编程中确保安全性和性能的关键函数,主要应用场景包括:
- 防止 SQL 注入(优先使用命名占位符)。
- 处理复杂数据类型(如日期、二进制)。
- 优化重复查询(预编译 SQL)。
- 存储过程参数传递。
在 TODO 系统中,建议对所有涉及用户输入的 SQL 语句(如搜索、添加任务)都使用 bindValue()
,以避免安全风险并提升代码健壮性。