[QT]数据库-SQLite简明教程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/humanking7/article/details/80920892


原创文章,欢迎转载。转载请注明:转载自 祥的博客

原文链接:https://blog.csdn.net/humanking7/article/details/80920892



1.源码概述

1.1. 文件QFxQSQLite.h

#ifndef QFXQSQLITE_H
#define QFXQSQLITE_H

#include <QObject>
#include <QStringList>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QTime>
#include <QDateTime>
#include <QtGlobal>

class QFxQSQLite : public QObject
{
    Q_OBJECT

public:
    QFxQSQLite(QObject *parent);
    QFxQSQLite();
    ~QFxQSQLite();
    //获取可以利用的所有SQL驱动
    static QStringList getAllDrivers();
    // 创造一个数据库名字
    static QString createDbNameByTime();
    // 由毫秒数 转换为 QTime变量
    static QTime getTimeFromMSec(const int msec);



    //建立数据库链接,打开创建表
    bool createDbConnection(const QString& filePath,  const QString& connectionName = "qt_sql_default_connection");

    //插入数据
    bool insertItem(const int id, const char* buf, const int len);

    //更新数据库
    bool updateItem(const int id, const int time);//更新 Time(第2列) 通过ID
    bool updateItem(const int id, const char* buf, const int len);//更新 Buf (第3列) 通过ID
    bool updateItem_ALL(const int id, const int time, const char* buf, const int len);//更新 Buf (第3列) 通过ID


    //查询数据
    bool getItem(const int id, int &time, char* &Buf, int &lenBuf);

    //查询最大ID
    bool findMaxID(int &id);

    //删除    
    bool delItem(const int id);

    //清空表格
    bool delTable();

    // 关闭数据库
    void closeDb();


private:
    QSqlDatabase m_database;
    QTime m_StartTime;//记录打开数据库的时间,用于换算插入数据的时间


};

#endif // QFXQSQLITE_H

1.2. 文件QFxQSQLite.cpp

#include "QFxQSQLite.h"
#include <QDebug>

#pragma execution_character_set("utf-8")

QFxQSQLite::QFxQSQLite(QObject *parent)
    : QObject(parent)
{

}

QFxQSQLite::QFxQSQLite()
{

}

QFxQSQLite::~QFxQSQLite()
{

}

// 静态类 beg
QStringList QFxQSQLite::getAllDrivers()
{
    qDebug() << "能够支持的数据库驱动有:";
    QStringList str_drivers = QSqlDatabase::drivers();
    foreach(QString str_driver, str_drivers)
        qDebug() << "  " << str_driver.toStdString().c_str();

    return str_drivers;
}

QString QFxQSQLite::createDbNameByTime()
{
    QString DbName;

    QTime cTime = QTime::currentTime();
    QDateTime cDTime = QDateTime::currentDateTime();
    DbName = cDTime.toString("[yyyyMMdd-hhmmss]") + QString("RTData.db");
    qDebug() << "DbName = " << DbName.toStdString().c_str();



    return DbName;
}


QTime QFxQSQLite::getTimeFromMSec(const int msec)
{
    QTime timeSpan(0, 0, 0, 0);
    timeSpan = timeSpan.addMSecs(msec);

    qDebug() << "time span: " << timeSpan.toString("hh-mm-ss:zzz");

    return timeSpan;

}


// 静态类 beg

bool QFxQSQLite::createDbConnection(const QString& filePath, const QString& connectionName /*= "qt_sql_default_connection"*/)
{

    // 如果数据库驱动里没有SQLite,则错误警告
    //============
    if (!QSqlDatabase::drivers().contains("QSQLITE"))
    {

        qDebug() << "Error: 系统需添加SQLite数据库驱动" << endl;

        return false;
    }


    // 建立数据库
    //============
    m_database = QSqlDatabase::addDatabase("QSQLITE", connectionName ); 
    m_database.setDatabaseName(filePath);
    m_database.setUserName("NiCai");//对于SQLite无用
    m_database.setPassword("123456");//对于SQLite无用

    // 打开数据库
    //============
    if (!m_database.open())
    {
        qDebug() << "Error: Failed to connect database.\n" 
                 << m_database.lastError() << endl;
        return false;
    }


    // 创建表格
    //============
    QSqlQuery sql_query(m_database);//不这样初始化,会出现"QSqlQuery::prepare: database not open"

    //创建一个名为 RT_Data 的表格,表格包含三列,第一列是id,第二列是time(ms),第三列是Buf(二进制)

    QString str_create_SQL = "CREATE TABLE IF NOT EXISTS RTData(Id int primary key, Time int, Buf BLOB )";
    sql_query.prepare(str_create_SQL);
    if (!sql_query.exec())
    {
        qDebug() << "Error: Fail to create table.\n" 
                 << sql_query.lastError() << endl;
        return false;
    }

    // 记录当前创建表格的时间
    //============
    m_StartTime = QTime::currentTime();


    return true;
}

