上一篇文章介绍了如何通过序列化操作实现对象信息的本地持久化。通过文件来序列化保存对象,在对象比较少且操作比较简单的时候是适用的。但是如果需要操作的内存对象比较多,且需要实现增删改查等各种复杂操作的时候,通过文件来保存对象已经不能满足需求了,这时候我们就需要通过数据库来对对象信息进行持久化存储了。这里介绍一下如何通过封装数据库操作类,实现内存对象的本地持久化。
由于数据库的种类比较多,我们先抽离出数据库操作的接口,然后针对某种具体类型的数据库操作类只需要继承实现对应的接口就行了,对应的类图如下所示:
数据库访问接口主要定义了对数据表的增删改查操作,对应的实现如下:
//dababase_interface.h
#ifndef _I_DATABASE_H
#define _I_DATABASE_H
#include
#include
#include
#include
#include
//数据库访问引擎的接口
class IDatabase : public QObject
{
Q_OBJECT
public:
IDatabase(QObject* parent) : QObject(parent){}
virtual ~IDatabase(){}
//向某个数据表中插入一条数据
virtual bool createRow(const QString& tableName, const QString& id, const QJsonObject& jsonObject) const = 0;
//删除某个数据表中的一条数据
virtual bool deleteRow(const QString& tableName, const QString& id) const = 0;
//查找数据库中的数据
virtual QJsonArray find(const QString& tableName, const QString& searchText) const = 0;
//根据一条ID查找数据
virtual QJsonObject readRow(const QString& tableName, const QString& id) const = 0;
//更新某条数据内容
virtual bool updateRow(const QString& tableName, const QString& id, const QJsonObject& jsonObject) const = 0;
};
#endif
针对QT对象的本地持久化,我们选取了QT默认支持的Sqlite数据库作为使用的数据库,Sqlite数据库对应的增删改查操作实现如下:
//sqlitedatabase.h
#ifndef _SQLITE_DATABASE_H_
#define _SQLITE_DATABASE_H_
#include
#include
#include "dababase_interface.h"
class SqliteDatabase : public IDatabase
{
Q_OBJECT
public:
explicit SqliteDatabase(QObject *parent, QString databaseName,QString tableName);
~SqliteDatabase();
//实现Sqlite数据库操作对应的接口
//向数据库的某个表中插入一条Json数据
bool createRow(const QString& tableName, const QString& id, const QJsonObject& jsonObject) const override;
//删除数据表中的某条数据
bool deleteRow(const QString& tableName, const QString& id) const override;
//查找包含特定文本内容的数据条目
QJsonArray find(const QString& tableName, const QString& searchText) const override;
//根据ID查找某个用户的数据
QJsonObject readRow(const QString& tableName, const QString& id) const override;
//更新某个用户的数据
bool updateRow(const QString& tableName, const QString& id, const QJsonObject& jsonObject) const override;
private:
//数据私有类用来存储类型的私有数据
class Implementation;
QScopedPointer<Implementation> implementation;
};
#endif
//sqlitedatabase.cpp
#include "sqlitedatabase.h"
#include
#include
#include
#include
class SqliteDatabase::Implementation
{
public:
Implementation(SqliteDatabase* _SqliteDatabase)
: SqliteDatabase(_SqliteDatabase)
{}
SqliteDatabase* SqliteDatabase{nullptr};
QSqlDatabase database;
QString mTableName;
QString mDatabaseName;
public:
void initdatabase()
{
if (initialise()) {
qDebug() << "Database created using Sqlite version: " + sqliteVersion();
if (createTables()) {
qDebug() << "Sqlite Database tables created";
} else {
qDebug() << "ERROR: Unable to create database tables";
}
} else {
qDebug() << "ERROR: Unable to open database";
}
}
private:
bool initialise()
{
database = QSqlDatabase::addDatabase("QSQLITE", "current_user");
database.setDatabaseName(mDatabaseName);
return database.open();
}
//如果表不存在的话创建对应的表
bool createTables()
{
return createJsonTable(mTableName);
}
bool createJsonTable(const QString& tableName) const
{
QSqlQuery query(database);
QString sqlStatement = "CREATE TABLE IF NOT EXISTS " + tableName + " (id text primary key, json text not null)";
if (!query.prepare(sqlStatement)) return false;
return query.exec();
}
//获取Sqlite的版本
QString sqliteVersion() const
{
QSqlQuery query(database);
query.exec("SELECT sqlite_version()");
if (query.next()) return query.value(0).toString();
return QString::number(-1);
}
};
SqliteDatabase::SqliteDatabase(QObject *parent, QString databaseName, QString tableName):IDatabase(parent)
{
implementation.reset(new Implementation(this));
//设置数据库的名称和表的名称
implementation->mTableName = tableName;
implementation->mDatabaseName = databaseName;
//初始化数据库
implementation->initdatabase();
}
SqliteDatabase::~SqliteDatabase()
{
}
//插入一条新数据
bool SqliteDatabase::createRow(const QString& tableName, const QString& id, const QJsonObject& jsonObject) const
{
if (tableName.isEmpty()) return false;
if (id.isEmpty()) return false;
if (jsonObject.isEmpty()) return false;
QSqlQuery query(implementation->database);
QString sqlStatement = "INSERT OR REPLACE INTO " + tableName + " (id, json) VALUES (:id, :json)";
if (!query.prepare(sqlStatement)) return false;
query.bindValue(":id", QVariant(id));
query.bindValue(":json", QVariant(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact)));
if(!query.exec()) return false;
return query.numRowsAffected() > 0;
}
//根据ID删除某条数据
bool SqliteDatabase::deleteRow(const QString& tableName, const QString& id) const
{
if (tableName.isEmpty()) return false;
if (id.isEmpty()) return false;
QSqlQuery query(implementation->database);
QString sqlStatement = "DELETE FROM " + tableName + " WHERE id=:id";
if (!query.prepare(sqlStatement)) return false;
query.bindValue(":id", QVariant(id));
if(!query.exec()) return false;
return query.numRowsAffected() > 0;
}
//通过文本查找对应的数据
QJsonArray SqliteDatabase::find(const QString& tableName, const QString& searchText) const
{
if (tableName.isEmpty()) return {};
if (searchText.isEmpty()) return {};
QSqlQuery query(implementation->database);
QString sqlStatement = "SELECT json FROM " + tableName + " where lower(json) like :searchText";
if (!query.prepare(sqlStatement)) return {};
query.bindValue(":searchText", QVariant("%" + searchText.toLower() + "%"));
if (!query.exec()) return {};
QJsonArray returnValue;
while ( query.next() ) {
auto json = query.value(0).toByteArray();
auto jsonDocument = QJsonDocument::fromJson(json);
if (jsonDocument.isObject()) {
returnValue.append(jsonDocument.object());
}
}
return returnValue;
}
//根据ID查找对应的数据
QJsonObject SqliteDatabase::readRow(const QString& tableName, const QString& id) const
{
if (tableName.isEmpty()) return {};
if (id.isEmpty()) return {};
QSqlQuery query(implementation->database);
QString sqlStatement = "SELECT json FROM " + tableName + " WHERE id=:id";
if (!query.prepare(sqlStatement)) return {};
query.bindValue(":id", QVariant(id));
if (!query.exec()) return {};
if (!query.first()) return {};
auto json = query.value(0).toByteArray();
auto jsonDocument = QJsonDocument::fromJson(json);
if (!jsonDocument.isObject()) return {};
return jsonDocument.object();
}
//通过ID来更新对应的数据结构
bool SqliteDatabase::updateRow(const QString& tableName, const QString& id, const QJsonObject& jsonObject) const
{
if (tableName.isEmpty()) return false;
if (id.isEmpty()) return false;
if (jsonObject.isEmpty()) return false;
QSqlQuery query(implementation->database);
QString sqlStatement = "UPDATE " + tableName + " SET json=:json WHERE id=:id";
if (!query.prepare(sqlStatement)) return false;
query.bindValue(":id", QVariant(id));
query.bindValue(":json", QVariant(QJsonDocument(jsonObject).toJson(QJsonDocument::Compact)));
if(!query.exec()) return false;
return query.numRowsAffected() > 0;
}
通过Sqlite数据库操作类SqliteDatabase,我们就可以以Json结构作为存储内容对数据对象执行增删改查操作了。为了方便测试,这里我们以Customer类作为测试数据说明一下SqliteDatabase操作类的使用方法。Customer默认实现了Serializable接口,支持序列化成Json对象,对应的实现如下所示:
//Serializable.h
#ifndef SERIALIZABLE_H
#define SERIALIZABLE_H
#include
//需要序列化的对象需要继承该接口
class Serializable {
public:
virtual ~Serializable() {}
virtual QVariant toVariant() const = 0;
virtual void fromVariant(const QVariant& variant) = 0;
};
#endif // SERIALIZABLE_H
//customer.h
#ifndef CUSTOMER_H
#define CUSTOMER_H
#include
#include "Serializable.h"
class Customer : public Serializable
{
public:
explicit Customer(QString name,int age,QString clientid,QString addr);
QVariant toVariant() const override;
void fromVariant(const QVariant& variant) override;
QString mName; //姓名
int mAge; //年龄
QString mClientid;//身份ID
QString mAddr; //家庭住址
QString mId; //UUID数据库唯一标识ID
};
#endif // CUSTOMER_H
//customer.cpp
#include "Customer.h"
#include
Customer::Customer(QString name, int age, QString clientid, QString addr):
mName(name),mAge(age),mClientid(clientid),mAddr(addr),mId(QUuid::createUuid().toString())
{
}
//转成JSon需要的数据对象
QVariant Customer::toVariant() const
{
QVariantMap map;
map.insert("name", mName);
map.insert("age", mAge);
map.insert("clientid",mClientid);
map.insert("addr",mAddr);
map.insert("id",mId);
return map;
}
//通过数据对象实现类
void Customer::fromVariant(const QVariant &variant)
{
QVariantMap map = variant.toMap();
mName = map.value("name").toString();
mAge = map.value("age").toInt();
mClientid = map.value("clientid").toString();
mAddr = map.value("addr").toString();
mId = map.value("id").toString();
}
实现了数据对象之后,我们就可以通过数据库操作类,将对应的类信息持久化到本地了,对应的数据库操作流程如下所示:
//main.cpp
#include
#include "dababase_interface.h"
#include "sqlitedatabase.h"
#include "customer.h"
#include
#include
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
IDatabase* sqlite = new SqliteDatabase(NULL,"ceshi.sqilte","Customer");
Customer *newCustomer = new Customer("李明",20,"67788889999","北京市海淀区西三旗");
//向数据库中新加一条数据
QJsonObject customer_obj = QJsonDocument::fromVariant(newCustomer->toVariant()).object();
sqlite->createRow("Customer",newCustomer->mId,customer_obj);
//查找一下数据看看是不是增加成功了
QJsonArray jsonArray = sqlite->find("Customer","67788889999");
//修改对应的属性
QJsonObject new_customer_obj = jsonArray.at(0).toObject();
new_customer_obj.insert("age",90);
sqlite->updateRow("Customer",new_customer_obj.value("id").toString(),new_customer_obj);
//查找是否更新成功了
QJsonObject updated_obj = sqlite->readRow("Customer",new_customer_obj.value("id").toString());
qDebug() << updated_obj.value("age").toInt();
//删除对应的用户
if(sqlite->deleteRow("Customer",updated_obj.value("id").toString()))
{
//查找一下数据看看是不是增加成功了
QJsonArray jsonArray = sqlite->find("Customer","67788889999");
qDebug() << "find:" << jsonArray.size() << "Customer";
}
return a.exec();
}
当然这里只是实现了一些数据库基本操作,如果需要额外的功能,开发者可以根据自己的业务需要添加对应的业务逻辑。