Linux下Qt连接MySQL:QMYSQL驱动编译与避坑指南

发布时间:2026/10/11 15:48:21

Linux下Qt连接MySQL:QMYSQL驱动编译与避坑指南 简介面向 Linux 平台 Qt 开发者的 MySQL 连接实战文档聚焦 Ubuntu 环境下的驱动编译与项目集成难题。文档先说明安装 libmysqlclient-dev 客户端再演示进入 Qt 源码 sqldrivers/mysql 目录生成 mysql.pro、用 qmake 指定 /usr/include/mysql 头文件及 /usr/lib/mysql 库路径并完成编译安装随后给出 QSqlDatabase 建立连接、QSqlQuery 查询 t_homedata 表并输出的可运行代码以及 .pro 文件中数据库驱动路径的配置方法。压缩包共 1 个 doc 文档大小 142KB内容紧凑且步骤完整适合刚接触 Qt SQL 模块、需要快速配置 MySQL 驱动的初中级开发者查漏补缺。该资料已有 635 人学习具备较强实践参考价值。1. 先说结论Linux 下 Qt 连 MySQL代码不是难点把 QMYSQL 驱动编出来才是不少人第一次在 Ubuntu 上做 Qt MySQL程序一跑就碰到QSqlDatabase: QMYSQL driver not loaded第一反应是检查连接参数、检查 SQL 语句折腾半天发现什么都没错其实是 Qt 根本没加载到 MySQL 驱动插件。Windows 下的 Qt 安装包通常会顺手带上 qsqlmysql.dll但 Linux 下的 Qt 二进制包默认只带 SQLite 驱动MySQL 驱动需要你自己对着源码编一遍。这篇文章就沿着这条主线讲清楚环境怎么选型、依赖装哪些、驱动怎么编、连接参数怎么设、故障从哪查。适合正在做 Linux 桌面应用数据管理、需要把采集或业务数据落进数据库的开发者新手可以完整跟下来熟手可以直接跳到第 3 章看编译命令或者去第 5 章对照踩坑记录。2. 环境准备版本选型、MySQL 安装、Qt 插件路径确认2.1 版本组合怎么选Ubuntu 20.04 配 Qt 5.15 是最省事的组合我先给结论如果不是在维护老项目推荐直接固定在 Qt 5.15.2 MySQL 8.0 或 5.7 Ubuntu 20.04/22.04 这套组合。Qt 6 里 MySQL 驱动的编译方式从 qmake 换成了 CMake插件源码的位置和构建脚本都不一样新手第一次编很容易在 configure 阶段就翻车。我一般让新项目锁 Qt 5.15.2 LTSQt 6 留给确实需要新特性的模块再说。MySQL 版本方面5.7 和 8.0 都能连但 8.0 默认的认证插件是 caching_sha2_passwordQt 5.15.2 编出来的驱动实测可以握手如果你还在用更老的 Qt 5.12建议把 MySQL 的 default_authentication_plugin 改回 mysql_native_password否则会在连接阶段直接报 unable to logon那感觉相当劝退。Linux 发行版优先选 Ubuntu 或 Debian因为 apt 源里直接有 libmysqlclient-dev装完就能编译RHEL/CentOS 系要另找 mysql-devel头文件路径还不一样后面编译时要多传两个参数。另外提醒一句系统里装了几个 Qt 版本最容易造成驱动加载混乱。编译插件用的 qmake 必须和运行时用的 Qt 是同一套否则插件版本不匹配程序照样报 driver not loaded。这个坑我后面会细说先记住这个原则。2.2 安装 MySQL 服务端与客户端开发库apt 和 rpm 两条路线装 MySQL 不能只装服务端因为编译 Qt 的 MySQL 驱动插件时需要 mysql.h 头文件这个头文件由开发库提供装少了会在 make 阶段卡住。Ubuntu/Debian 系执行sudo apt update sudo apt install mysql-server mysql-client libmysqlclient-dev第一条命令更新软件源索引避免装到过旧的包第二条里的 mysql-server 提供 mysqld 服务mysql-client 提供命令行工具libmysqlclient-dev 提供/usr/include/mysql/mysql.h和链接用的 libmysqlclient.so。如果只想跑通最小环境前两个可以不要但开发库一定得装。装完把服务拉起来并验证sudo systemctl start mysql sudo systemctl enable mysql mysqladmin pingsystemctl start 是立即启动enable 是设置开机自启mysqladmin ping 返回 mysqld is alive 就说明服务在跑。RHEL/CentOS 系则是yum install mysql-server mysql-devel头文件同样落在 /usr/include/mysql/ 下。还有人会用 docker 起一个 mysql 容器来省去本机安装方案可行但要注意把 3306 端口映射到宿主机否则 Qt 程序跑在宿主机上会连不上容器里的 MySQL这个在第 5 章有专门记录。2.3 确认 Qt 的安装目录和插件路径为编译驱动做准备在动手编译之前先在终端里确认三件事qmake 在不在、Qt 插件目录在哪、现在有哪些 SQL 驱动插件。执行下面三条命令which qmake qmake -query QT_INSTALL_PLUGINS ls -l $(qmake -query QT_INSTALL_PLUGINS)/sqldrivers第一条看 qmake 是否进了 PATH。如果没输出说明 Qt 是用镜像站安装包装的但没把 bin 目录加进环境变量你需要手动 export PATH 指向 Qt 安装根目录下的 bin例如/opt/Qt/5.15.2/gcc_64/bin。第二条会打印插件目录通常形如/opt/Qt/5.15.2/gcc_64/plugins。第三条列出已有的 SQL 驱动插件只能看到 libqsqlite.so 是正常的看不到 MySQL 插件正是我们下一步要解决的问题。这三个信息后面反复用到建议先把它们抄在终端旁边。如果你是用清华镜像下载的 Qt 安装器装的路径规则和官方包一致不用额外处理。确认完这些就可以进第 3 章的编译环节了。3. 编译 QMYSQL 驱动插件核心步骤与完整脚本3.1 找到 Qt 的 sqldrivers 源码确认 mysql 子目录Qt 5.15 的 MySQL 驱动源码在 qtbase/src/plugins/sqldrivers/mysql 目录下工程文件叫 mysql.pro。如果你的 Qt 是源码编译安装的直接进源码目录就行。但大多数人是通过安装包装的安装包里并不带 qtbase 源码需要单独下载 qtbase-everywhere-src-5.15.2.tar.xz到 Qt 官方仓库或清华镜像站都能拿到。还有一种情况用 apt 装的 qtbase5-dev源码路径在不同发行版里摆放得很乱有的在 /usr/src有的跟着 dev 包走找起来不如手动下载源码包省心。无论走哪条路最后要确保能看到 mysql.pro 这个文件否则 qmake 会直接报找不到工程文件。3.2 编译前检查 mysql.h 与 qmake 的匹配编译的第一步是确认两个前提mysql.h 存在且 qmake 指向正确的 Qt 版本。执行ls -l /usr/include/mysql/mysql.h qmake -v如果 mysql.h 不存在回到第 2 章把 libmysqlclient-dev 装上。如果 qmake -v 显示的版本不是你打算用的 Qt就改用绝对路径的 qmake比如/opt/Qt/5.15.2/gcc_64/bin/qmake。这一步偷懒的话后面编出来的插件版本不对运行时还会给你一个 driver not loaded白忙一场。接下来进入源码目录编译cd qtbase-everywhere-src-5.15.2/src/plugins/sqldrivers/mysql qmake mysql.pro INCLUDEPATH/usr/include/mysql LIBS-lmysqlclient make -j4INCLUDEPATH 让编译器能找到 mysql.hLIBS 告诉链接器要链 libmysqlclient。这两个参数只在自动检测失败时才需要手动传但提前写上也没坏处。如果缺了 INCLUDEPATH编译会卡在 fatal error: mysql.h: No such file or directory和没装开发库的表现一样这时候要先分清是没装还是没传对路径。make -j4 里的 -j4 是按 CPU 核心数并行编译老机器改成 -j2 更稳。3.3 编译、安装与验证插件make 完成后当前目录下会生成 libqsqlmysql.so。接下来把它装到 Qt 的 sqldrivers 目录sudo make install如果 qmake 的路径变量没配好make install 可能没把文件复制到正确位置那就手动复制sudo cp libqsqlmysql.so $(qmake -query QT_INSTALL_PLUGINS)/sqldrivers/复制完成后要做的第一件事不是写连接代码而是验证插件能不能被 Qt 加载。用下面这段小程序打印支持的驱动列表#include QCoreApplication #include QSqlDatabase #include QDebug int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QStringList drivers QSqlDatabase::drivers(); qDebug() drivers; return 0; }QSqlDatabase::drivers() 会枚举 Qt 当前能加载的所有数据库驱动插件。运行后只要输出里出现 QMYSQL说明插件加载成功如果 QMYSQL 仍然不出现用 ldd 检查插件依赖ldd $(qmake -query QT_INSTALL_PLUGINS)/sqldrivers/libqsqlmysql.soldd 会列出插件依赖的共享库重点看 libmysqlclient.so 有没有显示 not found以及 libstdc 的路径是不是被系统里另一个 Qt 污染了。这一步能看到第 5 章要讲的依赖问题。4. 编写 Qt 连接 MySQL 的代码从最小示例到参数调优4.1 最小连接代码addDatabase、setHostName、setPort 一次跑通驱动插件装好后连接代码反而是最不容易出错的部分。用 QSqlDatabase 的静态方法 addDatabase 拿一个连接实例依次设置主机名、端口、用户名、密码和数据库名然后调 open。下面是一份完整可编译的最小示例#include QCoreApplication #include QSqlDatabase #include QSqlError #include QDebug int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QSqlDatabase db QSqlDatabase::addDatabase(QMYSQL); db.setHostName(127.0.0.1); db.setPort(3306); db.setUserName(app_user); db.setPassword(your_password); db.setDatabaseName(test_db); if (!db.open()) { qDebug() connect failed: db.lastError().text(); return 1; } qDebug() connect ok; return 0; }addDatabase 的第一个参数是驱动名必须和 drivers() 里出现的 QMYSQL 完全一致。setHostName 填 127.0.0.1 表示走 TCP 连本机如果填 localhostQt 在某些平台上会尝试走 socket 文件行为不一致所以我习惯统一写 IP。setPort 默认就是 3306但写上能让连接意图更明确。编译时记得在 .pro 文件里加一句QT sql否则会报找不到 QSqlDatabase 头文件。4.2 连接参数超时、字符集、网络类型连接超时是容易被忽略的选项。MySQL 服务不可达时默认行为是等很久才失败用户体验极差。可以在 open 之前设置连接选项db.setConnectOptions(QSQL_ATTR_CONNECT_TIMEOUT5;QSQL_ATTR_LOGIN_TIMEOUT5);QSQL_ATTR_CONNECT_TIMEOUT 是建立 TCP 连接的超时毫秒数QSQL_ATTR_LOGIN_TIMEOUT 是认证阶段超时毫秒数两个都设成 5 秒配合前面的 open 判断能让连接失败快速暴露。注意连接选项的键名区分大小写拼错了 Qt 不会报错只是静默忽略。字符集问题通常在写入中文时暴露。MySQL 8 默认是 utf8mb4但老库或老表可能是 utf8mb3客户端连接字符集如果和服务端不一致会出现乱码。稳妥的做法是 open 成功后立即执行QSqlQuery query(db); query.exec(SET NAMES utf8mb4);这条语句让当前连接使用的字符集强制切到 utf8mb4不依赖服务端默认值。要注意它只对当前连接生效连接池里每条新连接都得执行一遍我一般会把这一步封装在连接初始化函数里。4.3 查询与防注入prepare 配合 bindValue 是底线数据库连接打通后增删改查绕不开 QSqlQuery。最简单的查询是直接 exec 一条 SELECT 字符串但带用户输入的场景必须用参数绑定否则拼 SQL 字符串会在注入和转义上两头吃亏。QSqlQuery query(db); query.prepare(SELECT id, name FROM user WHERE age ? AND city ?); query.addBindValue(18); query.addBindValue(Shanghai); if (!query.exec()) { qDebug() query failed: query.lastError().text(); return; } while (query.next()) { int id query.value(0).toInt(); QString name query.value(1).toString(); qDebug() id name; }prepare 把 SQL 模板提交给 MySQL 做预编译addBindValue 按顺序填参数Qt 会自动处理字符串转义既避免注入又防止中文引号把 SQL 搞坏。query.next() 每调用一次向前取一行value 按列索引取值索引从 0 开始顺序对应 SELECT 列表。如果你要复用同一条 SQL 多次执行可以在循环里反复 addBindValue execMySQL 端不需要重新解析模板。4.4 连接失败时怎么看错误信息分清 text 和 databaseTextopen 失败时 QSqlError 里有两个字段很多人只盯着 text() 看发现是通用描述就没了方向。text() 是 Qt 生成的错误概要比如 Unable to logon 或 Connection faileddatabaseText() 才是 MySQL 服务器返回的原始信息比如 Access denied for user app_userlocalhost (using password: YES)这才是排错的关键。我通常直接两个一起打出来qDebug() db.lastError().text(); qDebug() db.lastError().databaseText();下表是几组常见错误的快速对照databaseText 特征实际原因优先检查方向Access denied for user用户名或密码错或 host 授权不匹配MySQL 用户权限Unknown database数据库名写错setDatabaseNameCant connect to MySQL serverIP/端口不通或服务未监听bind-address、防火墙Plugin caching_sha2_password is not loaded驱动版本太老换 Qt 5.15 后的驱动或改认证插件5. 避坑清单六条踩坑记录帮你省掉两天时间5.1 现象运行报 QMYSQL driver not loaded原因插件没放对目录或者编译插件用的 Qt 版本和运行程序用的 Qt 版本不一致。前者最常见插件目录和 qmake -query 的输出对不上。解决先用 qmake -query 确认插件路径把 libqsqlmysql.so 复制进去。如果插件路径正确但加载仍然失败检查 ldd 输出里有没有找不到的依赖库。还有一个容易忽略的点如果你在程序里手动设置了 QT_PLUGIN_PATH 环境变量Qt 会优先去这个目录找插件而不是默认目录这时候必须确认那个目录下也有 sqldrivers 子目录。5.2 现象编译时报 fatal error: mysql.h: No such file or directory原因没装 libmysqlclient-dev或者装了但 qmake 的 INCLUDEPATH 没指向头文件目录。这两个原因表现完全一样我先用ls -l /usr/include/mysql/mysql.h确认文件存在再确认 qmake 命令里带了 INCLUDEPATH。如果头文件存在但路径不是标准的 /usr/include/mysql就用qmake -query QT_INSTALL_HEADERS查 Qt 自己的头文件路径把 MySQL 头文件目录单独传进去。5.3 现象make 阶段报 cannot find -lmysqlclient原因链接器找不到 libmysqlclient.so常见于系统只装了 libmariadb 的场景。Ubuntu 上有些基础包会带入 mariadb 的客户端库名称是 libmariadb.soQt 的 mysql.pro 默认找 libmysqlclient。解决用sudo find /usr -name libmysqlclient*找一下库文件实际位置如果只有 libmariadb.so就把 LIBS 参数改成 -lmariadb同时把 mysql.pro 里对头文件路径的检测也一并调整。这个坑在 Debian 系的干净系统上尤其容易撞见。5.4 现象程序部署到另一台机器就连接失败本机却正常原因MySQL 默认只监听 127.0.0.1外部机器的 TCP 连接根本到不了服务端。解决修改 /etc/mysql/mysql.conf.d/mysqld.cnf 里的 bind-address 为 0.0.0.0重启 mysqld。要注意改完后 MySQL 会监听所有网卡生产环境要评估安全风险更稳妥的办法是用 SSH 隧道转发端口而不是把服务直接暴露出来。如果是 docker 容器起的 MySQL还要确认容器端口映射没写错常见翻车点是宿主机 3306 被别的服务占了docker 启动时报端口冲突改个映射端口继续跑Qt 程序却还在用 3306 连。5.5 现象连接被拒绝报 Access denied for user原因MySQL 的用户授权表里 host 字段和你的客户端来源不匹配。rootlocalhost 和 root% 是两个不同的账号本地命令行能连不代表远端程序能连。解决用 mysql 命令行执行授权 SQLCREATE USER app_user% IDENTIFIED BY your_password; GRANT ALL PRIVILEGES ON test_db.* TO app_user%; FLUSH PRIVILEGES;注意第一个语句的 host 段% 表示任意主机但 localhost 那条授权是独立存在的如果程序从本机连可以建 app_userlocalhost否则授权白做了。5.6 现象从 Windows 工程迁到 Linux编译报 dependent 路径错误原因.pro 或 .pri 文件里写死了 Windows 风格的相对路径比如INCLUDEPATH ..\..\qt\5.15.2\msvc2019_64\include\qtwidgets这类路径在 Linux 下解析失败报错形如error: dependent ..\..\qt\5.15.2\msvc2019_64\include\qtwidgets does not exist。解决把所有硬编码的 Qt 路径换成 Qt 提供的变量比如QT widgets自动引入头文件路径不要手写 include 目录必须手写时一律用正斜杠并把路径改成 Linux 实际安装位置。Windows 下的 msvc2019_64 路径更是直接删掉换成 gcc_64 对应的前缀。6. 连接验证的可靠顺序从小范围到大范围逐步打通驱动插件已经编好、代码也能跑通之后不要再一头扎进功能开发先按顺序验证一遍连接可靠性。我的顺序是命令行验证 MySQL 侧连通性再验证 Qt 插件加载最后用最小程序做真实查询。第一步是命令行测试数据库本身mysql -h 127.0.0.1 -P 3306 -u app_user -p -e SELECT 1这一步能排除 MySQL 服务和授权问题。-h 和 -P 必须显式指定因为命令行默认走 socket和 Qt 走 TCP 的行为不一致容易把问题掩盖掉。第二步是跑一遍第 3.3 节的驱动程序列表小程序确认 QMYSQL 出现在 drivers() 里。第三步再跑第 4.1 节的最小连接代码open 成功后面接一条最简单的查询比如 SELECT 1。还有一个值得养成的习惯把编译命令写进一个可重复执行的脚本。我一般会在 ~/bin/ 下放一个 reinit-mysql-qt.sh里面用变量定义 Qt 根目录、MySQL 头文件路径、源码解压路径换新机器时改三个变量就能重放整套编译流程。第一次因为你手动敲命令踩过的坑第二次会因为脚本化而完全跳过。等你发现项目里需要多条数据库连接时再考虑封装一个简单的连接池用 QMutex 保护一个连接队列那又是另一个话题了。先把这条链路从命令行到 Qt 程序全部打通后面加功能才有踏实的基础。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/11 15:48:21

