Qt多线程访问数据库3

在多线程环境下,例如存在线程A与线程B,且A,B线程都需要调用数据库,线程A为主线程,表的创建以及数据库的移除均出现在线程A中。

所需要的头文件

//数据库
#include<QSqlDatabase>
#include<QSqlError>
#include<QSqlQuery>
//线程锁
#include<QMutex>

所有线程中,所有关于数据库变量的设置,都在cpp源文件的初始化中进行 。假设线程A的初始化先于线程B


线程A 

连接数据库:

在线程A,线程A.cpp的构造函数中,进行数据库相关内容的设置:

QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL","a");
db.setHostName("localhost");//数据库服务器IP
db.setUserName("root");
db.setPassword("123456789");   //这里输入你的密码
db.setDatabaseName("test");//这里输入你的数据库名
bool flagdb=db.open();
if(flagdb==false)
{
   QMessageBox::critical(NULL, QObject::tr("无法打开数据库"),"无法创建数据库连接! ", QMessageBox::Cancel);
   return;
}

创建表:

需要在线程A.h头文件中,定义QSQLQuery的对象query,因为线程A.cpp中多个自定义函数需要调用数据库,保证query的作用域为整个A.cpp。

线程A的.h中

QSqlQuery query;
QString insert="insert into acmmdatabase "
                    "(id,enfrist,ensecond,enthree,enfour,enfive,ensix) "
                    "values(:id,:enfrist,:ensecond,:enthree,:enfour,:enfive,:ensix);";

之后紧接着线程A.cpp的初始化程序,创建表

query = QSqlQuery ("",db);
bool flagquery=query.exec("create table acmmdatabase(id int primary key,enfrist double,ensecond double,enthree double,enfour double,enfive double,ensix double)");
if(flagquery==false)// 一旦路径错误,就会出错,提示工作人员去修改路径
{
//        qDebug()<<"数据库的表有问题!";
  bool clearfalg = query.exec("DROP table acmmdatabase");//删除是使用DROP
  if(clearfalg==false)
  {
    qDebug()<< query.lastError();
  }
  else
  {
    qDebug() << "delete table!";
  }
  
  bool flagqueryagain=query.exec("create table acmmdatabase(id int primary key,enfrist double,ensecond double,enthree double,enfour double,enfive double,ensix double)");
  if(flagqueryagain==false)
  {
    int chose = QMessageBox::warning(NULL,"warning","数据库创建表失败,请联系工作人员!",QMessageBox::Yes);
    if(chose==QMessageBox::Yes)
       return;
  }
}

之后在线程A.cpp的其他自定义函数中,调用数据库,并进行操作即可,比如插入,清空,删除一项等。

因为此前query是全局变量,所以在整个线程A.cpp均可调用query进行数据库的相关操作

数据插入:

  • 数据插入(示例,最后会放上整个程序段)
void Widget::tableoperation()
{
    /*************数据库调用*************/
    mutex.lock();//保持原子性
 
    int oi=0;
    bool insertfalg =query.prepare(insert);//插入操作
    if(insertfalg == false)//判断操作是否成功
    {
        qDebug()<<query.lastError();
    }
    else
    {
        qDebug()<<"insert succeed!";
    }

    query.bindValue(":id",num+1);
    query.bindValue(":enfrist",dcom->oriangle[oi]);
    query.bindValue(":ensecond",dcom->oriangle[oi+1]);
    query.bindValue(":enthree",dcom->oriangle[oi+2]);
    query.bindValue(":enfour",dcom->oriangle[oi+3]);
    query.bindValue(":enfive",dcom->oriangle[oi+4]);
    query.bindValue(":ensix",dcom->oriangle[oi+5]);
 
    if(query.exec()==false)//判断赋值是否成功
    {
        qDebug()<<query.lastError();
    }
    else
    {
        qDebug()<<"insert data succeed!";
    }

    num++;
    mutex.unlock();//保持原子性
}

