QxOrm的使用
现在在网上看到的QxOrm教程,都是最基础的,只是使用该库的基本创建函数,并没有深入的介绍其强大的功能。所以我想写一个专栏,详细介绍QxOrm的使用,我会在介绍模块的时候提供对应的例子给大家参考,该篇文章与官方教程相对应,如果您对我写的有不理解的地方,请看官方文档,我这里也不是所有功能都介绍,仅介绍关系型数据库的连接部分,非关系型的请到官网查看。
本篇文章使用的数据库是sqlite,使用的可视化软件是dbeaver。
不做太多的截图,代码在gitee上都有展示,也会贴出具体的代码供学习,博主技术不强,学历不高,如果有问题,请联系我,我会及时修正
QxOrm 是一个C++库,旨在为C++用户提供对象关系映射 (ORM)功能。
QxOrm由Lionel Marty开发,他自2003年以来一直担任软件开发工程师
基于每个类的简单C++设置函数(如Java中的HibernateXML映射文件),QxOrm库提供以下功能:
- 持久性:支持最常见的数据库,如SQLite,MySQL,PostgreSQL,Oracle,MS SQL Server,MongoDB(具有1-1,1-n,n-1和n-n关系);
- 序列化:JSON,二进制和XML格式;
- 反射(或内省):动态访问类定义,检索属性和调用类方法;
- HTTP Web服务器:独立的多线程HTTP 1.1 Web服务器(支持SSL / TLS,持久连接,cookie,会话,分块响应,URL调度程序/路由);
- JSON API:与C++/Qt以外的其他技术(REST Web服务,QML应用程序,脚本语言)的互操作性。
这里不介绍对应的库编译。
接下来使用一个项目来介绍QxOrm的使用。
搭建项目环境
你可以在OxOrm中找到该项目
CMakeLists
cmake_minimum_required(VERSION 3.19)
project(QxOrmDemo)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/output)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/output)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/output)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/output)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
set(QT_VERSION 5)
set(REQUIRED_LIBS Core Gui Widgets PrintSupport Sql )
set(REQUIRED_LIBS_QUALIFIED Qt5::Core Qt5::Gui Qt5::Widgets Qt5::PrintSupport Qt5::Sql)
find_package(Qt${QT_VERSION} COMPONENTS ${REQUIRED_LIBS} REQUIRED)
#自动查找头文件路径函数
macro(FIND_INCLUDE_DIR result curdir) #定义函数,2个参数:存放结果result;指定路径curdir;
file(GLOB_RECURSE children "${curdir}/*.hpp" "${curdir}/*.h" ) #遍历获取{curdir}中*.hpp和*.h文件列表
file(GLOB SOURCE_INCLUDE ${children} ) #将文件放入 SOURCE_INCLUDE 中
set(dirlist "") #定义dirlist中间变量,并初始化
foreach(child ${children}) #for循环
string(REGEX REPLACE "(.*)/.*" "\\1" LIB_NAME ${child}) #字符串替换,用/前的字符替换/*h
if(IS_DIRECTORY ${LIB_NAME}) #判断是否为路径
list (FIND dirlist ${LIB_NAME} list_index) #判断dirlist是否含有${LIB_NAME}
if(${list_index} LESS 0)
LIST(APPEND dirlist ${LIB_NAME}) #将合法的路径加入dirlist变量中
else()
endif() #结束判断
endif()
endforeach() #结束for循环
set(${result} ${dirlist}) #dirlist结果放入result变量中
endmacro()
#自动查找源文件路径函数
macro(FIND_SRC_DIR result curdir)
file(GLOB_RECURSE children "${curdir}/*.cpp" "${curdir}/*.cc" "${curdir}/*.cxx")
file(GLOB SOURCE_SRC ${children} )
set(dirlist "")
foreach(child ${children})
string(REGEX REPLACE "(.*)/.*" "\\1" LIB_NAME ${child})
if(IS_DIRECTORY ${LIB_NAME})
list (FIND dirlist ${LIB_NAME} list_index)
if(${list_index} LESS 0)
LIST(APPEND dirlist ${LIB_NAME})
else()
endif()
endif()
endforeach()
set(${result} ${dirlist})
endmacro()
#调用函数,指定参数
#自动查找头文件路径函数
macro(FIND_UI_DIR result curdir) #定义函数,2个参数:存放结果result;指定路径curdir;
file(GLOB_RECURSE children "${curdir}/*.ui") #遍历获取{curdir}中*.hpp和*.h文件列表
file(GLOB SOURCE_UI ${children} ) #将文件放入 SOURCE_INCLUDE 中
set(dirlist "") #定义dirlist中间变量,并初始化
foreach(child ${children}) #for循环
string(REGEX REPLACE "(.*)/.*" "\\1" LIB_NAME ${child}) #字符串替换,用/前的字符替换/*h
if(IS_DIRECTORY ${LIB_NAME}) #判断是否为路径
list (FIND dirlist ${LIB_NAME} list_index) #判断dirlist是否含有${LIB_NAME}
if(${list_index} LESS 0)
LIST(APPEND dirlist ${LIB_NAME}) #将合法的路径加入dirlist变量中
else()
endif() #结束判断
endif()
endforeach() #结束for循环
set(${result} ${dirlist}) #dirlist结果放入result变量中
endmacro()
FIND_SRC_DIR(SRC_DIR_LIST ${PROJECT_SOURCE_DIR}/src)
FIND_INCLUDE_DIR(INCLUDE_DIR_LIST ${PROJECT_SOURCE_DIR}/src)
FIND_UI_DIR(UI_DIR_LIST ${PROJECT_SOURCE_DIR}/src)
#将INCLUDE_DIR_LIST中路径列表加入工程,包括第三方库的头文件路径
include_directories(
${INCLUDE_DIR_LIST} #INCLUDE_DIR_LIST路径列表加入工程
${PROJECT_SOURCE_DIR}/third_party/QxOrm/include
${PROJECT_SOURCE_DIR}/third_party/spdlog/include
)
if (CMAKE_BUILD_TYPE AND (CMAKE_BUILD_TYPE STREQUAL "Debug"))
file(GLOB LIB_ORM ${PROJECT_SOURCE_DIR}/third_party/QxOrm/libd/*.lib)
link_directories(${PROJECT_SOURCE_DIR}/thirdParty/QxOrm/libd)
file(GLOB LIB_SPDLOG ${PROJECT_SOURCE_DIR}/third_party/spdlog/Debug/*.lib)
link_directories(${PROJECT_SOURCE_DIR}/thirdParty/spdlog/Debug)
else ()
file(GLOB LIB_ORM ${PROJECT_SOURCE_DIR}/third_party/QxOrm/lib/*.lib)
link_directories(${PROJECT_SOURCE_DIR}/third_party/QxOrm/lib)
file(GLOB LIB_SPDLOG ${PROJECT_SOURCE_DIR}/third_party/spdlog/Release/*.lib)
link_directories(${PROJECT_SOURCE_DIR}/thirdParty/spdlog/Release)
endif ()
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
add_executable(${PROJECT_NAME} WIN32 main.cpp
${SOURCE_INCLUDE} ${SOURCE_SRC} ${SOURCE_UI})
#set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set_target_properties(${PROJECT_NAME} PROPERTIES
CMAKE_MSVC_RUNTIME_LIBRARY_RELEASE "MultiThreaded$<$<CONFIG:Release>:Release>")
target_link_libraries(${PROJECT_NAME} ${REQUIRED_LIBS_QUALIFIED})
target_link_libraries(${PROJECT_NAME} ${LIB_ORM} ${LIB_SPDLOG})
main.cpp
#include <QApplication>
#include "precompiled.h"
#include "person.h"
#include "turbo_log.h"
void databaseInit()
{
qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE");
qx::QxSqlDatabase::getSingleton()->setDatabaseName("./demo.db");
qx::QxSqlDatabase::getSingleton()->setHostName("localhost");
qx::QxSqlDatabase::getSingleton()->setUserName("root");
qx::QxSqlDatabase::getSingleton()->setPassword("");
QSqlError daoError = qx::dao::create_table<person>();
if (daoError.type() != QSqlError::NoError)
{
TurboLog::instance().getDailyLogger()->error("Table Member:" + daoError.text().toStdString());
}
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
databaseInit();
return app.exec();
}
person.h
#ifndef QXORMDEMO_PERSON_H
#define QXORMDEMO_PERSON_H
#include "precompiled.h"
#include "export.h"
#include <QDateTime>
class person
{
public:
long id;
QString firstName;
QString lastName;
QDateTime birthDate;
};
QX_REGISTER_HPP_APP(person, qx::trait::no_base_class_defined, 0)
#endif //QXORMDEMO_PERSON_H
person.cpp
#include "person.h"
QX_REGISTER_CPP_APP(person)
namespace qx
{
template <> void register_class(QxClass<person> & t)
{
t.setName("t_person"); // 类映射的表名
t.id(& person::id, "id");// 将id注册为主键 long 的时候会注册成自增主键
t.data(& person::firstName, "first_name");//person::firstName注册为表first_name列
t.data(& person::lastName, "last_name");
t.data(& person::birthDate, "birth_date");
}
}
我的项目使用了日志库spdlog作为日志输出,这里不做详细介绍。
上面的例子主键是long默认自增的,如果你需要其他主键的话
使用其他类型主键
author.h
#ifndef _QX_BLOG_AUTHOR_H_
#define _QX_BLOG_AUTHOR_H_
#include "precompiled.h"
#include "export.h"
#include <QString>
class author
{
QX_REGISTER_FRIEND_CLASS(author)
public:
QString m_id;
QString m_name;
};
QX_REGISTER_PRIMARY_KEY(author, QString)
QX_REGISTER_HPP_APP(author, qx::trait::no_base_class_defined, 0)
#endif // _QX_BLOG_AUTHOR_H_
author.cpp
#include "author.h"
QX_REGISTER_CPP_APP(author)
namespace qx
{
template <> void register_class(QxClass<author> & t)
{
t.id(&author::m_id, "m_id");
t.data(& author::m_name, "m_name");
}
}
这里的QX_REGISTER_PRIMARY_KEY宏就是定义非long主键的时候使用的。
如果你的类成员变量是私有的或者是保护的,则需要使用QX_REGISTER_FRIEND_CLASS来声明一些友元类。不过我这里都是public,QX_REGISTER_FRIEND_CLASS宏也可以不使用。
使用复合主键
author2.h
#ifndef QXORMDEMO_AUTHOR2_H
#define QXORMDEMO_AUTHOR2_H
#include "precompiled.h"
#include "export.h"
#include <QString>
#include <tuple>
class author2
{
public:
typedef std::tuple<QString, long, QString> type_composite_key;
static QString str_composite_key() { return "author_id_0|author_id_1|author_id_2"; }
type_composite_key m_id;
QString m_name;
QDate m_birthdate;
};
QX_REGISTER_PRIMARY_KEY(author2, author2::type_composite_key)
QX_REGISTER_HPP_APP(author2, qx::trait::no_base_class_defined, 0)
#endif //QXORMDEMO_AUTHOR2_H
author2.cpp
#include "author2.h"
QX_REGISTER_CPP_APP(author2)
namespace qx
{
template <> void register_class(QxClass<author2> & t)
{
t.id(& author2::m_id, author2::str_composite_key());
t.data(& author2::m_name, "name");
t.data(& author2::m_birthdate, "birthdate");
}
}
这样就可以建表成功。具体的可以使用可视化工具查看对应的db文件。