Selenium自动化测试实战:从安装驱动到滚动与反爬

1. 为什么自动化测试绕不开Selenium?1.1 一个“老”框架为什么到现在还在大量使用我入行那会儿,自动化测试圈子里最响的名字就是Selenium。十几年过去,Playwright、Cypress这些新工具一个接一个冒出来,但打开招聘软件看测试开发岗…

2026/10/11 15:48:21

数字化车间规划如何从PPT走向落地:诊断、架构与避坑指南

简介:一份聚焦数字化、智能化车间规划与建设的专业演示文稿,面向制造企业管理者、工业互联网从业者及智能制造规划人员,系统梳理从业务转型到车间落地的关键路径。内容围绕数字化转型、工业互联网、车间规划与智能制造四大主线展开&#xff0…

2026/10/11 18:48:31

千问API申请全流程:从阿里云百炼到自动化办公实战

1. 项目缘起:用千问 API 给自动化办公装上大脑自动化办公这个事,前几年谈的是 RPA、流程引擎、低代码表单,核心思路是把重复点击的动作录下来、跑起来。但这类方案有个硬伤:但凡需要“理解内容”的环节——比如判断一封邮件是催款…

2026/10/11 18:48:31

广州24小时自助健身房解决方案技术实现与实战指南

广州 24 小时自助健身房解决方案技术实现与实战指南 随着城市化进程的加快,人们对健身的需求日益增长,而传统健身房在时间、地点和管理方式上存在诸多限制。广州作为一线城市,具备良好的经济基础和技术环境,为 24 小时自助健身房提…