线程B:

连接数据库 :

在线程B.cpp中,连接数据库

QSqlDatabase db2 = QSqlDatabase::addDatabase("QMYSQL","b");
db2.setHostName("localhost");//数据库服务器IP
db2.setUserName("root");
db2.setPassword("123456789");   //这里输入你的密码
db2.setDatabaseName("test");//这里输入你的数据库名

bool flagdb = db2.open();
if(flagdb == false)
{
   QMessageBox::critical(NULL, QObject::tr("无法打开数据库"),"无法创建数据库连接! ", QMessageBox::Cancel);
   return;
}

imquery =QSqlQuery("",db2);

同理,需要在线程B.h中,定义QSQLQuery的对象imquery,因为线程B.cpp中多个自定义函数需要调用数据库,保证query的作用域为整个B.cpp。

QSqlQuery imquery;

查询(检测A线程中插入的数据是否存在): 

之后在线程B.cpp进行调用数据库的操作,如查询。

bool fflag = imquery.exec("select enfrist from acmmdatabase");//只进行enfrist的查询

if(fflag==false)
{
   qDebug() <<"Error: Fail to enfrist create table." << imquery.lastError();
}
else
{
   while(imquery.next())
   {
      //数据处理的示例,仅供参考
      x=52*sin( imquery.value("enfrist").toDouble()*pi )+65;
      y=52*cos(imquery.value("enfrist").toDouble()*pi )+85;
      p.drawLine(65,85,x,y);
   }
}

注意:使用select查询语句时,切记要注意,imquery.next必须在imquery.value之前,否则没有数据。

移除数据库: 
最后,当我们需要关闭程序时,要对数据库的表进行删除,并对数据库进行removeDatabase

如前文所说,线程A.cpp才是主线程,所以表的删除及removeDatabase需要在线程A中完成。

void Widget::closeEvent(QCloseEvent *event)
{
    qDebug()<<"进入退出处理程序";
 
    int ret = QMessageBox::question(this,"question","是否需要关闭窗口",QMessageBox::Yes,QMessageBox::No);
 
    if(ret == QMessageBox::Yes)
    {
        event->accept();
 
        stopimage();//关闭线程B
 
        QSqlDatabase::removeDatabase("b");//移除数据库b,及线程B中使用的数据库
 
        QSqlDatabase::removeDatabase("a");//移除数据库a,及线程B中使用的数据库
        /*MySQL*/
 
        qDebug()<<"数据库已经完成关闭和移除";
        /*MySQL*/
    }
    else if(ret == QMessageBox::No)
    {
        event->ignore();
    }
}

附录(完整代码): 

  • 补充:整个程序段的展示(部分数据处理的内容未展示)
  • 线程A.h
//数据库
#include<QSqlDatabase>
#include<QSqlError>
#include<QSqlQuery>
//线程锁
#include<QMutex>
 
namespace Ui {
class Widget;
}
 
class Widget : public QWidget
{
    Q_OBJECT
 
protected:
    void closeEvent(QCloseEvent *event);
   
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
 
    void progress();
 
     int prnumb;//进度条读书
 
     //数据库
     QSqlQuery query;
     QString insert="insert into acmmdatabase "
                    "(id,enfrist,ensecond,enthree,enfour,enfive,ensix) "
                    "values(:id,:enfrist,:ensecond,:enthree,:enfour,:enfive,:ensix);";
   
 
private slots:
 
    void on_save_clicked();
 
    void on_deletedata_clicked();
 
    void on_clear_clicked();
 
private:
    Ui::Widget *ui;
 
    imagethread *imT;
    QThread *imgthread;
 
    QProgressDialog *progressindex;
 
};
 
#endif // WIDGET_H
  • 线程A.cpp
