准备工作

1. 创建数据库文件

2. 打开数据库

3. 创建表格

4. 插入数据

5. 更新数据

6. 查询某行某列

7. 获取一列数据

8. 删除一行数据

9  关闭数据库

文章涉及到使用sqlite数据库保存整型,字符串,二进制以及时间类型的操作

在QT中调用Sqlite需在.pro文件中添加

1
QT += sql

本文使用停车系统中使用的数据库为例,我们先创建一个结构体把我们需要保存的数据整理一下,便于操作

1
2
3
4
5
6
7
8
9
10
11
typedef struct SQL_DATA{
QString t_pl; /* 车牌号 */
QString t_location; /* 车位号 */
QString t_card; /* 卡号 */
int t_featuresize; /* 人脸特征数据大小 */
QByteArray t_featuredata; /* 人脸特征数据 */
QString t_datetime; /* 存车时间 */
//int t_way; /* 存车方式 */
} SQL_DATA_TypeDef;

Q_DECLARE_METATYPE(SQL_DATA)

在对数据库进行操作时,建议创建一个数据库类,本文中创建的类名为HSSql, 在头文件中添加数据库相关头文件

1
2
3
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>

下面简单介绍数据库的简单使用

1. 创建数据库文件函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* .h文件 */
QSqlDatabase create_db(QString fileName, QString identifier = "Topp");

/* .c文件 */
QSqlDatabase HSSql::create_db(QString fileName, QString identifier)
{
QSqlDatabase db;
QString name = QCoreApplication::applicationDirPath() + "/" + fileName + ".db";
db = QSqlDatabase::addDatabase("QSQLITE", identifier);
db.setDatabaseName(name);

QTextCodec *codec = QTextCodec::codecForName("UTF-8");
QTextCodec::setCodecForLocale(codec);

return db;
}

2. 打开数据库

1
2
3
4
5
6
bool open_db(QSqlDatabase db);

bool HSSql::open_db(QSqlDatabase db)
{
return db.open();
}

3. 创建表格

1
2
3
4
5
6
7
8
9
10
bool create_table(QSqlDatabase db, QString sql_command);

bool HSSql::create_table(QSqlDatabase db, QString sql_command)
{
if(!open_db(db)){
return false;
}
QSqlQuery sqlquery(db);
return sqlquery.exec(sql_command);
}

4. 插入数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
bool insert_data_into_table(QSqlDatabase db, QString table, SQL_DATA_TypeDef* data_t);

