QxOrm操作数据库

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 我们在QT应用开发专栏中对数据库操作做了基本的介绍,很多程序员对于数据库的语法并不是很熟悉,我们就需要使用ORM库来弥补该方面的不足

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.数据库的搭建

我这里比较简单

b2649bc88993488fb1fe61266e7f5ccb.png

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
5月前
|
XML Java 数据库连接
【MyBatis】MyBatis操作数据库(一)
【MyBatis】MyBatis操作数据库(一)
52 1
|
4月前
|
Oracle 关系型数据库 Java
实时计算 Flink版操作报错合集之cdc postgres数据库,当表行记录修改后报错,该如何修改
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
4月前
|
SQL 监控 关系型数据库
实时计算 Flink版操作报错合集之在设置监控PostgreSQL数据库时,将wal_level设置为logical,出现一些表更新和删除操作报错,怎么办
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
4月前
|
关系型数据库 Java 数据库
实时计算 Flink版操作报错合集之flinksql采PG数据库时报错,该如何解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
4月前
|
关系型数据库 MySQL 数据库
实时计算 Flink版操作报错合集之在处理PostgreSQL数据库遇到报错。该如何解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
4月前
|
消息中间件 关系型数据库 数据库
实时计算 Flink版操作报错合集之在使用RDS数据库作为源端,遇到只能同步21个任务,是什么导致的
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
4月前
|
SQL 数据库 Python
Django框架数据库ORM查询操作(6)
【7月更文挑战第6天】```markdown Django ORM常用数据库操作:1) 查询所有数据2) 根据ID查询 3) 精确查询 4) 分页排序
74 1
|
5月前
|
Java Devops API
阿里云云效操作报错合集之云效页面提示数据库保存不进去,该怎么办
本合集将整理呈现用户在使用过程中遇到的报错及其对应的解决办法,包括但不限于账户权限设置错误、项目配置不正确、代码提交冲突、构建任务执行失败、测试环境异常、需求流转阻塞等问题。阿里云云效是一站式企业级研发协同和DevOps平台,为企业提供从需求规划、开发、测试、发布到运维、运营的全流程端到端服务和工具支撑,致力于提升企业的研发效能和创新能力。
|
5月前
|
分布式计算 大数据 数据处理
MaxCompute操作报错合集之odps数据库T1有几百行的数据,为什么出来只有5行的数据
MaxCompute是阿里云提供的大规模离线数据处理服务,用于大数据分析、挖掘和报表生成等场景。在使用MaxCompute进行数据处理时,可能会遇到各种操作报错。以下是一些常见的MaxCompute操作报错及其可能的原因与解决措施的合集。
|
4月前
|
存储 监控 安全
安全规范问题之跟数据库交互涉及的敏感数据操作需要有哪些措施
安全规范问题之跟数据库交互涉及的敏感数据操作需要有哪些措施