#include "widget.h"
#include "ui_widget.h"
 
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
   
    /*********************************线程B*********************************************/
   
    imT = new imagethread;//分配空间
    imgthread = new QThread(this);//指定父对象,子线程
    imT->moveToThread(imgthread);//自定义线程加入子线程
 
    //启动image子线程
    imgthread->start();
    //启动线程
    
    /*********************************线程B*********************************************/
    /***********************************数据库Mysql*****************************************************/
 
    mutex.lock();
    QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL","a");
    db.setHostName("localhost");//数据库服务器IP
    db.setUserName("root");
    db.setPassword("2112414cjm");   //这里输入你的密码
    db.setDatabaseName("test");//这里输入你的数据库名
    bool flagdb=db.open();
    if (flagdb==false)
    {
      QMessageBox::critical(NULL, QObject::tr("无法打开数据库"),"无法创建数据库连接! ", QMessageBox::Cancel);
      return;
    }
    mutex.unlock();
    query = QSqlQuery ("",db);//如果是全局变量,需要制定
    bool flagquery=query.exec("create table acmmdatabase(id int primary key,enfrist double,ensecond double,enthree double,enfour double,enfive double,ensix double)");
    if(flagquery==false)// 一旦路径错误,就会出错,提示工作人员去修改路径
    {
//        qDebug()<<"数据库的表有问题!";
        bool clearfalg = query.exec("DROP table acmmdatabase");//删除是使用DROP
        if(clearfalg==false)
        {
            qDebug()<< query.lastError();
        }
        else
        {
            qDebug() << "delete table!";
        }
        bool flagqueryagain=query.exec("create table acmmdatabase(id int primary key,enfrist double,ensecond double,enthree double,enfour double,enfive double,ensix double)");
        if(flagqueryagain==false)
        {
            int chose = QMessageBox::warning(NULL,"warning","数据库创建表失败,请联系工作人员!",QMessageBox::Yes);
            if(chose==QMessageBox::Yes)
                return;
        }
    }
    
    /***********************************数据库Mysql*****************************************************/
 
 
}
Widget::~Widget()
{
    delete ui;
}
 
//前界面表格的具体内容
void Widget::tableoperation()
{
  /*******
   数据处理....
    ********/
    /*************数据库调用*************/
    mutex.lock();
 
    //摁下之后会进行操作
    int oi=0;
    bool insertfalg =query.prepare(insert);//插入操作
    if(insertfalg == false)
    {
        qDebug()<<query.lastError();
    }
    else
    {
        qDebug()<<"insert succeed!";
    }
    query.bindValue(":id",num+1);
    query.bindValue(":enfrist",dcom->oriangle[oi]);
    query.bindValue(":ensecond",dcom->oriangle[oi+1]);
    query.bindValue(":enthree",dcom->oriangle[oi+2]);
    query.bindValue(":enfour",dcom->oriangle[oi+3]);
    query.bindValue(":enfive",dcom->oriangle[oi+4]);
    query.bindValue(":ensix",dcom->oriangle[oi+5]);
//    qDebug()<<num+1;
   //更改之后,可以连续赋值
    if( query.exec()==false)//判断赋值是否成功
    {
        qDebug()<<query.lastError();
    }
    else
    {
        qDebug()<<"insert data succeed!";
    }
 
    mutex.unlock();
 /*******
   数据处理....
    ********/
    /**************数据库调用************/
  }
   /*******
   数据处理....
    ********/
}
 
