QxOrm使用入门教程
ORM简介
ORM作用:
- 统一访问接口。
- 简化访问方式。
- 实现分层模式。
- 提高安全特性。
基本知识
下载QxOrm源代码
gitee下载链接:https://gitee.com/jiangtao008/QxOrm
gitee仓库下载(速度快点)如有问题可去github搜索QxOrm一样可下载。
github下载:https://github.com/QxOrm/QxOrm
QxOrm数据手册
目前未找到pdf,但是在源码中有html网页手册,如下图路径:
打开index.html文件后,点击如下图即可找到官方手册:
开源协议
使用QxOrm前一般都需要对源代码进行编译生成动态库,编译过程有时间另起文章。
为什么需要编译动态库,因为QxOrm为GNU/GPLv3开源协议,直接使用源代码则需要公开你的源代码,你的app使用他的源代码编译的动态库也需要公开源代码。。。。。,主要是使用动态库方便很多,所以像这种第三方库,没有特殊需求基本都是编译动态库使用。
其实使用开源软件编译的动态库,LGPL协议明确你的代码不需要开源,而GPL在国内这环境可以不开源!毕竟没人管你
不过开发商业app还是需要注意点,毕竟国内已经有相关案例了。
GPL认知及诉讼案例:https://blog.csdn.net/weixin_42887343/article/details/121157052
GNU/GPL V3快速指南:https://www.gnu.org/licenses/quick-guide-gplv3.html
图片来源github。
项目准备
(1)首先将QxOrm源代码放在你的项目根目录下,如下图所示:
(2)我的项目为qt项目,所以使用pro文件管理项目,在项目的pro文件中添加如下代码,具体内容看注释:
#_BUILDING_USER,通过它可以知道项目是否正在编译
DEFINES += _BUILDING_APP
#包含模块的pri文件,一些宏定义使用中需要用到
include($$PWD/QxOrm/QxOrm.pri)
#添加源代码的头文件路劲,后面代码包含头文件的时候就可以使用相对路径了
INCLUDEPATH += $$PWD/QxOrm/include
#添加编译好的动态库,QxOrm源码编译的动态库在QxOrm/lib目录中,最好编译debug、release两个版本
LIBS += -L$$PWD/QxOrm/lib
CONFIG(debug, debug|release) {
LIBS += -lQxOrmd
} else {
LIBS += -lQxOrm
}
连接数据库
qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE");
qx::QxSqlDatabase::getSingleton()->setDatabaseName("./Users.db");
qx::QxSqlDatabase::getSingleton()->setHostName("localhost");
qx::QxSqlDatabase::getSingleton()->setUserName("root");
qx::QxSqlDatabase::getSingleton()->setPassword("");
创建表格
qx::dao::create_table<User>();
其中传入的模板-User类是要求的,需要被qx注册,注册对象(数据库中对应的表结构)User示例如下:
tableUser.h文件
#ifndef USER_H
#define USER_H
#include "precompiled.h"
/******************************************************************
* User 类对应的是数据库中的 User 表,而类的属性对应的是表中的一个字段(列)
* 所以,User 类实例对应 User 数据库表中的一条记录(record)
* 以上都需要的Qx注册
* ***************************************************************/
class APP_DLL_EXPORT User
{
public:
User():id(0)
{
}
~User()
{
}
//user的属性
long id;
QString name;
int age;
};
/************************************************************
* QX_REGISTER_HPP_APP 宏是必须的,用于将 User 类注册到 QxOrm 的上下文中
* 参数一:表示要注册的当前类 - User
* 参数二:基类,如果没有基类,则使用 qx::trait::no_base_class_defined
* 参数三:用于序列化的类版本
* ***********************************************************/
QX_REGISTER_HPP_APP(User, qx::trait::no_base_class_defined, 1)
#endif // USER_H
tableUser.cpp文件
#include "tableUser.h"
#include <QxOrm_Impl.h>
//与QX_REGISTER_HPP_APP宏一样
QX_REGISTER_CPP_APP(User)
namespace qx
{
/**************************************************************
* qx::register_class() 是一个设置函数
* 用于将 User 类对应的属性 注册到 QxOrm 的上下文中
* ***********************************************************/
template <> void register_class(QxClass<User> & t)
{
// 注册 User::id <=> 数据库中的主键
t.id(&User::id, "id");
// 注册 User::name 属性,使用的 key 是 name,version 是 1。
t.data(&User::name, "name", 1);
// 注册 User::age 属性,使用的 key 是 age。
t.data(&User::age, "age");
}
}
其中#include "precompiled.h"
为预编译头文件,如下:
#ifndef PRECOMPILED_H
#define PRECOMPILED_H
//预编译头文件,此文件写好后几乎不会变动,可以减少后期的编译时间
#include <QxOrm.h>
#include "export.h"
#endif // PRECOMPILED_H
#include "export.h"
为动态库调用常用的宏定义,不具体说明了,代码如下:
#ifndef EXPORT_H
#define EXPORT_H
#ifdef _BUILDING_APP
#define APP_DLL_EXPORT QX_DLL_EXPORT_HELPER
#else
#define APP_DLL_EXPORT QX_DLL_IMPORT_HELPER
#endif
#ifdef _BUILDING_APP
#define QX_REGISTER_HPP_APP QX_REGISTER_HPP_EXPORT_DLL
#define QX_REGISTER_CPP_APP QX_REGISTER_CPP_EXPORT_DLL
#else
#define QX_REGISTER_HPP_APP QX_REGISTER_HPP_IMPORT_DLL
#define QX_REGISTER_CPP_APP QX_REGISTER_CPP_IMPORT_DLL
#endif
#endif // EXPORT_H
插入数据
插入数据给出了一系列接口,如下图:
其中最基本的就是第一个接口了,因为使用的模板,所以插入的数据可以是当个User类实体,也可以是链表、容器、哈希等等。
QList<User> aList;
for(int i = 0;i < 500;i ++)
{
User user;
user.age = qrand() % 90 + 10;
user.name = QString ("jiangtao %1").arg(i);
aList.append(user);
//qx::dao::insert(user); //插入数据1
}
qx::dao::insert(aList); //插入数据2
QVector<User> aVector;
qx::dao::insert(aVector); //插入数据3
更新/编辑数据
更新接口如下:
最常用的接口为update,其更新依据为User中的id值,使用如下:
//将id为1对应的name改为"test"
User user_update;
user_update.id = 1;
user_update.name = "test";
qx::dao::update(user_update);
在数据不存在的情况下编辑,使用update将会失败并返回错误信息。如果无法判断数据是否存在,则使用下图接口:
save接口在此数据库id对应的数据不存在时将会新增,如果存在则是编辑。
删除数据
接口如下,有两种接口,下面的destroy类型的为软删除。
示例:
User user_delete;
user_delete.id = 3;
qx::dao::delete_by_id(user_delete); //1、以id为依据
QSqlError daoError = qx::dao::delete_all<User>(); //2、删除表中所有数据
qx::QxSqlQuery delete_query("WHERE User.name = 'test 8'");
qx::dao::delete_by_query<User>(delete_query); //3、使用过滤语句删除
获取数据
官方手册中说明获取数据有三种方法,1是通过id获取,2是获取所有数据,3是通过过滤条件获取,如下图所示:
使用id为依据查询的,代码如下:
User user_fetch;
user_fetch.id = 225;
qx::dao::fetch_by_id<User>(user_fetch);
qDebug() << "Fetch user id:"<<user_fetch.id
<< " user age: " << user_fetch.age
<< " use name:"<<user_fetch.name;
判断表中数据是否存在
User user_fetch;
user_fetch.id = 225; //以id为依据
qx_bool bDaoExist = qx::dao::exist(user_fetch);
qDebug()<<"exist flag:"<<bDaoExist.toString();
查询方法
个人感觉QxOrm的查询接口比较鸡肋。。。。。。。。。。。,如下图:
而且他们官方都说了,如果他们的接口无法满足你的需求,就需要使用Qt的QtQuery了。。。。。。,最关键的是,QxOrm查询的结果只能放在链表或容器等数据结构中,而不能转为QSqlQueryModel类型供QtableView直接显示!!!(不知道是不是我没有找到地方,目前无法实现)
接下来提供一些查询示例代码:
(1)sql语句搜索
使用QxSqlQuery 类,说是sql语句吧,又不完全…
qx::QxSqlQuery query("WHERE User.name = 'test 8'");
QList<User> list_of_female;
qx::dao::fetch_by_query(query, list_of_female);
for (int i = 0; i < list_of_female.count(); i++)
;//your code
(2)单条件搜索
qx_query query;
query.where("author.sex").isEqualTo(author::female);
qx::dao::fetch_by_query(query, list_of_female);
for (int i = 0; i < list_of_female.count(); i++)
;//your code
(3)组合搜索(官方示例):
qx_query query; //搜索3 - 组合搜索
query.where("sex").isEqualTo(author::female)
.and_("age").isGreaterThan(38)
.or_("last_name").isNotEqualTo("Dupont")
.or_("first_name").like("Alfred")
.and_OpenParenthesis("id").isLessThanOrEqualTo(999)
.and_("birth_date").isBetween(date1, date2)
.closeParenthesis()
.or_("id").in(50, 999, 11, 23, 78945)
.and_("is_deleted").isNotNull()
.orderAsc("last_name", "first_name", "sex")
.limit(50, 150);
QList<User> list_of_female;
qx::dao::fetch_by_query(query, list_of_female);
for (int i = 0; i < list_of_female.count(); i++)
;//your code
查询篇,如果写的qt代码,还是建议使用Qt的QtQuery进行查询,毕竟查询结果可以直接转为QSqlQueryModel、QSqlTableModel等数据模型,然后QtableView、QtableWidget就可以直接调用setModel显示数据。
数据序列化
这个部分就是将程序中的数据转为XML、Json等文件存储,或者反过来的过程,其中程序中的数据可以从数据库中读取,从Json中读取的数据也可以写入数据库中。
序列化代码全部定义在qx::serialization命名空间中,使用如下:
(1)XML文件的读写
目前我是用的QxOrm版本xml的接口暂时未实现,不过代码先贴上,官网也有:
//=== 将容器中的用户导出到 XML 文件中(序列化) ===
QVector<User> users;
qx::serialization::xml::to_file(users, "./export_users.xml");
//=== 将 XML 中的用户导入至新容器 ===
QVector<User> usersXmlTmp;
qx::serialization::xml::from_file(usersXmlTmp, "./export_users.xml");
(2)Json文件的读写
//=== 将容器中的用户导出到 Json 文件中(序列化) ===
qx::serialization::json::to_file(users, "./export_users.json");
//=== 将 Json 文件中的用户导入至新容器 ===
QVector<User> usersJsonTmp;
qx::serialization::json::from_file(usersJsonTmp, "./export_users.json");
事务
QxOrm官方原话:
数据库事务是作为单个逻辑工作单元执行的一系列操作。如果事务执行过程中没有发生错误,则系统提交事务;如果在事务过程中发生错误,或者如果用户指定了回滚操作,那么事务中的数据操作不会持久化(保存)到数据库中。
QxOrm 库的qx::QxSession类目的就是自动管理数据库事务,使用非常方便,类中实现操作符重载,直接可以加上各个操作即可,示例代码如下:
// 创建一个会话:一个有效的线程数据库连接被自动分配给该会话并打开一个事务
qx::QxSession session;
session += qx::dao::delete_all<User>(); //删除User表中得所有数据
session += qx::dao::insert(aList); //重新加载一些数据(aList自定义的User表格数据)
updatatableView(); //对数据库做了操作后,更新表格显示
if(session.isValid())
QMessageBox::information(nullptr,"提示","事务操作成功!");
else
QMessageBox::information(nullptr,"提示","事务操作失败!\n" + session.firstError().text());
其他接口
以下接口中,除了第一个,其他接口很少使用。
(1)计算表中数据量
可知道表中有多少行数据,代码如下:
long rowCount = qx::dao::count<User>();
qDebug() << "rowCount: " << rowCount;
(2)克隆用户
UserPtr uClone = qx::clone_to_qt_shared_ptr(*u1);
qDebug() << "Clone from u1: " << uClone->id << uClone->name << uClone->age;
(3)按类名(factory)创建新用户
qx::any uAny = qx::create("User");
(4)将用户插入到 qx::cache
qx::cache::set("users", users);
(5)从 qx::cache 中删除所有元素
qx::cache::clear();
总结
QxOrm还有非常多的功能的,本文只是最最最基本使用,可以说是分享给大家的入门经验。
其中感觉QxOrm除了接口简明外,他的数据库表关联比较强大,官方重点介绍了Relationships篇章(3.8章)。
有时间的话还是可以看看QxOrm的源代码,比如很多接口使用模板,支持类、链表、等各种数据结构,我们可以研究研究实现方式。
文章可能存在很多错误,有了解的望指正,最后希望大家共同学习,共同进步!