QxOrm操作数据库
我们在QT应用开发专栏中对数据库操作做了基本的介绍,很多程序员对于数据库的语法并不是很熟悉,我们就需要使用ORM库来弥补该方面的不足
什么是ORM
对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。ORM框架是连接数据库的桥梁,只要提供了持久化类与表的映射关系,ORM框架在运行时就能参照映射文件的信息,把对象持久化到数据库中。
为什么使用ORM?
当我们实现一个应用程序时(不使用O/R Mapping),我们可能会写特别多数据访问层的代码,从数据库保存、删除、读取对象信息,而这些代码都是重复的。而使用ORM则会大大减少重复性代码。对象关系映射(Object Relational Mapping,简称ORM),主要实现程序对象到关系数据库数据的映射。
对象-关系映射解释:
1.简单:ORM以最基本的形式建模数据。比如ORM会将MySQL的一张表映射成一个类(模型),表的字段就是这个类的成员变量
2.精确:ORM使所有的MySQL数据表都按照统一的标准精确地映射成类,使系统在代码层面保持准确统一
3.易懂:ORM使数据库结构文档化。比如MySQL数据库就被ORM转换为了程序员可以读懂的类,程序员可以只把注意力放在他擅长的层面(当然能够熟练掌握MySQL更好)
4.易用:ORM包含对持久类对象进行CRUD操作的API,也就是讲sql查询全部封装成了编程语言中的函数,通过函数的链式组合生成最终的SQL语句。通过这种封装避免了不规范、冗余、风格不统一的SQL语句,可以避免很多人为Bug,方便编码风格的统一和后期维护。
ORM的优缺点:
优点:
1)提高开发效率,降低开发成本
2)使开发更加对象化
3)可移植
4)可以很方便地引入数据缓存之类的附加功能
缺点:
1)自动化进行关系数据库的映射需要消耗系统性能。其实这里的性能消耗还好啦,一般来说都可以忽略之。
2)在处理多表联查、where条件复杂之类的查询时,ORM的语法会变得复杂。
使用QxOrm
下面通过一个例子使用QxOrm操作数据库
项目gitee:https://gitee.com/turbolove/OxOrm.git
QxOrm编译部分不在此处介绍,此次只介绍相关的使用
1.使用cmake创建项目,cmake内容如下
cmake_minimum_required(VERSION 3.19) project(NeuralNetworkConfiguration) #指定输出目录 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) #指定C++标准 set(CMAKE_CXX_STANDARD 17) #自动编译QT文件 set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) #开启包含当前编译目录 set(CMAKE_INCLUDE_CURRENT_DIR ON) #指定QT版本和对应的库 set(QT_VERSION 5) set(REQUIRED_LIBS Core Gui Widgets Sql) set(REQUIRED_LIBS_QUALIFIED Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Sql) #寻找QT库 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") 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() #调用函数,指定参数 FIND_SRC_DIR(SRC_DIR_LIST ${PROJECT_SOURCE_DIR}/src) FIND_INCLUDE_DIR(INCLUDE_DIR_LIST ${PROJECT_SOURCE_DIR}/src) #将INCLUDE_DIR_LIST中路径列表加入工程,包括第三方库的头文件路径 include_directories( ${INCLUDE_DIR_LIST} #INCLUDE_DIR_LIST路径列表加入工程 ${PROJECT_SOURCE_DIR}/thirdparty/QxOrm/include ) #增减windows库文件 if(WIN32) set(PLAT_FROM_DEP ws2_32.lib ) endif() #增加第三方连接库文件 if (CMAKE_BUILD_TYPE AND (CMAKE_BUILD_TYPE STREQUAL "Debug")) file(GLOB LIB_QX_ORM ${PROJECT_SOURCE_DIR}/thirdparty/QxOrm/libd/*.lib) link_directories(${PROJECT_SOURCE_DIR}/thirdparty/QxOrm/libd) else () file(GLOB LIB_QX_ORM ${PROJECT_SOURCE_DIR}/thirdparty/QxOrm/lib/*.lib) link_directories(${PROJECT_SOURCE_DIR}/thirdparty/QxOrm/lib) endif () # 指定格式为utf-8 add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>") add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>") # 第三方头文件 file(GLOB INCLUDE ${PROJECT_SOURCE_DIR}/thirdparty/QxOrm/include/*.h ) #使用指定的源文件来生成目标可执行文件 add_executable(${PROJECT_NAME} WIN32 main.cpp ${SOURCE_INCLUDE} ${SOURCE_SRC} ${UI_INCLUDE}) #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_CODEEDITOR} ${LIB_QX_ORM}) if(WIN32) target_link_libraries(${PROJECT_NAME} ${PLAT_FROM_DEP}) endif()
2.新建export.h和precompiled.h
export.h
#ifndef EXPORT_H #define EXPORT_H #define _BUILDING_APP #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
precompiled.h
#ifndef DATABASE_PRECOMPILED_H #define DATABASE_PRECOMPILED_H //预编译头文件,此文件写好后几乎不会变动,可以减少后期的编译时间 #include <QxOrm.h> #include "export.h" #include <QxOrm_Impl.h> #endif //DATABASE_PRECOMPILED_H
3.新建sqlbase初始化sql
#ifndef NEURALNETWORKCONFIGURATION_SQLBASE_H #define NEURALNETWORKCONFIGURATION_SQLBASE_H #include "precompiled.h" void initDataBase() { qx::QxSqlDatabase::getSingleton()->setDriverName("QSQLITE"); qx::QxSqlDatabase::getSingleton()->setDatabaseName("EDI.db"); qx::QxSqlDatabase::getSingleton()->setHostName("localhost"); qx::QxSqlDatabase::getSingleton()->setUserName("root"); qx::QxSqlDatabase::getSingleton()->setPassword(""); } #endif //NEURALNETWORKCONFIGURATION_SQLBASE_H
4.建立数据库模型类
networkcategory.h
#ifndef NEURALNETWORKCONFIGURATION_NETWORKCATEGORY_H #define NEURALNETWORKCONFIGURATION_NETWORKCATEGORY_H #include <QString> #include "precompiled.h" class NetworkCategory { QX_REGISTER_FRIEND_CLASS(NetworkCategory) public: NetworkCategory(); ~NetworkCategory(); void setId(const QString &id); QString getId(); void setName(const QString &name); QString getName(); protected: QString id_{}; QString name_{}; }; QX_REGISTER_PRIMARY_KEY(NetworkCategory, QString) //主键不是整形的时候使用 QX_REGISTER_HPP_APP(NetworkCategory, qx::trait::no_base_class_defined, 1) #endif //NEURALNETWORKCONFIGURATION_NETWORKCATEGORY_H
networkcategory.cpp
#include "networkcategory.h" QX_REGISTER_CPP_APP(NetworkCategory) namespace qx { template <> void register_class(QxClass<NetworkCategory> & t) { // 映射表名称 t.setName("network_category"); // 映射主键 t.id(&NetworkCategory::id_, "id"); // 映射字段 t.data(&NetworkCategory::name_, "name"); } } NetworkCategory::NetworkCategory() { } NetworkCategory::~NetworkCategory() { } void NetworkCategory::setId(const QString &id) { id_ = id; } QString NetworkCategory::getId() { return id_; } void NetworkCategory::setName(const QString &name) { name_ = name; } QString NetworkCategory::getName() { return name_; }
5.界面的绘制
mainwindow.h
#ifndef NEURALNETWORKCONFIGURATION_MAINWINDOW_H #define NEURALNETWORKCONFIGURATION_MAINWINDOW_H #include <QMainWindow> #include <QTableWidget> #include <QTabWidget> #include <QComboBox> #include "networkcategory.h" #include "categorywidget.h" #include "subclasswidget.h" #include "paramswidget.h" class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow() override; protected: void initUi(); protected slots: private: QTabWidget *tab_widget_{nullptr}; CategoryWidget *category_widget_{nullptr}; SubclassWidget *subclass_widget_{nullptr}; ParamsWidget *params_widget_{nullptr}; QTableWidget *table_subclass_{nullptr}; QLineEdit *category_line_{nullptr}; QLineEdit *subclass_line_{nullptr}; QLineEdit *subclass_pic_line_{nullptr}; QComboBox *category_box_{nullptr}; QComboBox *subclass_useful_box_{nullptr}; }; #endif //NEURALNETWORKCONFIGURATION_MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h" #include <QVBoxLayout> #include <QLineEdit> #include <QPushButton> #include <QMessageBox> #include <QHeaderView> #include <QLabel> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { initUi(); } MainWindow::~MainWindow() { } void MainWindow::initUi() { setMinimumSize(1366,768); tab_widget_ = new QTabWidget(this); setCentralWidget(tab_widget_); category_widget_ = new CategoryWidget(this); subclass_widget_ = new SubclassWidget(this); params_widget_ = new ParamsWidget(this); connect(category_widget_,&CategoryWidget::sig_categoryChange,subclass_widget_,&SubclassWidget::categoryChanged); connect(subclass_widget_,&SubclassWidget::sig_subclassChange,params_widget_,&ParamsWidget::subclassChanged); category_widget_->getCategoryList(); subclass_widget_->getSubclass(); // 大类设置界面 tab_widget_->addTab(category_widget_,"大类维护"); // 子类设置界面 tab_widget_->addTab(subclass_widget_,"子类维护"); // 参数维护界面 tab_widget_->addTab(params_widget_,"参数维护"); }
categorywidget.h
#ifndef NEURALNETWORKCONFIGURATION_CATEGORYWIDGET_H #define NEURALNETWORKCONFIGURATION_CATEGORYWIDGET_H #include <QWidget> #include <QLineEdit> #include <QTableWidget> #include "networkcategory.h" class CategoryWidget : public QWidget { Q_OBJECT public: explicit CategoryWidget(QWidget *parent = nullptr); ~CategoryWidget() override; void getCategoryList(); signals: void sig_categoryChange(const QStringList &list); protected: void initUi(); void getNetworkCategories(); protected slots: void addCategory(); void deleteCategory(); private: QLineEdit *category_line_{nullptr}; QTableWidget *table_category_{nullptr}; QList<NetworkCategory> network_categories_; }; #endif //NEURALNETWORKCONFIGURATION_CATEGORYWIDGET_H
categorywidget.cpp
#include "categorywidget.h" #include <QVBoxLayout> #include <QLabel> #include <QPushButton> #include <QHeaderView> #include <QMessageBox> CategoryWidget::CategoryWidget(QWidget *parent) { initUi(); } CategoryWidget::~CategoryWidget() { } void CategoryWidget::initUi() { table_category_ = new QTableWidget(this); QVBoxLayout *layout = new QVBoxLayout(this); QHBoxLayout *hLayout1 = new QHBoxLayout(this); QLabel *labelCategory = new QLabel("大类名称",this); setLayout(layout); category_line_ = new QLineEdit(this); QPushButton *addCategoryBtn = new QPushButton("添加",this); QPushButton *deleteCategoryBtn = new QPushButton("删除",this); connect(addCategoryBtn,&QPushButton::clicked,this,&CategoryWidget::addCategory); connect(deleteCategoryBtn,&QPushButton::clicked,this,&CategoryWidget::deleteCategory); hLayout1->addWidget(labelCategory); hLayout1->addWidget(category_line_); hLayout1->addWidget(addCategoryBtn); hLayout1->addWidget(deleteCategoryBtn); layout->addItem(hLayout1); layout->addWidget(table_category_); table_category_->setEditTriggers(QAbstractItemView::NoEditTriggers); table_category_->setSelectionBehavior(QTableWidget::SelectRows); table_category_->setSelectionMode(QTableWidget::SingleSelection); table_category_->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); table_category_->setColumnCount(1); table_category_->setHorizontalHeaderLabels({"大类名称"}); getNetworkCategories(); } void CategoryWidget::getNetworkCategories() { table_category_->clearContents(); table_category_->setRowCount(0); network_categories_.clear(); qx::dao::fetch_all(network_categories_); for(NetworkCategory & network_category : network_categories_) { int row = table_category_->rowCount(); table_category_->insertRow(row); QString name = network_category.getId(); table_category_->setItem(row,0,new QTableWidgetItem(name)); } getCategoryList(); } void CategoryWidget::addCategory() { QString name = category_line_->text(); if(name.isEmpty()) { QMessageBox::critical(this,"error","大类名称不能为空"); return; } NetworkCategory category; category.setName(name); category.setId(name); QSqlError daoError = qx::dao::insert(category); if(daoError.type() != QSqlError::NoError) { QMessageBox::critical(this,"error",daoError.text()); return; } getNetworkCategories(); } void CategoryWidget::deleteCategory() { QList<QTableWidgetItem*> items = table_category_->selectedItems(); if(items.isEmpty()) { QMessageBox::critical(this,"error","请选择需要删除的项目"); return; } if(items.count() > 1) { QMessageBox::critical(this,"error","不可以一次删除多条数据"); return; } QString name = items[0]->text(); NetworkCategory category; category.setName(name); category.setId(name); QSqlError daoError = qx::dao::delete_by_id(category); if(daoError.type() != QSqlError::NoError) { QMessageBox::critical(this,"error",daoError.text()); return; } getNetworkCategories(); } void CategoryWidget::getCategoryList() { QStringList list; for(NetworkCategory & network_category : network_categories_) { QString name = network_category.getId(); list.append(name); } emit sig_categoryChange(list); }
6.数据库的搭建
我这里比较简单