void QFxQSQLite::closeDb()
{

    if (m_database.isOpen())
    {
        qDebug() << "关闭数据库" << endl;
        m_database.close();
    }
    else
    {
        qDebug() << "数据库没有打开.无法关闭" << endl;
    }


}




bool QFxQSQLite::insertItem(const int id, const char* buf, const int len)
{
    // Table
    //[ Id , Time , Buf ]
    QTime cTime = QTime::currentTime();
    int msecTime = m_StartTime.msecsTo(cTime);
    //qDebug() << "time span(ms): " << msecTime;

    QByteArray arrayBuf(buf, len);

    QSqlQuery sql_query(m_database);//不这样初始化,会出现"QSqlQuery::prepare: database not open"
    QString str_insert_SQL = "INSERT INTO RTData VALUES (?, ?, ?)";//[ Id , Time , Buf ]
    sql_query.prepare(str_insert_SQL);
    sql_query.addBindValue( id );//ID
    sql_query.addBindValue( msecTime );//Time
    sql_query.addBindValue( arrayBuf );//Buf

    if (!sql_query.exec())
    {
        qDebug() << sql_query.lastError();
        qDebug("插入数据[%d] - 失败!!!!!!!!", id);
        return false;//插入失败
    }

    qDebug("插入数据[%d] - OK!", id);



    return true;
}

bool QFxQSQLite::updateItem(const int id, const int time)
{

    QString str_update_SQL = "UPDATE RTData SET Time = :msec WHERE Id = :id";

    QSqlQuery sql_query(m_database);//不这样初始化,会出现"QSqlQuery::prepare: database not open"
    sql_query.prepare(str_update_SQL);
    sql_query.bindValue(":msec", time);
    sql_query.bindValue(":id",   id );
    if (!sql_query.exec())
    {
        qDebug() << sql_query.lastError();
        qDebug("更新数据[%d] Time - 失败!!!!!!!!", id);
        return false;
    }

    qDebug("更新数据[%d] Time - OK!", id);

    return true;
}

bool QFxQSQLite::updateItem(const int id, const char* buf, const int len)
{
    QString str_update_SQL = "UPDATE RTData SET  Buf = :ByteArray WHERE Id = :id";
    QByteArray arrayBuf(buf, len);

    QSqlQuery sql_query(m_database);//不这样初始化,会出现"QSqlQuery::prepare: database not open"
    sql_query.prepare(str_update_SQL);

    sql_query.bindValue(":ByteArray", arrayBuf);
    sql_query.bindValue(":id", id);
    if (!sql_query.exec())
    {
        qDebug() << sql_query.lastError();
        qDebug("更新数据[%d] buf - 失败!!!!!!!!", id);
        return false;
    }


    qDebug("更新数据[%d] buf - OK!", id);

    return true;
}

bool QFxQSQLite::updateItem_ALL(const int id, const int time, const char* buf, const int len)
{
    QString str_update_SQL = "UPDATE RTData SET Time = :msec, Buf = :ByteArray WHERE Id = :id";
    QByteArray arrayBuf(buf, len);

    QSqlQuery sql_query(m_database);//不这样初始化,会出现"QSqlQuery::prepare: database not open"
    sql_query.prepare(str_update_SQL);
    sql_query.bindValue(":msec", time);
    sql_query.bindValue(":ByteArray", arrayBuf);
    sql_query.bindValue(":id", id);
    if (!sql_query.exec())
    {
        qDebug() << sql_query.lastError();
        qDebug("更新数据[%d] time buf - 失败!!!!!!!!",id);
        return false;
    }


    qDebug("更新数据[%d] time buf - OK!", id);


    return true;
}

