Qt連線並操作SQL資料庫(Qt學習①)
初學者記錄學習內容,如有錯誤請各位前輩指點。
DOS命令操作資料庫
雖然在window下是可以用DOS命令建立並操作資料庫,但是當涉及到一些比較複雜的資料操作的時候還是比較繁瑣,這裡做個簡單的示例,這裡對mySQL的Qt配置不做多言,貧道使用的Qt5.8已經自帶配置檔案了。
用DOS命令建立資料庫,輸入密碼後,顯示當前存在的資料庫輸入命令show databases;(命令後面一定要加“;”)
create database student;建立一個名叫student的資料庫;
use student; 對資料庫進行操作;
呃,黑色好難看,換個背景色,然後在資料庫中建立表格,並查看錶格詳情。
在資料庫中插入資料。
更多命令可自行查詢,其實這些並沒有什麼卵用,不易於資料的修改,推薦使用SQL資料庫視覺化工具,貧道使用的是“Navicat for MySQL”。
我們在Navicat中建立表格information,使用者密碼設定為777777,然後就可以將Qt專案與資料庫建立連線了。
Qt與SQL資料庫
注意要在專案中加入資料庫的應用,要先在.pro中加入sql,儲存。
QT += core gui sql
連線mySQL資料庫
在建構函式中加入這段程式碼;
QSqlDatabase dataBase=QSqlDatabase::addDatabase("QMYSQL" );
dataBase.setHostName("localhost");
dataBase.setUserName("root");
dataBase.setPassword("777777");
dataBase.setDatabaseName("picturedata");
dataBase.open();
第一句是我們所加入的資料庫的驅動型別,使用mySQL寫入”QMYSQL”,如果是使用sqlite資料庫寫入”QSQLITE”,使用Oracle資料庫寫入”QOICQ” 。如上的QSqlDatabase dataBase是定義了一個數據庫的控制代碼,又addDatabase返回並載入mySQL的驅動。資料庫支援遠端連線,setHostName()設定資料庫主機名,這裡是本地資料庫。然後是setUserName()設定資料庫使用者名稱,setPassword()設定密碼,setDatabaseName()設定資料庫名。 這些一定要一致。
如果沒有問題,連線成功以後open會返回一個bool型的變數true,我們可以接收一下這個變數qDebug顯示成功或輸出錯誤資訊。
bool ok=dataBase.open();
if(ok)
{
qDebug()<<"open database success";
}
else
{
qDebug()<<"error open database because"<<this->dataBase.lastError().text();
}
qDebug在專案編譯初期可以這樣做,但是如果不開啟編譯器是無法看到顯示的資訊的,即是說使用者是無法用這種方法來判斷是否連線成功的,所以可以改成用QMessageBox彈出小對話方塊來判斷是否能夠連線成功,後面會說到。
連線成功使用者需要完成功能對資料庫的增刪改查,先看設計的介面如圖:
上面的lineEdit填入資料,兩個pushButton,savedataBase將資料寫入資料庫,resetdataBase將資料庫中的資料清除,並清空介面,也就是說這個工程的資料庫中始終只有一條資料。同時注意,介面開啟時,lineEdit會顯示資料庫中存在的表資訊到介面上。
插入資料
我們執行工程開啟介面,在lineEdit中寫入資料,點選儲存,資料插入到資料庫中,插入語句。
注意插入語句的語法:
INSERT INTO table_name (列1, 列2,…) VALUES (值1, 值2,….);
如下的這種方式是我認為最簡單的資料庫操作函式,相當於直接在Qt中書寫SQL語句。
QSqlQuery query(dataBase);
QString datestr = ui->dateEdit->dateTime().toString("dd-MM-yyyy");
QString timestr = ui->dateEdit->time().toString("hh:mm:ss");
QString sql=QString("select *from information");
query.exec(sql);
if(query.numRowsAffected() == 0)
{
QString savesql = QString("INSERT INTO information(userName,IP,storagePath,productName,date)");
savesql += QString(" VALUES('%1','%2','%3','%4','%5')").arg(ui->userNameEdit->text())
.arg(ui->ipAddressEdit->text())
.arg(ui->storagePathEdit->text())
.arg(ui->productNameEdit->text())
.arg(datestr+' '+timestr);
bool ok=query.exec(savesql);
if(ok){
QMessageBox::about(NULL, "Save", "save new database success");
}
else{
QMessageBox::about(NULL, "Save", "error save new database");
}
}
在QString中寫插入語句insert,當然第一行程式碼也可以寫成這樣子
QString("INSERT INTO information(userName,IP,storagePath,productName,date)
VALUES('%1','%2','%3','%4','%5')");
但是由於程式碼過長不便於檢視最好進行拆分。在需要呼叫lineEdit中的輸入語句時,把值寫成引數”%1”””%2”……等,在後面加入.arg().arg()……;括號內可以寫從控制元件獲取文字資訊的函式,或直接寫入已經賦值的變數名,或者值本身。
Qt提供的一種格式化字串輸出的函式arg():
1. str=QString(“%1 %2 (%3s-%4s)”)
2. arg(“permissive”).arg(“society”).arg(1950).arg(1970);
這段程式碼中,%1, %2, %3, %4作為佔位符,將被後面的arg()函式中的內容依次替換,比如%1將被替換成permissive,%2將被替換成society,%3將被替換成1950,%4將被替換曾1970,最後,這句程式碼輸出為:permissive society (1950s-1970s). arg()函式比起sprintf()來是型別安全的,同時它也接受多種的資料型別作為引數,都會被轉化為QString型別然後進行替換。
使用bool ok=query.exec(savesql)執行該sql語句,返回一個bool型的結果。如上面說到的,可用一個變數來接收這個結果,由結果的不同true或fulse返回不同的QMessageBox小對話來讓使用者得到是否執行成功。
前面的一些程式碼在最後一部分會有說到。
修改資料
在資料庫中已經有資料的情況下,可以實現修改介面文字,點選儲存,實現資料庫中的值的修改,修改語句。
QString updatesql = QString("UPDATE information SET userName='%1',IP='%2',storagePath='%3',productName='%4',date='%5' WHERE IP=%6")
.arg(ui->userNameEdit->text())
.arg(ui->ipAddressEdit->text())
.arg(ui->storagePathEdit->text())
.arg(ui->productNameEdit->text())
.arg(datestr+' '+timestr)
.arg(oldIP);
bool ok=query.exec(updatesql);
if(ok){
QMessageBox::about(NULL, "Save", "save database success");}
else{
QMessageBox::about(NULL, "Save", "error save database");}
注意updata語句的語法是:
UPDATE 表名稱 SET 列名稱 = 新值 WHERE 列名稱 = 某值
where之後需要給出修改的條件,如題要獲取未修改的資料庫中的ip的值。
貧僧之前將修改的條件設定為介面上獲取的IP:
QString updatesql = QString("UPDATE information SET userName='%1',IP='%2',storagePath='%3',productName='%4',date='%5' WHERE IP=%6")
.arg(ui->userNameEdit->text())
.arg(ui->ipAddressEdit->text())
.arg(ui->storagePathEdit->text())
.arg(ui->productNameEdit->text())
.arg(datestr+' '+timestr).arg(ui->ipAddressEdit->text());
此時%6所接收到的IP是你準備要修改成的,而此時資料庫中並沒有的值,導致sql語句執行之後資料庫中毫無變化,修改失敗。可以在執行修改之前,定義一個變數oldIP去儲存資料庫中原來的IP值,寫到主函式中。作為update語句的條件傳入。
QString oldIP;
oldIP=ui->ipAddressEdit->text();
刪除資料
接著實現點選重新整理按鈕,刪除資料庫中的使用者資訊,刪除語句。
注意delete的語法是DELETE FROM 表名稱 WHERE 列名稱 = 值;
IP條件引用與上例相同。
QSqlQuery deletequery(dataBase);
QString deletesql = QString("DELETE FROM information WHERE IP='%1'").arg(oldIP);
bool ok=deletequery.exec(deletesql);
if(ok)
{
QMessageBox::about(NULL, "Reset", "Reset database success");
ui->userNameEdit->clear();
ui->ipAddressEdit->clear();
ui->storagePathEdit->clear();
ui->productNameEdit->clear();
ui->dateEdit->clear();
}
else
{
QMessageBox::about(NULL, "Reset", "error reset database");
}
刪除資料後,需要清空lineEdit()上的資料,呼叫.clear();
其他的幾個重點問題
判斷資料庫是否為空
我們再回頭看插入語句的程式碼會發現一個問題,這裡有一個問題,只有一個儲存save按鈕,當資料庫是否為空,兩種情況點選button是執行update或insert這兩種哪一種sql語句呢?這裡就需要加入一個判斷資料庫是否為空。使用select語句查詢語句查詢表,query.exec()執行語句。
QString sql=QString("select *from information");
query.exec(sql);
然後呼叫query.numRowsAffected()
int QSqlQuery::numRowsAffected () const
返回有多少行記錄被結果集的 SQL語句影響了,如果不能確定將返回 -1 。注意,對於 SELECT語句,此值等同於size()如果查詢處於非活動狀態(isActive()返回FALSE),將反回 -1。
如果資料庫為空,將有0行語句被影響,那麼就執行插入語句,否則執行修改語句。
QdateTimeEdit的處理
注意介面中獲取時間和日期的控制元件,使用的是QdateTimeEdit,從這個控制元件獲得的資料插入到資料庫中時總是出現問題,此處的是從中.dateTime()和.time()單獨獲取日期和時間,以dd-MM-yyyy和hh:mm:ss的形式toString();單獨儲存,同時傳入。
QString datestr = ui->dateEdit->dateTime().toString("dd-MM-yyyy");
QString timestr = ui->dateEdit->time().toString("hh:mm:ss");
寫入資料庫時進行組合arg(datestr+’ ‘+timestr)
資料庫顯示到介面
如果資料庫中有已插入的資料,專案執行起來時,資料會從資料庫載入到lineEdit上顯示到介面中,這裡自然又要用到select查詢語句,涉及到對執行SQL語句後返回的結果集的操作,結果集其實就是查詢到的所有記錄的集合,需要注意這個集合中的記錄是從0開始編號的,這裡使用value(int n)來獲取屬性的值,其中n表示你查詢的第n個屬性。比如value(0),返回userName的值,轉化為String型別.settext()顯示到lineEdit中。
關注:MySql執行之後返回的結果集指標都指向陣列第一條語句之前,因此需要呼叫一個next(),更便於使用while迴圈顯示,而且每執行一次該函式,便指向相鄰的下一條記錄。Sqllite資料庫也是如此,要先呼叫next()。
QSqlQuery showquery(dataBase);
QString showsql=QString("select *from information");
showquery.exec(showsql);
if(showquery.numRowsAffected() != 0)
{
showquery.next();
ui->userNameEdit->setText(showquery.value(0).toString());
ui->ipAddressEdit->setText(showquery.value(1).toString());
ui->storagePathEdit->setText(showquery.value(2).toString());
ui->productNameEdit->setText(showquery.value(3).toString());
ui->dateEdit->setDateTime(QDateTime::fromString(showquery.value(4).toString(),"dd-MM-yyyy hh:mm:ss"));
}
我們注意到,資料庫中始終保持了一行程式碼需要顯示,但是如果資料庫中是3×5的表,select*以後返回的結果集是個二維陣列,若需要用上述的方式的顯示到3×5的lineEdit中的話,可以使用while迴圈呼叫query.value();
while(query.next()){
ui->button1->setText(query.value(0).toString());
ui->button2->setText(query.value(1).toString());
ui->button3->setText(query.value(2).toString());
ui->button4->setText(query.value(3).toString());
ui->button5->setText(query.value(4).toString());
}
While語句開始執行,第一次執行query.next()後指向第一行資料,然後五次呼叫.value(),遍歷五列資料(value只管列),逐次顯示到介面上。
結束,如有錯誤,還望指正。