前言支持内置数据库:
驱动关系:
拿Mysql举例,我们的Qt程序有自己的驱动,libqsqlmysql.dll,如果Qt安装好了之后没有这个动态库,则需要自己使用qmake编译。如果有了这个驱动,说明我们的Qt环境已经ok了,但是如果需要访问Mysql数据库,还需要Mysql提供的访问它的驱动libmysql.dll【linux对应libmysqlclient.so.18,不同mysql版本名字不一样】。总结就是:Qt程序->libqsqlmysql.dll->libmysql.dll->Mysql数据库
libqsqlmysql.dll:安装Qt附带或者源代码编译
libmysql.dll:安装mysql或者直接网上找这个动态库
一、sqlite
1、在头文件中声明数据库对象
QSqlDatabase db;
2、在构造函数中定义对象(最好这样定义,因为对于db来说只需要addDatabase一次,否则多次addDatabase会报错)
if(QSqlDatabase::contains("qt_sql_default_connection"))
db = QSqlDatabase::database("qt_sql_default_connection");
else
db = QSqlDatabase::addDatabase("QSQLITE");
3、设置数据库文件路径
db.setDatabaseName(".//qtDb.db");//设置数据库文件名字,选择的是当前路径
4、打开数据库
if(!db.open())
{
qDebug() << "打开数据库失败";
return;
}
5、判断数据库中是否存在某表,不存在则新建(数据库指令相关)
QSqlQuery query(db);
bool isTableExist = query.exec("select * from CSSBDB");//关键的判断
if(!isTableExist)//表不存在,新建表
{
bool success = query.exec("create table CSSBDB(id INTEGER PRIMARY KEY,设备名称 varchar,设备型号 varchar,固定资产编号 varchar,测试部自编号 varchar,卡片编号 varchar,数量 varchar,单价 varchar,启用时间 varchar,用途 varchar,使用人或保管人 varchar,备注 varchar)");//创建数据表
if(success)
{
qDebug() <<"数据库表1创建成功!";
}
else
{
qDebug() <<"数据库表1创建失败!";
QSqlError tempErr = query.lastError();
qDebug()< return; } } 6、获取数据库中有多少数据(行数和列数) query.exec("select * from CSSBDB"); QSqlQueryModel *model = new QSqlQueryModel(); model->setQuery(query); int nRecordCount = model->rowCount();//行数 int nColumnCount = model->columnCount();//列数 7、获取表的表头内容 if(query.exec("PRAGMA table_info(CSSBDB)")) { QStringList tempList; while(query.next()) { tempList.append(query.value(1).toString()); } } 下图是获取表信息结果 8、更新某条记录 update CSSBDB set companyNumber='050689' where id=2 9、插入一条记录 insert into CSSBDB values(null,"台式主机","DELL9020MT","050688","CSB-PC001-A","123","1","3000","2016.4.15","服务器","","")//如果id是null则表示id按顺序增加1 10、关闭数据库 db.close(); 二、Access ps: 1、query.next(数据库命令)是每次返回一行数据,要取出当前行的某列数据,使用query.value(n),n从0开始 2、判断当前qt可用的数据库驱动 QStringList drivers = QSqlDatabase::drivers(); QSqlDatabase db = QSqlDatabase::addDatabase("QODBC", "test"); 3、query.exec,如果sql语句没错,拿它是不会报错的,即时返回值为空,它也没错 4、数据库回收 { //这里用{}画出一个作用域,当这个域完成之后,数据库的所有操作动作都被回收了 //数据库操作 }QSqlDatabase::removeDatabase("qt_sql_default_connection");//这句话之前保证当前连接的数据库没有任何数据操作,由上面的作用域实现 5、数据库原理: QSqlDatabase db = QSqlDatabase::addDatabase("QODBC"); 上面这句话是新建一个连接,每个连接是对数据库的唯一标识,如果像上面这样写,连接名称就是默认的qt_sql_default_connection,最后回收的时候也是QSqlDatabase::removeDatabase("qt_sql_default_connection") 如果在程序中多个地方【线程】操作数据库:QSqlDatabase db = QSqlDatabase::addDatabase("QODBC","MyConnction"); 回收:QSqlDatabase::removeDatabase("MyConnction"); 6、有时候出现问题:"[Microsoft][ODBC 驱动程序管理器] 未发现数据源名称并且未指定默认驱动程序 QODBC3: Unable to connect" 1、dsn语句有误 2、数据库驱动不正确,当时我编译的是64位的,正常运行,后来改成了32位的,报上面错,下载32位的驱动安装了就ok了 7、解决通过model->rowCount();只能返回最多256个数据 while(model->canFetchMore()) { model->fetchMore(); } 8、使用了数据库时,有时打包软件出现“driver not load” 1)将C:\Qt\Qt5.3.1\5.3\msvc2012路径下的文件夹plugins复制到exe文件目录下,打开plugins,只保留sqldrivers文件夹,需要确认里面是否有你需要的驱动, 如:程序中使用了QSqlite数据库,则需要有qsqlite.dll(发布版)qsqlited.dll(调试版), 2)在main.cpp文件中添加下面第二行和第三行: QApplication a(argc, argv); 9、将数据库快速转Excel的办法 三、mysql 3.1、自己封装的单次访问数据库类,自动实现释放连接 头文件: 源文件: 注意:在removeDatabase之前要保证数据库相关的对象都回收了,所以将db设计成指针,如果db是类成员变量,那么removeDatabase的时候db永远存在,就会失败。 这是自己封装的访问数据库类,工作原理是每次访问数据库都在对应的线程里建立一个连接,并建立db对象,再访问。缺点是需要每次都建立数据库,浪费时间和数据库资源。 下面使用数据库连接池的方式来封装【参考网上的】 3.2、网上借鉴的数据库连接池,问题其实都很多。释放的时候锁应该放最上面【原文是先方式互斥体再使用锁,让我弄了好久】 ConnectPool.h ConnectPool.cpp 注意: 1、这里的原理是仿照线程池的原理,用两个队列来存使用的和没使用的数据库连接名,然后在各自的地方或线程处使用这个连接名。这里有个问题:可能会跨线程调用数据库连接,注意,经过测试,5.12版本到5.13版本一直不支持这个操作,无奈又退到5.10。看过外帖子,貌似是Qt自己的bug,但是至今2019.09.06没修复。 2、在使用了Qt5.13版本时,发现Qt根本没有QMysql驱动,即打印当前可用数据库驱动没有MySql【按理说是文章开头那张表】。 3.3、自己封装的数据库连接池,借鉴了上面的 MyConnection.h MyConnection.cpp 我自己封装这个相比于网上广为流传的,少了等待操作。所以当外部向这个单例类请求获取db后,如果返回的是空db,应该再外面自行等待 2019.10.24: 今天出现了个问题,刚好是程序员节,md调试了了一天。 问题描述: 线程A不断产生sql语句,需要让两个数据库分别执行这个sql语句。所以在线程A中建立两个子线程B和C,分别对应两个数据库。然后发现两个线程同时启动【即两个线程同时调Qt访问mysql的dll】会报错: 解决: 在其中一个子线程中初次调用的时候延时1s:{
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC", "EXPORT_EXCEL");
QString dsn = QString("DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};FIL={MS Access};DBQ=%1").arg(filePath);// DB_PATH
db.setDatabaseName(dsn);//设置数据库路径
QString sqlStr;
if (!db.open())
{
qDebug() << "打开数据库失败";
}
else
{
QSqlQuery query(db);
sqlStr = "";
sqlStr = QString("SELECT * FROM tableName");
query.exec(sqlStr);
}
}
QSqlDatabase::removeDatabase("EXPORT_EXCEL");
foreach(QString driver, drivers)
qDebug() << "\t" << driver;
qDebug() << "ODBC driver valid?" << db.isValid();
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");//数据库,设置为Access2000
QString dsn = QString("DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};FIL={MS Access};DBQ=%1").arg(DB_PATH);//这是access的数据库,需要与当前电脑数据库驱动一致:控制面板,数据源,ODBC
db.setDatabaseName(dsn);//设置数据库路径
db.open();
QString strLibPath(QDir::toNativeSeparators(QApplication::applicationDirPath())+QDir::separator()+"plugins");
a.addLibraryPath(strLibPath);sqlStr = "SELECT * INTO [excel 8.0;database=.\\Data\\export\\1.xls].Sheet1 FROM tableName";
#ifndef MYSQLTOOL_H
#define MYSQLTOOL_H
#include
#include "mysqltool.h"
#include "mymethod.h"
#include
#ifndef CONNECTPOOL_H
#define CONNECTPOOL_H
#include
#include "connectpool.h"
QMutex ConnectPool::mutex;
QWaitCondition ConnectPool::waitConnection;
ConnectPool* ConnectPool::instance = nullptr;
ConnectPool::ConnectPool()
{
hostName = "192.168.1.126";
databaseName = "source_data";
username = "root";
password = "zhuxiaoyong1212";
databaseType = "QMYSQL";
testOnBorrow = true;
testOnBorrowSql = "SELTCT 1";
maxWaitTime = 1000;
waitInterval = 200; //尝试获取连接时等待间隔时间
maxConnectionCount = 5;
}
ConnectPool::~ConnectPool()
{
foreach (QString connectionName, usedConnectionNames) {
QSqlDatabase::removeDatabase(connectionName);
}
foreach (QString connectionName, unusedConnectionNames) {
QSqlDatabase::removeDatabase(connectionName);
}
}
ConnectPool &ConnectPool::getInstance()
{
if(instance==nullptr)
{
QMutexLocker locker(&mutex);
if(nullptr==instance)
{
instance = new ConnectPool();
}
}
return *instance;
}
void ConnectPool::release()
{
QMutexLocker locker(&mutex);
delete instance; //调用析构函数
instance = nullptr;
}
QSqlDatabase ConnectPool::openConnection()
{
ConnectPool &pool = ConnectPool::getInstance();
QString connectionName;
QMutexLocker locker(&mutex);
//已创建的连接数
int connectionCount = pool.unusedConnectionNames.size() + pool.usedConnectionNames.size();
//如果连接已经用完,等待waitInterval毫秒,看是否有可用连接,最大等待maxWaitTime毫秒
for(int i=0;i
QMutexLocker locker(&mutex);//位置别放错了
//如果是我们创建的连接,从used里删除,放入unused
if(pool.usedConnectionNames.contains(connectName))
{
pool.usedConnectionNames.removeOne(connectName);
pool.unusedConnectionNames.enqueue(connectName);
waitConnection.wakeOne();
}
}
QSqlDatabase ConnectPool::createConnection(const QString &connectionName)
{
//连接已经创建过的,复用它,而不是重新创建
if(QSqlDatabase::contains(connectionName))
{
QSqlDatabase db1 = QSqlDatabase::database(connectionName);
if(testOnBorrow)
{
qDebug()<<"Test connection on borrow, execute"<#ifndef MYCONNECTION_H
#define MYCONNECTION_H
#define MYDBP_VALUE_0 0
#include
#include "myconnection.h"
/*****************************************************************/
//作者:朱小勇
//函数名称:构造函数
//函数参数:NULL
//函数返回值:NULL
//函数作用:NULL
//备注:NULL
/*****************************************************************/
MyConnection::MyConnection()
{
Mymethod::record("construct dp connection pool.",PRINT_INFO);
dbIp = "192.168.1.1";
dbName = "ea_phm";
userName = "root";
password = "zhuxiaoyong1212";
dbType = "QMYSQL";
maxConnectionCount = DB_CONNET_COUNT;// 最大连接数
}
/*****************************************************************/
//作者:朱小勇
//函数名称:数据库设置
//函数参数:NULL
//函数返回值:NULL
//函数作用:NULL
//备注:NULL
/*****************************************************************/
void MyConnection::setCfg(DbConfig cfg)
{
dbIp = cfg.hostIp;
dbName = cfg.databaseName;
userName = cfg.username;
password = cfg.password;
dbType = "QMYSQL";
Mymethod::record("db connection initialized,database name:"+dbName,PRINT_INFO);
}
/*****************************************************************/
//作者:朱小勇
//函数名称:析构函数
//函数参数:NULL
//函数返回值:NULL
//函数作用:NULL
//备注:NULL
/*****************************************************************/
MyConnection::~MyConnection()
{
clearAllDb();
if(singleton != nullptr)
{
delete singleton;
singleton = nullptr;
}
}
MyConnection* MyConnection::singleton = new MyConnection();//静态变量初始化
std::mutex MyConnection::m_mutex;
std::condition_variable MyConnection::cv;
/*****************************************************************/
//作者:朱小勇
//函数名称:返回单例对象
//函数参数:NULL
//函数返回值:NULL
//函数作用:NULL
//备注:NULL
/*****************************************************************/
MyConnection* MyConnection::getInstace()
{
return singleton;
}
/*****************************************************************/
//作者:朱小勇
//函数名称:获取一个连接
//函数参数:NULL
//函数返回值:NULL
//函数作用:NULL
//备注:NULL
/*****************************************************************/
QSqlDatabase MyConnection::getDb()
{
std::unique_lock
QSqlDatabase db = MyConnection::getDb();
while(!db.isValid() || !db.isOpen())
{
Mymethod::record("db is invalid,retry to get a valid db.",PRINT_ERR);
QThread::msleep(VALUE_300);
db = MyConnection::getDb();
}
QSqlQuery q(db);
if(!q.exec(sql))
{
QSqlError tempErr = q.lastError();
Mymethod::record(tempErr.text()+" sql:"+sql,PRINT_ERR);
}
else
{
Mymethod::record("insert "+db.databaseName()+" ok.",PRINT_OK);
}
MyConnection::removeDb(db);
static bool test=true;
if(test)
{
QThread::sleep(1);
test=false;
}