bool QFxQSQLite::findMaxID(int &id)
{
    QString str_select_max_SQL = "SELECT MAX(Id) FROM  RTData";
    QSqlQuery sql_query(m_database);//不这样初始化,会出现"QSqlQuery::prepare: database not open"
    int max_id = 0;
    sql_query.prepare(str_select_max_SQL);
    if (!sql_query.exec())
    {
        qDebug("查询最大ID失败");
        qDebug() << sql_query.lastError();
        return false;
    }
    else
    {
        while (sql_query.next())
        {
            max_id = sql_query.value(0).toInt();
            qDebug("find max ID: %d", max_id );
        }
    }

    id = max_id;
    return true;
}

bool QFxQSQLite::delItem(const int id)
{
    QSqlQuery sql_query(m_database);//不这样初始化,会出现"QSqlQuery::prepare: database not open"
    QString str_delete_SQL = "DELETE FROM RTData WHERE Id = ?";
    sql_query.prepare(str_delete_SQL);
    sql_query.addBindValue(id);
    if (!sql_query.exec())
    {

        qDebug("删除[%d] - 失败!!!!!!!!", id);
        qDebug() << sql_query.lastError();
        return false;
    }

    qDebug("删除[%d] - OK!", id);
    return true;
}

bool QFxQSQLite::delTable()
{
    QSqlQuery sql_query(m_database);//不这样初始化,会出现"QSqlQuery::prepare: database not open"
    QString str_clear_SQL = "DELETE FROM RTData";
    sql_query.prepare(str_clear_SQL);
    if (!sql_query.exec())
    {

        qDebug("删除-[Table] - 失败!!!!!!!!");
        qDebug() << sql_query.lastError();
        return false;
    }

    qDebug("删除-[Table] - OK!");
    return true;

}

bool QFxQSQLite::getItem(const int id, int &time, char* &Buf, int &lenBuf)
{//其中char* &Buf,是为了不让传回去的Buf无法使用,这样new的才会有用,不用[引用]就得用 char **pBuf,解决问题。
    QSqlQuery sql_query(m_database);//不这样初始化,会出现"QSqlQuery::prepare: database not open"
    QString str_select_SQL = "SELECT * FROM  RTData WHERE Id = ?";

    sql_query.prepare(str_select_SQL);
    sql_query.addBindValue(id);

    if (!sql_query.exec())
    {
        qDebug("getItem[%d] - 失败!!!!!!!!",id);
        qDebug() << sql_query.lastError();

        return false;
    }
    else
    {
        while (sql_query.next())
        {
            int getID = sql_query.value(0).toInt();
            time = sql_query.value(1).toInt();
            QByteArray array = sql_query.value(2).toByteArray();
            lenBuf = array.size();
            Buf = new char[lenBuf];
            memcpy(Buf, array.data(), lenBuf);

            qDebug("%d,%d, time = %d, lenArry = %d, lenBuf = %d", id, getID, time, array.size(), sizeof(Buf));
            for (int i = 0; i < lenBuf; i++)
            {
                qDebug("%02X", Buf[i]);
            }
        }
    }



    qDebug("getItem[%d] - OK!\nTime = %d", id, time);
    return true;
}


2.分析源码

2.1. 建立数据库链接,打开创建表

bool createDbConnection(const QString& filePath,  const QString& connectionName = "qt_sql_default_connection");


bool QFxQSQLite::createDbConnection(const QString& filePath, const QString& connectionName /*= "qt_sql_default_connection"*/)
{

    // 如果数据库驱动里没有SQLite,则错误警告
    //============
    if (!QSqlDatabase::drivers().contains("QSQLITE"))
    {

        qDebug() << "Error: 系统需添加SQLite数据库驱动" << endl;

        return false;
    }


    // 建立数据库
    //============
    m_database = QSqlDatabase::addDatabase("QSQLITE", connectionName ); 
    m_database.setDatabaseName(filePath);
    m_database.setUserName("NiCai");//对于SQLite无用
    m_database.setPassword("123456");//对于SQLite无用

    // 打开数据库
    //============
    if (!m_database.open())
    {
        qDebug() << "Error: Failed to connect database.\n" 
                 << m_database.lastError() << endl;
        return false;
    }


    // 创建表格
    //============
    QSqlQuery sql_query(m_database);//不这样初始化,会出现"QSqlQuery::prepare: database not open"

    //创建一个名为 RT_Data 的表格,表格包含三列,第一列是id,第二列是time(ms),第三列是Buf(二进制)

    QString str_create_SQL = "CREATE TABLE IF NOT EXISTS RTData(Id int primary key, Time int, Buf BLOB )";
    sql_query.prepare(str_create_SQL);
    if (!sql_query.exec())
    {
        qDebug() << "Error: Fail to create table.\n" 
                 << sql_query.lastError() << endl;
        return false;
    }

    // 记录当前创建表格的时间
    //============
    m_StartTime = QTime::currentTime();


    return true;
}