//删除某行
void Widget::on_deletedata_clicked()
{
    /*******
   数据处理....
    ********/
 
    bool onefalg = query.exec("DELETE FROM acmmdatabase WHERE id = ?");//ok
    if(onefalg==false)
    {
        qDebug()<<"delete one`s 指令错误"<<query.lastError();
    }
    else
    {
        qDebug()<<"deleted,one!指令正确";
    }
    query.addBindValue(numcow+1);
 
    if(query.exec()==false)
    {
        qDebug()<<"delete one`s error"<<query.lastError();
    }
    else
    {
        qDebug()<<"deleted,one!";
    }
 /*******
   数据处理....
    ********/
}
//表格全部清空
void Widget::on_clear_clicked()
{
    
   /*******
   数据处理....
    ********/
    qDebug()<<"准备清理数据库";
     mutex.lock();
    bool clearfalg = query.exec("DELETE FROM acmmdatabase");//
    if(clearfalg==false)
    {
        qDebug()<< query.lastError();
    }
    else
    {
        qDebug() << "delete,all!";
    }
     mutex.unlock();
    /*******
   数据处理....
    ********/
}
 
void Widget::progress()//进度条
{
    prnumb=0;//进度条初始化
//    qDebug()<<"进度条";
    progressindex = new QProgressDialog(this);
    progressindex->setMinimum(0);//最小值
    progressindex->setMaximum(table->rowCount());//最大值
    progressindex->reset();//让进度条重新回到开始
    progressindex->setWindowModality(Qt::WindowModal);
    progressindex->setMinimumDuration(5);
    progressindex->setWindowTitle(tr("plese waiting"));
    progressindex->setLabelText("Copying...");
    progressindex->setCancelButtonText(tr("Cancel"));
    progressindex->setRange(0,table->rowCount());
    progressindex->autoReset();//自动关闭
 
}
void Widget::progressValue()//进度条赋值
{
    prnumb++;
    progressindex->setValue(prnumb);
}
 