bool HSSql::insert_data_into_table(QSqlDatabase db, QString table, SQL_DATA_TypeDef *data_t)
{
m_command = "create table userInfo( pl char(20) not null, \
location char(9) not null, \
card char(9), \
featuresize int, \
featuredata blob, \
datetime char(20))";
QSqlQuery sqlquery(db);
/* 此处可根据实际应用判断数据的唯一性 */
QString command = QString("INSERT INTO %1
(pl,location,card,featuresize,featuredata,datetime) VALUES
(:pl,:location,:card,:featuresize,:featuredata,:datetime);").arg(table);
sqlquery.prepare(command);
sqlquery.bindValue(":pl", data_t->t_pl);
sqlquery.bindValue(":location", data_t->t_location);
sqlquery.bindValue(":card", data_t->t_card);
sqlquery.bindValue(":featuresize", data_t->t_featuresize);
sqlquery.bindValue(":featuredata", data_t->t_featuredata);
sqlquery.bindValue(":datetime", data_t->t_datetime);

if(sqlquery.exec()) return true;
else {
qDebug() << sqlquery.lastError().text();
return false;
}
}

5. 更新数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool update_userInfo(QSqlDatabase db, QString table, int id, QString col_name, QString new_value);

bool HSSql::update_userInfo(QSqlDatabase db, QString table, int id, QString col_name, QString new_value)
{
QSqlQuery sqlquery(db);
QString command = QString("update %1 set %2 = %3 where id = :id;").arg(table)
.arg(col_name)
.arg(new_value);
sqlquery.bindValue(":id", id);
if(!sqlquery.exec()) {
return false;
}
else {
return true;
}
}

6. 查询某行某列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
bool getLocationByPl(const QSqlDatabase &db, const QString &table, const QString &pl, QString &location);

bool HSSql::getLocationByPl(const QSqlDatabase &db, const QString &table, const QString &pl, QString &location)
{
QSqlQuery sqlquery(db);
QString command = QString("select location from %1 where pl = '%2';").arg(table).arg(pl);
if(!sqlquery.exec(command)) {
qDebug() << sqlquery.lastError().text();
return false;
}


while (sqlquery.next()) {
location = sqlquery.value(0).toString();
// qDebug() << location;
/* 查寻过程中有时会查询到空字符串,所以我在下面加了条件判断 */
if(!location.isEmpty()) {
return true;
}
}

return true;
}

7. 获取一列数据

1
2
3
4
5
6
bool getOneColumn(const QSqlDatabase &db, const QString &table, const QString &str, 
void(*callBack)(QStringList, void *),void *pUserData);
bool getOneColumn(const QSqlDatabase &db, const QString &table, const QString &str,
void(*callBack)(QList<int> &, void *),void *pUserData);
bool getOneColumn(const QSqlDatabase &db, const QString &table, const QString &str,
void(*callBack)(QList<QByteArray> &, void *),void *pUserData);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
bool HSSql::getOneColumn(const QSqlDatabase &db, const QString &table, const QString &str, void (*callBack)(QStringList, void *), void *pUserData)
{
QSqlQuery sqlquery(db);
QStringList str_list;
QString command = QString("select %1 from %2;").arg(str).arg(table);
if(!sqlquery.exec(command)) return false;
while (sqlquery.next()) {
str_list << sqlquery.value(0).toString();
}
callBack(str_list, pUserData);
return true;
}

bool HSSql::getOneColumn(const QSqlDatabase &db, const QString &table, const QString &str, void (*callBack)(QList<int> &, void *), void *pUserData)
{
QSqlQuery sqlquery(db);
QList<int> int_list;
QString command = QString("select %1 from %2;").arg(str).arg(table);
if(!sqlquery.exec(command)) return false;
while (sqlquery.next()) {
int_list << sqlquery.value(0).toInt();
}
callBack(int_list, pUserData);
return true;
}

bool HSSql::getOneColumn(const QSqlDatabase &db, const QString &table, const QString &str, void (*callBack)(QList<QByteArray> &, void *), void *pUserData)
{
QSqlQuery sqlquery(db);
QList<QByteArray> array_list;
QString command = QString("select %1 from %2;").arg(str).arg(table);
if(!sqlquery.exec(command)) return false;
while (sqlquery.next()) {
array_list << sqlquery.value(0).toByteArray();
}
callBack(array_list, pUserData);
return true;
}

8. 删除一行数据

1
2
3
4
5
6
7
8
9
bool delete_data(QSqlDatabase db, QString table, int key);

bool HSSql::delete_data(QSqlDatabase db, QString table, int key)
{
QSqlQuery sqlquery(db);
QString delete_sql = QString("delete from %1 where pl = %2").arg(table).arg(key);
if(sqlquery.exec(delete_sql)) return true;
else return false;
}

9  关闭数据库

1
void close_db(QSqlDatabase &db) { if(open_db(db)) db.close(); }

另外,在构造函数中需要对用户创建了结构体进行注册

1
qRegisterMetaType<SQL_DATA>("SQL_DATA");

在创建表格时,需要一条数据库命令,这个命令要和我们创建的结构体对应起来,其中

char -– QString

int -–  int

图片,数据等其他二进制文件 -– blob 

datetime -– char (sqlite不支持datetime类型,所以这里使用char型代替,保存为字符串格式)

1
2
3
4
5
6
m_command = "create table userInfo( pl char(9) not null,       \
location char(9) not null, \
card char(9), \
featuresize int, \
featuredata blob, \
datetime char(20))";

下面写一个demo仅供参考,未测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
m_command = "create table userInfo( pl char(9) not null,       \
location char(9) not null, \
card char(9), \
featuresize int, \
featuredata blob, \
datetime char(20))";

m_sql = new HSSql;
QSqlDatabase db = m_sql->create_db("ToppDb");
m_sql->create_table(db, m_command);

SQL_DATA_TypeDef data_t;
data_t.t_pl = "ABC";
data_t.t_card = "961202";
data_t.t_location = "201";
data_t.t_featuresize = 123456;
data_t.t_datetime = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss");

QByteArray array;
array.resize(2);
array[0] = 0xaa;
array[1] = 0x03;
data_t.t_featuredata = array;

m_sql->insert_data_into_table(db, "userInfo", &data_t);

m_sql->close_db(db);

以下是调试过程中遇到的问题:

1. 查询的到的数据(有汉字)在UI上显示乱码

原因: 是由于保存的字符串中的汉字编码为GBK

解决:将字符串先转成UTF-8编码再保存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
QString UTF8ToGBK(const QString &utfStr);

QString ToppHMI::UTF8ToGBK(const QString &utfStr)
{
QString ustr = QTextCodec::codecForName("UTF-8")->toUnicode(utfStr.toLocal8Bit().data());
QByteArray gbk_byte = QTextCodec::codecForName("GBK")->fromUnicode(ustr);

return QString(gbk_byte);
}


QString gbkToUtf8(const QString &gbkStr);

QString CameraUI::gbkToUtf8(const QString &gbkStr)
{
QString ustr = QTextCodec::codecForName("GBK")->toUnicode(gbkStr.toLocal8Bit().data());
QByteArray utf8_byte = QTextCodec::codecForName("UTF-8")->fromUnicode(ustr);

return QString(utf8_byte);
}

2. 获取当前时间与数据保存时间之间的差值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 将查询到的时间字符串转成时间戳 */
QDateTime dt = QDateTime::fromString(start.at(i), "yyyy/MM/dd HH:mm:ss");
uint start_T = dt.toTime_t();

/* 获取当前时间时间戳 */
QDateTime eDT = QDateTime::currentDateTime();
uint et = eDT.toTime_t();
/* 取其差值 */
uint dif = et - sDT;

ushort day = dif / 86400;
unsigned char hour = ( dif % 86400 ) / 3600;
unsigned char minute = (( dif % 86400 ) % 3600) / 60;
unsigned char second = (( dif % 86400 ) % 3600) % 60;

return QString("%1天%2时%3分%4秒").arg(day,3,10,QLatin1Char('0'))
.arg(hour,2,10,QLatin1Char('0'))
.arg(minute,2,10,QLatin1Char('0'))
.arg(second,2,10,QLatin1Char('0'));

3. 提供一个保存图片的思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1. 使用opencv读取图片得到图片的Mat型值
2. 使用数据库保存Mat的type, cols, rows, elemSize, ptr

e.g
sdt.t_imgtype = ret_image.type();
sdt.t_imgcols = ret_image.cols;
sdt.t_imgrows = ret_image.rows;
sdt.t_imgsize = ret_image.elemSize();
const size_t data_size = ret_image.cols * ret_image.rows * ret_image.elemSize();
QByteArray imgByte = QByteArray::fromRawData( (const char*)ret_image.ptr(), data_size );
sdt.t_imgdata = imgByte;

3. 加载数据库中的图片
cv::Mat img = cv::Mat(sdt.t_imgrows,sdt.t_imgcols, sdt.t_imgtype, (void*)sdt.t_imgdata.data()).clone();
QImage image((const uchar*)img.data, img.cols, img.rows, QImage::Format_RGB888);

4. 使用sqlite3工具可以很方便的进行调试

下载 sqlite3二进制文件

64位电脑下载以下两个文件

解压之后如图

在地址栏输入cmd(也可以将sqlite3加入环境变量)打开调试窗口,输入

sqlite3 [数据库文件的绝对地址]

出现版本号即打开成功

输入 .table 查看数据库中的表格,这里只有一张表userInfo

更多相关数据库操作可以看 21分钟 MySQL 入门教程