[1]初始设置

建立一个QSqlDatabase对象m_database,后续的操作都建立在这个对象上。

这句话SqlDatabase::drivers().contains("QSQLITE"),是用静态函数SqlDatabase::drivers()得到一个QStringList变量,这个变量是返回能够支持的数据库种类,如果不能支持,就返回false

QSqlDatabase::addDatabase("QSQLITE", connectionName )中:
- 参数1:QSQLITE是SQLite对应的驱动名
- 参数2:connectionName是自定义的连接名称(如果程序需要处理多个数据库文件的话就会这样)。Qt默认连接名称qt_sql_default_connection,用if (QSqlDatabase::contains(connectionName))判断链接是否存在(如果有这个需求的话)。

setDatabaseName()的参数是数据库文件名(可以包含路径)。如果这个数据库不存在,则会在后续操作时自动创建;如果已经存在,则后续的操作会在已有的数据库上进行。

setUserName()setPassword()是设置用户名和密码,但是对于SQLite来说没有用。

[2]打开数据库

使用open()打开数据库,返回值为bool类型,用于判断成功还是失败。

m_StartTime = QTime::currentTime();打开数据库成功后,我会记录打开数据库的时间,用于以后记录插入每条记录的时间。

[3]创建表格

对数据库的操作,都是用SQLite的语句完成的,把这些指令以QString类型,通过QSqlQuery::prepare()函数,保存在QSqlQuery对象中。也可将指令,以QString形式直接写在QSqlQuery::exec()函数的参数中,例如:

sql_query.exec("CREATE TABLE IF NOT EXISTS RTData(Id int primary key, Time int, Buf BLOB )");

创建表格语句:

CREATE TABLE <table_name> (f1 type1, f2 type2,…)

CREATE TABLE是创建表格的语句,也可用小写create tableRTData是表格的名称,可以任意取;括号中是表格的格式,上述指令表明,表格中有三列:

  • 第一列的名称(表头)是Id,这一列储存的数据类型是int
  • 第二列名称是Time,数据类型也是是int,存放毫秒数
  • 第三列的名称是Buf,数据类型是BLOB,用于存放二进制数据(我要存放一个char[]数组,大小为100,但是并不是字符串,里面有效数据有可能为\0,所以不能用SQLite中的varchar数据类型)。

如果sql_query.exec()执行成功,则创建表格成功。

注意:
其中IF NOT EXISTS是说明:如果已经有了表RTData就不创建了,不加这句,如果数据库文件中已经有了表RTData,会报错。

对于QSqlQuery sql_query(m_database);要用打开的数据库m_databaseQSqlDatabase类型的变量)进行初始化。如果不这样初始化,会出现"QSqlQuery::prepare: database not open"的错误。

PS:
扩展讲解varchar类型。
varchar(30)数据类型是字符数组,最多有30个字符(和char(30)的区别在于,varchar的实际长度是变化的,而char的长度始终是给定的值)。

2.2. 插入数据

bool QFxQSQLite::insertItem(const int id, const char* buf, const int len)
{
    // Table
    //[ Id , Time , Buf ]
    QTime cTime = QTime::currentTime();
    int msecTime = m_StartTime.msecsTo(cTime);
    //qDebug() << "time span(ms): " << msecTime;

    QByteArray arrayBuf(buf, len);//不能用char[]存储,要用QByteArray存储二进制

    QSqlQuery sql_query(m_database);//不这样初始化,会出现"QSqlQuery::prepare: database not open"
    QString str_insert_SQL = "INSERT INTO RTData VALUES (?, ?, ?)";//[ Id , Time , Buf ]
    sql_query.prepare(str_insert_SQL);
    sql_query.addBindValue( id );//ID
    sql_query.addBindValue( msecTime );//Time
    sql_query.addBindValue( arrayBuf );//Buf

    if (!sql_query.exec())
    {
        qDebug() << sql_query.lastError();
        qDebug("插入数据[%d] - 失败!!!!!!!!", id);
        return false;//插入失败
    }

    qDebug("插入数据[%d] - OK!", id);

    return true;
}