//关闭窗口
void Widget::closeEvent(QCloseEvent *event)
{
    qDebug()<<"进入退出处理程序";
 
    int ret = QMessageBox::question(this,"question","是否需要关闭窗口",QMessageBox::Yes,QMessageBox::No);
    //摁键一定要设置,否则会报错
    if(ret == QMessageBox::Yes)
    {
        event->accept();
        stopimage();//关闭线程image
 
        QSqlDatabase::removeDatabase("b");
 
        QSqlDatabase::removeDatabase("a");
        /*MySQL*/
 
        qDebug()<<"数据库已经完成关闭和移除";
        /*MySQL*/
    }
    else if(ret == QMessageBox::No)
    {
        event->ignore();
    }
}
 //关闭线程B
 void Widget::stopimage()//退出崩溃就是你
 {
//     qDebug()<<"进入线程关闭函数";
     if(imgthread->isRunning() == true)
     {
         imgthread->quit();
         imgthread->wait();
     }
     if(imT!=NULL)//imT不为空,删除,让他变成空的
     {
         delete imT;
     }
 }
 void Widget::on_save_clicked()
 {
     
     filepath= QFileDialog::getSaveFileName(this, "保存","../","Excel 文件(*.xls)");
     for(int i=0;i<7;i++)
     {
         hHeaderItem[i]=table->horizontalHeaderItem(i)->text(); //读取表头
     }
     throwcount = table->rowCount();//行数
     thcolcount = table->columnCount();
 
     //
         if (filepath!="")
         {
             QAxObject *excel ;
             excel = new QAxObject;//建立excel操作对象
             if (excel->setControl("Excel.Application")) //连接Excel控件
             {
                 qDebug()<<"进入程序";
                 excel->dynamicCall("SetVisible (bool Visible)","false");//不显示窗体
                 excel->setProperty("DisplayAlerts", false);
                 //不显示任何警告信息。如果为true那么在关闭是会出现类似“文件已修改,是否保存”的提示
                 QAxObject *workbooks = excel->querySubObject("WorkBooks");//获取工作簿集合
                 workbooks->dynamicCall("Add");//新建一个工作簿
                 QAxObject *workbook = excel->querySubObject("ActiveWorkBook");//获取当前工作簿
                 QAxObject *worksheet = workbook->querySubObject("Worksheets(int)", 1);
                 //访问excel中的工作表中第一个单元格
                 //保存到execl,总的来说就是操作单元格,赋值的赋值,改格式大的改格式,遵循一点,修改颜色,行高,字体这些属性,务必是要设定一个修改范围再动手
                 int i,j,colcount,rowcount;//表格的列数
                 colcount=thcolcount,rowcount=throwcount;
 
                 QAxObject *cell;
 
                 QAxObject *range;//= worksheet->querySubObject("Range(const QString&)", cellTitle);
 
                 for(i=0;i<colcount;i++)
                 {
                     //读取
                     QString columnName;
                     //修改内容
                     cell=worksheet->querySubObject("Cells(int,int)", 1, i+1);//确定操作单元格,将数据保存到哪儿,行的表头,所以要从第二列开始
                     columnName=hHeaderItem[i];//获取此处的文本内容,i是列号,就是第几列中的文本内容
                     cell->dynamicCall("SetValue(const QString&)", columnName);//打印到excel
                     //前半句是获取单元格字体,后半句是字体加粗
                     cell->querySubObject("Font")->setProperty("Bold", true);
                     cell->querySubObject("Interior")->setProperty("Color",QColor(191, 191, 191));
                     cell->setProperty("HorizontalAlignment", -4108);//xlCenter
                     cell->setProperty("VerticalAlignment", -4108);//xlCenter
                 }
                 //数据区
                 //qDebug()<<"行数"<<rowcount<<"列数"<<colcount;//列数不对
                 progress();
                 QString rowdata[7];
                 if(rowcount!=0)
                 {
//                     mutex.lock();
                     for(int datai=0;datai<rowcount;datai++)
                    {
                         mutex.lock();
                         bool chaxun = query.exec(tr("select * from acmmdatabase where id = %1 ").arg(datai+1));//是从1开始的
                         if(chaxun==false)
                         {
                             qDebug()<<query.lastError();
                         }
                         else
                             qDebug()<<"succeed";
                         if(query.next())//必须要有next,输出,必须是string
                         {
//                             (dcom->oriangle[numj]/180)*3.14159265359
                         double test[7];
                         test[0]=query.value("id").toDouble();
                         test[1]=((query.value("enfrist").toDouble())/anglepi)*piw;
                         test[2]=((query.value("ensecond").toDouble())/anglepi)*piw;
                         test[3]=((query.value("enthree").toDouble())/anglepi)*piw;
                         test[4]=((query.value("enfour").toDouble())/anglepi)*piw;
                         test[5]=((query.value("enfive").toDouble())/anglepi)*piw;
                         test[6]=((query.value("ensix").toDouble())/anglepi)*piw;
 
                         rowdata[0]=QString::number(test[0],10,6);
                         rowdata[1]=QString::number(test[1],10,6);
                         rowdata[2]=QString::number(test[2],10,6);
                         rowdata[3]=QString::number(test[3],10,6);
                         rowdata[4]=QString::number(test[4],10,6);
                         rowdata[5]=QString::number(test[5],10,6);
                         rowdata[6]=QString::number(test[6],10,6);
                         }
                        for (int dataj=0;dataj<colcount;dataj++)
                        {
                         //在此处,直接从数据库里面读取数据,不要从前界面读取内容了,实在是太麻烦了,而且容易崩溃,毕竟你的链表没有分陪空间
 //                        rowdata[dataj]=printdata[datai][dataj];
                         worksheet->querySubObject("Cells(int,int)", datai+2, dataj+1)->dynamicCall("SetValue(const QString&)",rowdata[dataj]);
                         }
                        mutex.unlock();
 
                     //进度条
                        progressValue();
 
                     }
 
                 //画框线
                 QString lrange;
                 lrange.append("A2:");//起始行列位置(原点),修改后,开始的位置会变化,A2的从A2那一行开始,A3就是第三行
                 lrange.append(colcount-1 + 'A');//终止列,-1,就是正常状态,改变colcount后面的
                 lrange.append(QString::number(rowcount +1));//终止行
                 //这个起始的位置,相当于原点,最终给一个行列的终止位置就行
                 range = worksheet->querySubObject("Range(const QString&)", lrange);
                 //querySubObject("Borders")是对边框的设置,必须有
                 range->querySubObject("Borders")->setProperty("LineStyle", QString::number(1));
                 range->querySubObject("Borders")->setProperty("Color", QColor(0, 0, 0));//颜色的设置
 
                 //调整数据区行高
                 QString rowsName;
                 rowsName.append("A2:");//起始行列位置
                 rowsName.append(colcount-1 + 'A');//终止列
                 //上面两句也可以变成
                 //rowsName.append("2:");//起始列
                 rowsName.append(QString::number(rowcount + 1));//终止行
 
                 range = worksheet->querySubObject("Range(const QString&)", rowsName);
                 range->setProperty("RowHeight", 20);//设置行高
                 range->setProperty("ColumnWidth", 20);  //设置单元格列宽
                 }
 
                 workbook->dynamicCall("SaveAs(const QString&)",QDir::toNativeSeparators(filepath));
                 //qDebug()<<"chenggong";
                 workbook->dynamicCall("Close()");//关闭工作簿
                 excel->dynamicCall("Quit()");//关闭excel
                 delete excel;
                 QMessageBox::warning(this,tr("save"),tr("导出成功"),QMessageBox::Yes);
             }
             else
             {
                  QMessageBox::warning(this,"错误","未能创建 Excel 对象,请安装 Microsoft Excel。",QMessageBox::Apply);
             }
     }
         qDebug()<<"线程一次完成";
 
 
 }
  • 线程B.h