2026/10/11 18:48:31

免费数据恢复全攻略:从原理到实操,教你救回误删文件

先说个扎心的现实:绝大多数人的重要数据,都是在毫无防备的情况下丢掉的。U盘还没安全弹出就一把拽走、Solid State Drive(SSD)突然不认盘、Word写了一半电脑断电重启、手机照片误删之后又被新照片覆盖……等你反应过来想要找回来&…

2026/10/11 18:48:31

两个数组合并排序全解析:从双指针归并到原地合并与去重

前阵子做日志归并工具,遇到了一个看似简单、却把我折腾得不轻的问题:两个数组合并排序。A 数组是用户行为日志,B 数组是系统事件日志,各路日志内部都按时间戳排好了序,我需要把它们合并成一条完整的事件流,…

2026/10/11 18:48:31

AI会话记忆持久化:从内存到数据库的落地实践与避坑指南

让AI记住上次聊到哪:会话记忆持久化的落地记录做AI对话类应用的人,大概率都遇到过同一个尴尬场景——用户上周还在跟你的机器人核对合同条款,今天回来继续问,机器人却一脸懵地反问“您说的是哪份合同”。不是模型不够聪明&#xf…

2026/10/11 18:43:31

SpringBoot汽车租赁系统开发实战:从数据库设计到并发控制

1. 项目定位与整体设计思路 1.1 毕业设计题目的核心需求拆解 先把这个题目的关键词掰开揉碎。汽车租赁系统的设计与实现,本质上是要你从零搭建一套能跑的完整业务系统,不是写个CRUD Demo交差。评委和导师真正想看的是你对业务流程的理解、对技术栈的把控…

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/11 0:02:13

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/11 0:02:13

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/11 0:02:13

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/11 0:02:13

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