插入语句:

INSERT INTO <table_name> VALUES (value1, value2,…)

INSERT INTO是插入语句,RTData是表格名称,VALUES()是要插入的数据。
这里用addBindValue来替代语句中的?,替代的顺序与addBindValue调用的顺序相同。

也可以用绑定变量名的方式(名称绑定,原代码为位置绑定):

QString str_insert_SQL = "INSERT INTO RTData VALUES (:ID, :Time, :buf)";//[ Id , Time , Buf ]
sql_query.prepare(str_insert_SQL);
sql_query.addBindValue( ":ID", id );//ID
sql_query.addBindValue( ":Time", msecTime );//Time
sql_query.addBindValue( ":buf", arrayBuf );//Buf

sql_query.exec();

注意:
不能用char[]存储,要用QByteArray存储二进制

2.3. 更新数据

一共有3个函数,用于更新数据,现在只说明全部更新的。

bool QFxQSQLite::updateItem_ALL(const int id, const int time, const char* buf, const int len)
{
    QString str_update_SQL = "UPDATE RTData SET Time = :msec, Buf = :ByteArray WHERE Id = :id";
    QByteArray arrayBuf(buf, len);

    QSqlQuery sql_query(m_database);//不这样初始化,会出现"QSqlQuery::prepare: database not open"
    sql_query.prepare(str_update_SQL);
    sql_query.bindValue(":msec", time);
    sql_query.bindValue(":ByteArray", arrayBuf);
    sql_query.bindValue(":id", id);
    if (!sql_query.exec())
    {
        qDebug() << sql_query.lastError();
        qDebug("更新数据[%d] time buf - 失败!!!!!!!!",id);
        return false;
    }


    qDebug("更新数据[%d] time buf - OK!", id);


    return true;
}

更新(修改)的语句是:

UPDATE <table_name> SET <f1=value1>, <f2=value2>… WHERE <expression>

2.4. 查询数据

bool QFxQSQLite::getItem(const int id, int &time, char* &Buf, int &lenBuf)
{//其中char* &Buf,是为了不让传回去的Buf无法使用,这样new的才会有用,不用[引用]就得用 char **pBuf,解决问题。
    QSqlQuery sql_query(m_database);//不这样初始化,会出现"QSqlQuery::prepare: database not open"
    QString str_select_SQL = "SELECT * FROM  RTData WHERE Id = ?";

    sql_query.prepare(str_select_SQL);
    sql_query.addBindValue(id);

    if (!sql_query.exec())
    {
        qDebug("getItem[%d] - 失败!!!!!!!!",id);
        qDebug() << sql_query.lastError();

        return false;
    }
    else
    {
        while (sql_query.next())
        {
            int getID = sql_query.value(0).toInt();
            time = sql_query.value(1).toInt();
            QByteArray array = sql_query.value(2).toByteArray();
            lenBuf = array.size();
            Buf = new char[lenBuf];
            memcpy(Buf, array.data(), lenBuf);//复制数据
        }
    }



    qDebug("getItem[%d] - OK!\nTime = %d", id, time);
    return true;
}

查询语句:

SELECT <f1>, <f2>, ... FROM <table_name>
SELECT * FROM <table_name>
SELECT * FROM <table_name> WHERE <expression>

注意:
使用这个函数时,对于char*是会在函数内部中new,不要用数组常量,以及注意最后释放掉。

QFxQSQLite myDb;
QString str_path = "D:\\MyDataBase.db";
QString str_connectionName = "q-db";
myDb.createDbConnection(str_path, str_connectionName);//初始化

//插入数据等操作
int i, ID;
char buf[100];

for (ID = 0; ID < 3;ID++)
{
    for (i = 0; i < 100; i++)
    {
        buf[i] = 100 - i +ID ;//获取Buf数据
    }
    myDb.insertItem(ID, buf, sizeof(buf));//插入数据
}