#ifndef IMAGETHREAD_H
#define IMAGETHREAD_H
 
 
//数据库
#include<QSqlDatabase>
#include<QSqlError>
#include<QSqlQuery>
#include<QMessageBox>
//线程锁
#include<QMutex>
 
class imagethread : public QObject
{
    Q_OBJECT
public:
    explicit imagethread(QObject *parent = nullptr);
   
    QSqlDatabase db2;//此处为全局变量,也可为局部变量,根据工程需求而定
    QSqlQuery imquery;
    
 
};
 
#endif // IMAGETHREAD_H
  • 线程B
 #include "imagethread.h"
 
imagethread::imagethread(QObject *parent) : 
 QObject(parent)
{
    /*********
     数据处理
     *********/
    
}

void imagethread::drawImage()
{
 
    if(newflag==true)
    {
        db2 = QSqlDatabase::addDatabase("QMYSQL","b");
        db2.setHostName("localhost");//数据库服务器IP
        db2.setUserName("root");
        db2.setPassword("2112414cjm");   //这里输入你的密码
        db2.setDatabaseName("test");//这里输入你的数据库名
        bool flagdb=db2.open();
        if (flagdb==false)
        {
          QMessageBox::critical(NULL, QObject::tr("无法打开数据库"),"无法创建数据库连接! ", QMessageBox::Cancel);
          return;
        }
 
        imquery =QSqlQuery("",db2);
        newflag=false;
    }

    /*********************************MySql****************************************/
    if(competeleflag==true)
    {
        competeleflag=false;
        mutex.lock();
 
 
        bool fflag = imquery.exec("select enfrist from acmmdatabase");//只进行enfrist的查询
        if(fflag==false)
        {
           qDebug() <<"Error: Fail to enfrist create table." << imquery.lastError();
 
        }
        else
        {
           while(imquery.next())
           {
 
              x=52*sin( imquery.value("enfrist").toDouble()*pi )+65;
              y=52*cos(imquery.value("enfrist").toDouble()*pi )+85;
              p.drawLine(65,85,x,y);
           }
    }

    /*********
     数据处理
     *********/
     mutex.unlock();
  }
 
}

写程序时务必不要忘记,保证程序段的原子性。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值