Qt+数据库
前言支持内置数据库:
驱动关系:
拿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()<<tempErr;
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
{
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");
ps:
1、query.next(数据库命令)是每次返回一行数据,要取出当前行的某列数据,使用query.value(n),n从0开始
2、判断当前qt可用的数据库驱动
QStringList drivers = QSqlDatabase::drivers();
foreach(QString driver, drivers)
qDebug() << "\t" << driver;
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC", "test");
qDebug() << "ODBC driver valid?" << db.isValid();
3、query.exec,如果sql语句没错,拿它是不会报错的,即时返回值为空,它也没错
4、数据库回收
{
//这里用{}画出一个作用域,当这个域完成之后,数据库的所有操作动作都被回收了
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();
//数据库操作
}
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);
QString strLibPath(QDir::toNativeSeparators(QApplication::applicationDirPath())+QDir::separator()+"plugins");
a.addLibraryPath(strLibPath);
9、将数据库快速转Excel的办法
sqlStr = "SELECT * INTO [excel 8.0;database=.\\Data\\export\\1.xls].Sheet1 FROM tableName";
三、mysql
3.1、自己封装的单次访问数据库类,自动实现释放连接
头文件:
#ifndef MYSQLTOOL_H
#define MYSQLTOOL_H
#include <QSqlDatabase>
#include "includes.h"
#include <QSqlQuery>
#include <mutex>
class Mysqltool
{
public:
Mysqltool(QString dataBaseName);
virtual ~Mysqltool();
public:
bool exec(QString sql);//插入、删除、更新
bool select(QString sql,QSqlQuery& rsltQuery);//查询
private:
QSqlDatabase* pDb;
QString dataBaseName;
void setRandString(QString & randString);//获取随机字符串
};
extern std::mutex m_mutext_opensql;
#endif // MYSQLTOOL_H
源文件:
#include "mysqltool.h"
#include "mymethod.h"
#include <QSqlError>
#include <QTime>
std::mutex m_mutext_opensql;
/*****************************************************************/
//作者:朱小勇
//函数名称:构造函数
//函数参数:NULL
//函数返回值:NULL
//函数作用:NULL
//备注:NULL
/*****************************************************************/
Mysqltool::Mysqltool(QString dataBaseName)
{
QString randStr;setRandString(randStr);
dataBaseName += randStr;
#if CLOSE_IF
Mymethod::record(dataBaseName);
#endif
pDb = new QSqlDatabase(QSqlDatabase::addDatabase("QMYSQL", dataBaseName));
pDb->setHostName(DB_IP); //ip
pDb->setUserName(DB_USER_NAME); //登陆MYSQL的用户名
pDb->setPassword(DB_PASSWORD); //登陆的密码
pDb->setDatabaseName(DB_DATABASE_NAME); //数据库的名称
m_mutext_opensql.lock();
if(!pDb->open())
{
Mymethod::record("数据库打开失败:");
// QSqlError tempErr = db.lastError();
// qDebug()<<tempErr;
}
m_mutext_opensql.unlock();
this->dataBaseName = dataBaseName;
} /*****************************************************************/
//作者:朱小勇
//函数名称:析构函数
//函数参数:NULL
//函数返回值:NULL
//函数作用:NULL
//备注:NULL
/*****************************************************************/
Mysqltool::~Mysqltool()
{
pDb->close();
delete pDb;
pDb = nullptr;
QSqlDatabase::removeDatabase(dataBaseName);
} /*****************************************************************/
//作者:朱小勇
//函数名称:增删改
//函数参数:NULL
//函数返回值:NULL
//函数作用:NULL
//备注:NULL
/*****************************************************************/
bool Mysqltool::exec(QString sql)
{
bool ret = true;
RET_VALUE_IF_EAQU(pDb->isOpen(),false,false);
QSqlQuery query(*pDb);
#if OPEN_IF
if(!query.exec(sql))
{
QSqlError tempErr = query.lastError();
qDebug()<<tempErr;
qDebug()<<"sql:"<<sql;
}
#endif
#if CLOSE_IF
RET_VALUE_IF_EAQU(query.exec(sql),false,false);
#endif return ret;
} /*****************************************************************/
//作者:朱小勇
//函数名称:查
//函数参数:NULL
//函数返回值:NULL
//函数作用:NULL
//备注:如果有问题可以将查询结果放在堆上
/*****************************************************************/
bool Mysqltool::select(QString sql,QSqlQuery& rsltQuery)
{
bool ret = true;
QSqlQuery query(*pDb);
RET_VALUE_IF_EAQU(query.exec(sql),false,false);
rsltQuery = query;
return ret;
} /*****************************************************************/
//作者:朱小勇
//函数名称:获取随机字符串,保证连接名不同
//函数参数:NULL
//函数返回值:NULL
//函数作用:NULL
//备注:NULL
/*****************************************************************/
void Mysqltool::setRandString(QString & randString)
{
int max = 8;
QString tmp = QString("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWZYZ");
QString str;
QTime t;
t= QTime::currentTime();
qsrand(t.msec()+t.second()*1000);
for(int i=0;i<max;i++) {
int ir = qrand()%tmp.length();
str[i] = tmp.at(ir);
}
randString = str;
}
注意:在removeDatabase之前要保证数据库相关的对象都回收了,所以将db设计成指针,如果db是类成员变量,那么removeDatabase的时候db永远存在,就会失败。
这是自己封装的访问数据库类,工作原理是每次访问数据库都在对应的线程里建立一个连接,并建立db对象,再访问。缺点是需要每次都建立数据库,浪费时间和数据库资源。
下面使用数据库连接池的方式来封装【参考网上的】
3.2、网上借鉴的数据库连接池,问题其实都很多。释放的时候锁应该放最上面【原文是先方式互斥体再使用锁,让我弄了好久】
ConnectPool.h
#ifndef CONNECTPOOL_H
#define CONNECTPOOL_H #include <QObject>
#include <QtSql>
#include <QQueue>
#include <QMutex>
#include <QMutexLocker> class ConnectPool : public QObject
{
Q_OBJECT public:
static void release(); //关闭所有数据库连接
static QSqlDatabase openConnection(); //获取数据库连接
static void closeConnection(QSqlDatabase connection); //释放数据库连接回连接池 ~ConnectPool();
public:
static ConnectPool& getInstance();
private:
ConnectPool();
ConnectPool(const ConnectPool &other)=default;
ConnectPool& operator=(const ConnectPool &other)=default;
QSqlDatabase createConnection(const QString &connectionName); //创建数据库连接 QQueue<QString> usedConnectionNames; //已使用的数据库连接名
QQueue<QString> unusedConnectionNames; //未使用的数据库连接名 //数据库信息
QString hostName;
QString databaseName;
QString username;
QString password;
QString databaseType; bool testOnBorrow; //取得连接的时间验证连接是否有效
QString testOnBorrowSql; //测试访问数据库的SQL int maxWaitTime; //最大等待时间
int waitInterval; //尝试获取连接时等待间隔时间
int maxConnectionCount; //最大连接数 static QMutex mutex;
static QWaitCondition waitConnection;
static ConnectPool *instance; signals: public slots:
}; #endif // CONNECTPOOL_H
ConnectPool.cpp
#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 = ;
waitInterval = ; //尝试获取连接时等待间隔时间
maxConnectionCount = ; } 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=;i<pool.maxWaitTime&&pool.unusedConnectionNames.size()==&&connectionCount == pool.maxConnectionCount;i+=pool.waitInterval)
{
waitConnection.wait(&mutex,pool.waitInterval); //重新计算已创建连接数
connectionCount = pool.unusedConnectionNames.size() + pool.usedConnectionNames.size();
} if(pool.unusedConnectionNames.size()>)
{
//有已经回收的连接,复用它们
connectionName = pool.unusedConnectionNames.dequeue();
}
else if(connectionCount < pool.maxConnectionCount)
{
//没有已经回收的连接,但是没有达到最大连接数,则创建新连接
connectionName = QString("Connection-%1").arg(connectionCount+);
}
else
{
//已经达到最大连接数
qDebug()<<"Cannot create more connections";
return QSqlDatabase();
} //创建连接
QSqlDatabase db = pool.createConnection(connectionName); //有效连接才放入usedConnectionNames
if(db.isOpen())
{
pool.usedConnectionNames.enqueue(connectionName);
}
return db;
} void ConnectPool::closeConnection(QSqlDatabase connection)
{
ConnectPool &pool = ConnectPool::getInstance();
QString connectName = connection.connectionName();
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"<<testOnBorrowSql<<",for"<<connectionName; QSqlQuery query(testOnBorrowSql,db1);
if(query.lastError().type()!=QSqlError::NoError&&!db1.open())
{
qDebug()<<"Open database error:"<<db1.lastError().text();
return QSqlDatabase();
}
}
return db1;
}
if(!QSqlDatabase::contains(connectionName))
{
QSqlDatabase db = QSqlDatabase::addDatabase(databaseType,connectionName); db.setHostName(hostName);
db.setDatabaseName(databaseName);
db.setUserName(username);
db.setPassword(password); if(!db.open())
{
qDebug()<<"Open database error:"<<db.lastError().text();
return QSqlDatabase();
}
return db;
}
}
注意:
1、这里的原理是仿照线程池的原理,用两个队列来存使用的和没使用的数据库连接名,然后在各自的地方或线程处使用这个连接名。这里有个问题:可能会跨线程调用数据库连接,注意,经过测试,5.12版本到5.13版本一直不支持这个操作,无奈又退到5.10。看过外帖子,貌似是Qt自己的bug,但是至今2019.09.06没修复。
2、在使用了Qt5.13版本时,发现Qt根本没有QMysql驱动,即打印当前可用数据库驱动没有MySql【按理说是文章开头那张表】。
3.3、自己封装的数据库连接池,借鉴了上面的
MyConnection.h
#ifndef MYCONNECTION_H
#define MYCONNECTION_H #define MYDBP_VALUE_0 0 #include <QDebug>
#include <QSqlDatabase>
#include <QSqlError>
#include <QQueue>
#include <mutex>
#include <condition_variable>
#include "mymethod.h"
#include "includes.h"
class MyConnection
{ public:
static MyConnection* getInstace();
static QSqlDatabase getDb();//获取一个db
static void removeDb(QSqlDatabase db);//回收一个db
static void clearAllDb();//清楚所有连接
void setCfg(DbConfig cfg);
private:
MyConnection();
static MyConnection* singleton;
~MyConnection();
private://运算相关
QQueue<QString> freeQueue,busyQueue;//可用连接队列,繁忙队列
static std::mutex m_mutex;
static std::condition_variable cv;
private://数据库配置相关
QString dbIp;
QString dbName;
QString userName;
QString password;
QString dbType;
int maxConnectionCount; // 最大连接数
DbConfig cfg;
}; #endif // MYCONNECTION_H
MyConnection.cpp
#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<std::mutex> lck(m_mutex);
MyConnection* instace = MyConnection::getInstace(); int currentConnets = instace->freeQueue.size()+instace->busyQueue.size();
if(currentConnets < instace->maxConnectionCount)//新建连接
{
QSqlDatabase db = QSqlDatabase::addDatabase(instace->dbType,QString("connet_num_%1").arg(currentConnets));
db.setHostName(instace->dbIp);
db.setDatabaseName(instace->dbName);
db.setUserName(instace->userName);
db.setPassword(instace->password);
if(!db.open())
{
QSqlError tempErr = db.lastError();
Mymethod::record(tempErr.text(),PRINT_ERR);
return QSqlDatabase();
}
if(db.isValid()&&db.isOpen())
{
instace->busyQueue.push_back(db.connectionName());
return db;
}
} if(instace->freeQueue.size() > MYDBP_VALUE_0)//有空闲的连接直接使用
{
QSqlDatabase db = QSqlDatabase::database(instace->freeQueue.front());
db.setHostName(instace->dbIp);
db.setDatabaseName(instace->dbName);
db.setUserName(instace->userName);
db.setPassword(instace->password);
if(!db.open())
{
QSqlError tempErr = db.lastError();
Mymethod::record(tempErr.text(),PRINT_ERR);
return QSqlDatabase();
}
if(db.isValid()&&db.isOpen())
{
instace->busyQueue.push_back(db.connectionName());//存入繁忙队列
instace->freeQueue.pop_front();//从空闲队列删除
return db;
}
} if((instace->freeQueue.size()==MYDBP_VALUE_0)&&(instace->busyQueue.size()==instace->maxConnectionCount))//无空闲的连接,等待外部释放
{
Mymethod::record(QString("cannot create more connections,used db connection:%1,unused db connection:%2").arg(instace->busyQueue.size()).arg(instace->freeQueue.size()),PRINT_ERR);
while(instace->freeQueue.size()==MYDBP_VALUE_0)
{
cv.wait(lck);
}
QSqlDatabase db = QSqlDatabase::addDatabase(instace->dbType,instace->freeQueue.front());
db.setHostName(instace->dbIp);
db.setDatabaseName(instace->dbName);
db.setUserName(instace->userName);
db.setPassword(instace->password);
if(!db.open())
{
QSqlError tempErr = db.lastError();
Mymethod::record(tempErr.text(),PRINT_ERR);
return QSqlDatabase();
}
if(db.isValid()&&db.isOpen())
{
instace->busyQueue.push_back(db.connectionName());//存入繁忙队列
instace->freeQueue.pop_front();//从空闲队列删除
return db;
}
}
return QSqlDatabase();
} /*****************************************************************/
//作者:朱小勇
//函数名称:删除连接
//函数参数:NULL
//函数返回值:NULL
//函数作用:NULL
//备注:NULL
/*****************************************************************/
void MyConnection::removeDb(QSqlDatabase db)
{
std::unique_lock<std::mutex> lck(m_mutex);
MyConnection* instace = MyConnection::getInstace();
if(instace->busyQueue.contains(db.connectionName()))
{
instace->busyQueue.removeOne(db.connectionName());
instace->freeQueue.push_back(db.connectionName());
}
cv.notify_one();
} /*****************************************************************/
//作者:朱小勇
//函数名称:清除所有连接
//函数参数:NULL
//函数返回值:NULL
//函数作用:NULL
//备注:NULL
/*****************************************************************/
void MyConnection::clearAllDb()
{
std::unique_lock<std::mutex> lck(m_mutex);
MyConnection* instace = MyConnection::getInstace();
for(auto connct : instace->freeQueue)
{
QSqlDatabase::removeDatabase(connct);
}
for(auto connct : instace->busyQueue)
{
QSqlDatabase::removeDatabase(connct);
}
}
我自己封装这个相比于网上广为流传的,少了等待操作。所以当外部向这个单例类请求获取db后,如果返回的是空db,应该再外面自行等待
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);
2019.10.24:
今天出现了个问题,刚好是程序员节,md调试了了一天。
问题描述:
线程A不断产生sql语句,需要让两个数据库分别执行这个sql语句。所以在线程A中建立两个子线程B和C,分别对应两个数据库。然后发现两个线程同时启动【即两个线程同时调Qt访问mysql的dll】会报错:
解决:
在其中一个子线程中初次调用的时候延时1s:
static bool test=true;
if(test)
{
QThread::sleep();
test=false;
}
Qt+数据库的更多相关文章
- qt数据库多线程问题的解决(QSqlDatabase只能在创建它的线程中使用)
Qt数据库由QSqlDatabase::addDatabase()生成的QSqlDatabase只能在创建它的线程中使用, 在多线程中共用连接或者在另外一个线程中创建query都是不支持的几乎国内没有 ...
- Qt添加驱动——Qt数据库之添加MySQL驱动插件
Qt数据库之添加MySQL驱动插件(1) 现在可用的数据库驱动只有3种,在Qt中,我们需要自己编译其他数据库驱动的代码,让它们以插件的形式来使用.下面我们就以现在比较流行的MySQL数据库为例,说明一 ...
- Qt数据库_资料
1. QT笔记_数据库总结(一)-rojian-ChinaUnix博客.html http://blog.chinaunix.net/uid-28194872-id-3631462.html (里面有 ...
- Qt数据库 QSqlTableModel实例操作(转)
本文介绍的是Qt数据库 QSqlTableModel实例操作,详细操作请先来看内容.与上篇内容衔接着,不顾本文也有关于上篇内容的链接. Qt数据库 QSqlTableModel实例操作是本文所介绍的内 ...
- C/C++ Qt 数据库与TreeView组件绑定
在上一篇博文<C/C++ Qt 数据库QSql增删改查组件应用>介绍了Qt中如何使用SQL操作函数,并实现了对数据库的增删改查等基本功能,从本篇开始将实现数据库与View组件的绑定,通过数 ...
- C/C++ Qt 数据库与TableView多组件联动
Qt 数据库组件与TableView组件实现联动,以下案例中实现了,当用户点击并选中TableView组件内的某一行时,我们通过该行中的name字段查询并将查询结果关联到ListView组件内,同时将 ...
- QT 数据库编程三
//mainwindow.cpp #include "mainwindow.h" #include "logindlg.h" #include "sc ...
- Qt数据库操作(qt-win-commercial-src-4.3.1,VC6,Oracle,SQL Server)
qt-win-commercial-src-4.3.1.qt-x11-commercial-src-4.3.1Microsoft Visual C++ 6.0.KDevelop 3.5.0Window ...
- 【转】Qt数据库总结
转自:http://blog.chinaunix.net/uid-25201977-id-3014100.html #include <QtSql>QT += sql QSqlDataba ...
- QT数据库使用案列【联系人】-- 使用sqlite和QStringListModel
[关于如何打包自己开发的程序为安装包,可以参考http://www.cnblogs.com/yuliyang/p/4242179.html] [简要功能介绍] 使用sqlite数据库和Qt搭建界面,实 ...
随机推荐
- java输出
把一个java对象转化成一个json字符串: JSON.toJSON(user); JSON.toJSONStringWithDateFormat(user, "yyyy-MM-dd HH: ...
- mysql中对应oracle中的to_char()和to_number()函数
TO_CHAR(): CAST(123 AS CHAR(3)) TO_NUMBER(): cast( '123 ' as SIGNED INTEGER)
- PNG透明兼容IE6的几种方法
方法一:css方式,写一个属性hack,使用滤镜来解决png在ie6下不兼容的问题. _filter: progid:DXImageTransform.Microsoft.AlphaImageLoad ...
- Linux 装JDK
1.查看当前系统有没有装jdk java -version 2.看看有没有安装包 rpm -qa | grep java 3.卸载OpenJDK $>rpm -e --nodeps tzdata ...
- 学习 《UNIX网络编程》
学习本书之前,为了了解C语言,先通读了<C程序设计语言>.但对C语言的理解.熟悉可能还是不足,所以在学习本书的过程中,遇到看不懂的C代码,还要去查询.思考. 本书一开始,我就遇到了问题,运 ...
- js split 的用法和定义 js split分割字符串成数组的实例代码
关于js split的用法,我们经常用来将字符串分割为数组方便后续操作,今天写一段广告判断代码的时候,竟然忘了split的用法了,特整理下,方便需要的朋友, 关于js split的用法其它也不多说什么 ...
- Python菜鸟之路:Jquery Ajax的使用
Ajax概述 Ajax就是通过 HTTP 请求加载远程数据.通常用于定制一些http请求来灵活的完成前端与后端的数据交互需求. 注意,所有的选项都可以通过$.ajaxSetup()函数来全局设置. J ...
- MySQL复制(三):常见的复制任务
复制案例:包括横向扩展.热备 报表 企业需要挖掘有价值的报表信息,使用SLAVE,可以避免干扰到master的复制. 通过binlog获取某个时间范围的所有事件 $ mysqlbinlog --for ...
- Django 路由系统(URL)
介绍 Django 1.11版本 URLConf官方文档 URL配置(URLconf)就像Django 所支撑网站的目录.它的本质是URL与要为该URL调用的视图函数之间的映射表. 你就是以这种方式告 ...
- Android怎样在http头信息里设置參数
在使用http请求server时经常要传递一些參数给server.如IMEI号.平台号.渠道号.client的版本等一些通用信息,像这些參数我们没有必要每次都拼在url后,我们能够统一加入到http头 ...