//查找获取某个ID的变量
int outTime;//输出时间
char *outBuf;//一个未初始化的指针,在调用getItem()时,被new
int lenBuf;
ID = 2;
int maxID;
myDb.findMaxID(maxID);//获取这个表的最大ID,作为上限
qDebug("maxID = %d", maxID);

for (ID = 0; ID <= maxID;ID++)
{
    myDb.getItem(ID, outTime, outBuf, lenBuf);
    qDebug("ID = %d, Time = %d, buf len = %d\n", ID, outTime, lenBuf);
    for (i = 0; i < lenBuf; i++)
    {
        qDebug("%02X", outBuf[i]);//输出显示,或者使用
    }
}

delete[] outBuf;//删除变量,用完了防止内存泄漏,最好释放

2.5. 查询最大ID

bool QFxQSQLite::findMaxID(int &id)
{
    QString str_select_max_SQL = "SELECT MAX(Id) FROM  RTData";
    QSqlQuery sql_query(m_database);//不这样初始化,会出现"QSqlQuery::prepare: database not open"
    int max_id = 0;
    sql_query.prepare(str_select_max_SQL);
    if (!sql_query.exec())
    {
        qDebug("查询最大ID失败");
        qDebug() << sql_query.lastError();
        return false;
    }
    else
    {
        while (sql_query.next())
        {
            max_id = sql_query.value(0).toInt();
            qDebug("find max ID: %d", max_id );
        }
    }

    id = max_id;
    return true;
}

用了MAX(<f1>)的SQL函数语法。

2.6. 删除数据

bool QFxQSQLite::delItem(const int id)
{
    QSqlQuery sql_query(m_database);//不这样初始化,会出现"QSqlQuery::prepare: database not open"
    QString str_delete_SQL = "DELETE FROM RTData WHERE Id = ?";
    sql_query.prepare(str_delete_SQL);
    sql_query.addBindValue(id);
    if (!sql_query.exec())
    {

        qDebug("删除[%d] - 失败!!!!!!!!", id);
        qDebug() << sql_query.lastError();
        return false;
    }

    qDebug("删除[%d] - OK!", id);
    return true;
}

删除语句:

DELETE FROM  <table_name> WHERE <expression>
DELETE FROM  <table_name> WHERE <f1> = <value>

2.7. 清空表格

bool QFxQSQLite::delTable()
{
    QSqlQuery sql_query(m_database);//不这样初始化,会出现"QSqlQuery::prepare: database not open"
    QString str_clear_SQL = "DELETE FROM RTData";
    sql_query.prepare(str_clear_SQL);
    if (!sql_query.exec())
    {

        qDebug("删除-[Table] - 失败!!!!!!!!");
        qDebug() << sql_query.lastError();
        return false;
    }

    qDebug("删除-[Table] - OK!");
    return true;

}

清空语句:

DELETE FROM  <table_name> 

2.9. 关闭数据库

void QFxQSQLite::closeDb()
{
    if (m_database.isOpen())
    {
        qDebug() << "关闭数据库" << endl;
        m_database.close();
    }
    else
    {
        qDebug() << "数据库没有打开.无法关闭" << endl;
    }
}

先用isOpen()判断数据库是否打开,然后在调用close()进行关闭,这些函数都是QSqlDatabase的类成员函数。

3. 教程及其一般软件

SQLite的教程:http://www.runoob.com/sqlite/sqlite-tutorial.html

一般查看SQLite数据库文件的软件有:

  • SQLite Expert Personal : 我用的,感觉不错界面干净。(以下软件在下并为使用过,纯引用)
  • SQLite Manager:开放源代码的SQLite管理工具,用来管理本地电脑上的SQLite数据库,可以独立运行(以XULRunner方式),也可以作为Firefox、Thunderbird、Seamonkey、Songbird、Komodo、Gecko等的插件。
  • SQLite Administrator:一个用来管理SQLite数据库文件的图形化工具,可进行创建、设计和管理操作。提供代码编辑器具有自动完成和语法着色,支持中文,适合初学者。
  • SQLite Database browser:一个SQLite数据库的轻量级GUI客户端,基于Qt库开发,界面清洁,操作简单,主要是为非技术用户创建、修改和编辑SQLite数据库的工具,使用向导方式实现。

赞赏码New

猜你喜欢

转载自blog.csdn.net/humanking7/article/details/80920892
今日推荐