diff --git a/include/attributeTableModel.h b/include/attributeTableModel.h index 65525e0..672ff7c 100644 --- a/include/attributeTableModel.h +++ b/include/attributeTableModel.h @@ -62,6 +62,7 @@ public: void refresh(); void insertRecord(int); void removeRecord(int); + void submitChanges(); //提交更改(增、删、改) //展示列控制 //void setVisibleColumns(const QStringList& columns); @@ -98,6 +99,7 @@ private: void getDataTypes(); void loadPageData(); // 加载当前页数据 void updateTotalCount(); // 更新总记录数 + QList filterRowsByEditState(EditState); QString m_connection; QString m_tableName; diff --git a/include/dbBrowser.h b/include/dbBrowser.h index 89a4267..2b63f42 100644 --- a/include/dbBrowser.h +++ b/include/dbBrowser.h @@ -32,6 +32,7 @@ private slots: void onBtnClicked_addRecord(); void onBtnClicked_removeRecord(); + void onBtnClicked_submitChanges(); private: int tabIndex(const QString&); diff --git a/include/global.h b/include/global.h index a6bfbd9..956b79a 100644 --- a/include/global.h +++ b/include/global.h @@ -74,11 +74,11 @@ struct Model struct Attribute { - int id; + int id = -1; QString name; //中文展示名称(filed:attribute_name) QString type; //英文表示名称(filed:attribute),不可重名 int dataTypeID; - int dataLength; //filed:length_precision + int dataLength = -1; //filed:length_precision QString defaultValue; Attribute(int id, QString name, QString type, int dataTypeID, int dataLength, QString defaultVaule) diff --git a/include/sqlQueryExecutor.h b/include/sqlQueryExecutor.h index 20aa873..373562f 100644 --- a/include/sqlQueryExecutor.h +++ b/include/sqlQueryExecutor.h @@ -13,24 +13,28 @@ class SqlQueryExecutor : public QObject public: static SqlQueryExecutor& instance(); //单条SQL语句执行接口 - QSqlQuery executeSQL(const QString& strConnectionName, const QString& strSQL, const QVariantHash& params = {}, bool useTranscation = false); + QSqlQuery executeSQL(const QString& connectionName, const QString& strSQL, const QVariantHash& params = {}, bool useTranscation = false); /** * @brief 多条批量SQL语句执行接口 * @param sqlStatements SQL语句列表 * @param paramsList 参数列表(要与SQL语句一一对应) */ - QSqlQuery executeBatchSQL(const QString& strConnectionName, const QStringList& sqlStatements, + QSqlQuery executeBatchSQL(const QString& connectionName, const QStringList& sqlStatements, const QList& paramsList = QList(), bool useTranscation = false); - QHash getFiledType(const QString& strConnectionName, const QString& table, const QString& schema = "basic"); + QHash getFiledType(const QString& connectionName, const QString& table, const QString& schema = "basic"); //基于具体业务的查询接口-对外调用 - const QVector getModels(const QString&); + //属性组相关 const QVector getAttributeGroup(const QString&); const QString getArributeGropuName(const QString&, int); + //模型相关 + const QVector getModels(const QString&); bool addModel(const QString&, Model&); bool modelNameExistsInDB(const QString&, const QString&); bool modelTypeExistsInDB(const QString&, const QString&); bool removeModel(const QString&, int); + //属性相关 int getAttributeCount(const QString&, const QString&); + bool batchInserAttributis(const QString&, int, int, QList); signals: void errorOccurred(const QString& error); diff --git a/source/attributeTableModel.cpp b/source/attributeTableModel.cpp index 01c3612..dcddfec 100644 --- a/source/attributeTableModel.cpp +++ b/source/attributeTableModel.cpp @@ -328,6 +328,17 @@ void AttributeTableModel::updateTotalCount() m_paginationInfo.totalEntries = SqlQueryExecutor::instance().getAttributeCount(m_connection, m_tableName); } +QList AttributeTableModel::filterRowsByEditState(EditState state) +{ + QList result; + for(auto it = m_modifiedRows.begin(); it != m_modifiedRows.end(); it++) + { + if(it.value().state == state) + result.append(it.value()); + } + return result; +} + void AttributeTableModel::setTable(const QString& tableName) { if(m_tableName == tableName) @@ -407,6 +418,42 @@ void AttributeTableModel::removeRecord(int row) } +void AttributeTableModel::submitChanges() +{ + QList insertRows = filterRowsByEditState(New); + // QList updateRows = filterRowsByEditState(Modified); + // QList deleteRows = filterRowsByEditState(Deleted); + bool insertResult = false; + + if(!insertRows.isEmpty()) + { + QList insertAttributes; + for(const RowData& rowData : insertRows) + { + QString type = rowData.values.value(0).toString(); + QString name = rowData.values.value(1).toString(); + if(type.isEmpty() || name.isEmpty()) + continue; + + int dataTypeID = rowData.values.value(2).toInt(); + int dataLength = -1; + if(rowData.values.value(3).isValid()) + dataLength = rowData.values.value(3).toInt(); + QString defaultValue = rowData.values.value(4).toString(); + insertAttributes.emplace_back(-1, name, type, dataTypeID, dataLength, defaultValue); + } + insertResult = SqlQueryExecutor::instance().batchInserAttributis(m_connection, m_modelAttributeGroup.modelID, m_modelAttributeGroup.groupID, insertAttributes); + } + + if( (!insertRows.isEmpty() && insertResult) ) + { + m_modifiedRows.clear(); + refresh(); + } + else + emit showMessage(type_warning, QString::fromWCharArray(L"错误"), QString::fromWCharArray(L"数据提交失败,详情可查看日志文件")); +} + void AttributeTableModel::triggerSyncSignal() { emit syncDataStatus(m_modifiedRows.isEmpty(), m_paginationInfo); diff --git a/source/connectionDialog.cpp b/source/connectionDialog.cpp index 75aa1f3..88581b3 100644 --- a/source/connectionDialog.cpp +++ b/source/connectionDialog.cpp @@ -67,7 +67,7 @@ void ConnectionDialog::initialize() ui->connectionList->setColumnWidth(0, 120); //名称 //从配置文件初始化列表 QStringList connList = Settings::instance().getConnectionList(); - for(QString connID : connList) + for(const QString& connID : connList) { DatabaseConfig config = Settings::instance().loadDatabaseConfig(connID); appendConnListItem(connID, config.strConnectionName, config.strComment); diff --git a/source/dbBrowser.cpp b/source/dbBrowser.cpp index fbfe6a3..b652a77 100644 --- a/source/dbBrowser.cpp +++ b/source/dbBrowser.cpp @@ -16,6 +16,7 @@ DatabaseBrowser::DatabaseBrowser(QWidget *parent) connect(ui->btnAdd, &QPushButton::clicked, this, &DatabaseBrowser::onBtnClicked_addRecord); connect(ui->btnRemove, &QPushButton::clicked, this, &DatabaseBrowser::onBtnClicked_removeRecord); + connect(ui->btnSave, &QPushButton::clicked, this, &DatabaseBrowser::onBtnClicked_submitChanges); } DatabaseBrowser::~DatabaseBrowser() @@ -132,6 +133,8 @@ void DatabaseBrowser::onBtnClicked_addRecord() if(currentIndex.row() != -1) row = currentIndex.row();*/ model->insertRecord(row); + ui->btnSave->setEnabled(true); + ui->btnCancle->setEnabled(true); } } } @@ -149,11 +152,28 @@ void DatabaseBrowser::onBtnClicked_removeRecord() QModelIndex currentIndex = view->currentIndex(); int row = currentIndex.row(); if( row != -1) + { model->removeRecord(row); + ui->btnSave->setEnabled(true); + ui->btnCancle->setEnabled(true); + } } } } +void DatabaseBrowser::onBtnClicked_submitChanges() +{ + QWidget* widget = ui->tabWidget->currentWidget(); + AttributeView* attributeView = qobject_cast(widget); + if(attributeView) + { + QTableView* view = attributeView->view(); + AttributeTableModel* model = attributeView->model(); + if(view && model) + model->submitChanges(); + } +} + void DatabaseBrowser::onSyncDataStatus(bool hasModifiedData, const PaginationInfo& paginationInfo) { ui->btnSave->setEnabled(!hasModifiedData); diff --git a/source/dbStructureModel.cpp b/source/dbStructureModel.cpp index ff19207..b446748 100644 --- a/source/dbStructureModel.cpp +++ b/source/dbStructureModel.cpp @@ -248,6 +248,7 @@ void DBStructureModel::refreshStructure_Connection(const QString& connection) { QString groupName = SqlQueryExecutor::instance().getArributeGropuName(connection, groupID); DBStructureNode* groupNode = new DBStructureNode(GroupNode, groupName, modelNode); + groupNode->setData(Qt::UserRole + NodeDataRole::ID, groupID); modelNode->appendChild(groupNode); } connNode->appendChild(modelNode); diff --git a/source/dbStructureView.cpp b/source/dbStructureView.cpp index 0e687f5..0496bbe 100644 --- a/source/dbStructureView.cpp +++ b/source/dbStructureView.cpp @@ -200,9 +200,9 @@ void DBStructureView::showContextMenu(const QPoint& pos) DBStructureModel* model = dynamic_cast(this->model()); if(model) model->refreshStructure_Connection(connName); - }); + })->setEnabled(isConnected); menu.addSeparator(); - menu.addAction(QString::fromWCharArray(L"添加模型"), [this]{emit actionTrigger_addModel();}); + menu.addAction(QString::fromWCharArray(L"添加模型"), [this]{emit actionTrigger_addModel();})->setEnabled(isConnected); /*menu.addAction(QString::fromWCharArray(L"导入数据"), []{}); menu.addAction(QString::fromWCharArray(L"导出数据"), []{});*/ QPoint originPoint = this->mapToGlobal(QPoint(0,0)); diff --git a/source/sqlQueryExecutor.cpp b/source/sqlQueryExecutor.cpp index ce88e68..b57ffed 100644 --- a/source/sqlQueryExecutor.cpp +++ b/source/sqlQueryExecutor.cpp @@ -2,6 +2,7 @@ #include "sqlQueryExecutor.h" #include "logger.h" #include +#include SqlQueryExecutor& SqlQueryExecutor::instance() { @@ -16,12 +17,12 @@ SqlQueryExecutor::~SqlQueryExecutor() {} //单条SQL语句执行接口 -QSqlQuery SqlQueryExecutor::executeSQL(const QString& strConnectionName, const QString& strSQL, const QVariantHash& params, bool useTranscation) +QSqlQuery SqlQueryExecutor::executeSQL(const QString& connectionName, const QString& strSQL, const QVariantHash& params, bool useTranscation) { - QSqlDatabase db = QSqlDatabase::database(strConnectionName); + QSqlDatabase db = QSqlDatabase::database(connectionName); if(!db.isOpen()) { - LOG_ERROR("DB", QString("Database not open. connectionName: %1").arg(strConnectionName)); + LOG_ERROR("DB", QString("Database not open. connectionName: %1").arg(connectionName)); throw DatabaseException(db.lastError()); } @@ -31,7 +32,7 @@ QSqlQuery SqlQueryExecutor::executeSQL(const QString& strConnectionName, const Q { if(!db.transaction()) { - LOG_ERROR("DB", QString("Start transaction failed. connectionName: %1. error: %2").arg(strConnectionName, db.lastError().databaseText())); + LOG_ERROR("DB", QString("Start transaction failed. connectionName: %1. error: %2").arg(connectionName, db.lastError().databaseText())); throw DatabaseException(db.lastError()); } transactionStarted = true; @@ -59,17 +60,18 @@ QSqlQuery SqlQueryExecutor::executeSQL(const QString& strConnectionName, const Q if(transactionStarted && !db.commit()) { throw DatabaseException(db.lastError()); - LOG_ERROR("DB", QString("Commit transaction failed. connectionName: %1. error: %2").arg(strConnectionName, db.lastError().databaseText())); + LOG_ERROR("DB", QString("Commit transaction failed. connectionName: %1. error: %2").arg(connectionName, db.lastError().databaseText())); } } catch (const DatabaseException& e) { + LOG_INFO("DB", QString("DB Rollback")); // 错误处理:回滚事务(如果已开启) if(transactionStarted) { if(!db.rollback()) // 回滚失败时记录警告 { - LOG_ERROR("DB", QString("Rollback failed. connectionName: %1. error: %2").arg(strConnectionName, db.lastError().databaseText())); + LOG_ERROR("DB", QString("Rollback failed. connectionName: %1. error: %2").arg(connectionName, db.lastError().databaseText())); } } @@ -79,12 +81,12 @@ QSqlQuery SqlQueryExecutor::executeSQL(const QString& strConnectionName, const Q return sqlQuery; } //多条批量SQL语句执行接口 -QSqlQuery SqlQueryExecutor::executeBatchSQL(const QString& strConnectionName, const QStringList& sqlStatements, const QList& paramsList, bool useTranscation) +QSqlQuery SqlQueryExecutor::executeBatchSQL(const QString& connectionName, const QStringList& sqlStatements, const QList& paramsList, bool useTranscation) { - QSqlDatabase db = QSqlDatabase::database(strConnectionName); + QSqlDatabase db = QSqlDatabase::database(connectionName); if(!db.isOpen()) { - LOG_ERROR("DB", QString("Database not open. connectionName: %1").arg(strConnectionName)); + LOG_ERROR("DB", QString("Database not open. connectionName: %1").arg(connectionName)); throw DatabaseException(db.lastError()); } @@ -101,7 +103,7 @@ QSqlQuery SqlQueryExecutor::executeBatchSQL(const QString& strConnectionName, co { if(!db.transaction()) { - LOG_ERROR("DB", QString("Start transaction failed. connectionName: %1. error: %2").arg(strConnectionName, db.lastError().databaseText())); + LOG_ERROR("DB", QString("Start transaction failed. connectionName: %1. error: %2").arg(connectionName, db.lastError().databaseText())); throw DatabaseException(db.lastError()); } transactionStarted = true; @@ -136,7 +138,7 @@ QSqlQuery SqlQueryExecutor::executeBatchSQL(const QString& strConnectionName, co if(transactionStarted && !db.commit()) { throw DatabaseException(db.lastError()); - LOG_ERROR("DB", QString("Commit transaction failed. connectionName: %1. error: %2").arg(strConnectionName, db.lastError().databaseText())); + LOG_ERROR("DB", QString("Commit transaction failed. connectionName: %1. error: %2").arg(connectionName, db.lastError().databaseText())); } } } @@ -145,9 +147,10 @@ QSqlQuery SqlQueryExecutor::executeBatchSQL(const QString& strConnectionName, co // 错误处理:回滚事务(如果已开启) if(transactionStarted) { + LOG_INFO("DB", QString("DB Rollback")); if(!db.rollback()) // 回滚失败时记录警告 { - LOG_ERROR("DB", QString("Rollback failed. connectionName: %1. error: %2").arg(strConnectionName, db.lastError().databaseText())); + LOG_ERROR("DB", QString("Rollback failed. connectionName: %1. error: %2").arg(connectionName, db.lastError().databaseText())); } } @@ -157,7 +160,7 @@ QSqlQuery SqlQueryExecutor::executeBatchSQL(const QString& strConnectionName, co return lastQuery; } //获取表的字段类型信息(目前只针对PostgerSQL) -QHash SqlQueryExecutor::getFiledType(const QString& strConnectionName, const QString& table, const QString& schema) +QHash SqlQueryExecutor::getFiledType(const QString& connectionName, const QString& table, const QString& schema) { QHash fieldTypes; QString strSQL = QString( @@ -174,7 +177,7 @@ QHash SqlQueryExecutor::getFiledType(const QString& strConnect params.insert(":schema_name", schema); try { - QSqlQuery query = executeSQL(strConnectionName, strSQL, params); + QSqlQuery query = executeSQL(connectionName, strSQL, params); while(query.next()) fieldTypes.insert(query.value(0).toString(), query.value(1).toString()); } @@ -187,20 +190,20 @@ QHash SqlQueryExecutor::getFiledType(const QString& strConnect } //具体业务查询接口 -const QVector SqlQueryExecutor::getModels(const QString& strConnectionName) +const QVector SqlQueryExecutor::getModels(const QString& connectionName) { QVector models; QString strSQL = "SELECT id, model_type, model_name, remark FROM basic.model_type ORDER BY id ASC"; try { - QSqlQuery query = executeSQL(strConnectionName, strSQL); + QSqlQuery query = executeSQL(connectionName, strSQL); while(query.next()) { int id = query.value(0).toInt(); QString type = query.value(1).toString(); QString name = query.value(2).toString(); QString remark = query.value(3).toString(); - QVector groups = getModelGroups(strConnectionName, id); + QVector groups = getModelGroups(connectionName, id); models.emplace_back(id, name, type, remark, groups); } } @@ -210,13 +213,13 @@ const QVector SqlQueryExecutor::getModels(const QString& strConnectionNam } return models; //编译器的RVO/NRVO会自动优化、避免临时拷贝 } -QVector SqlQueryExecutor::getModelGroups(const QString& strConnectionName, int modelID) +QVector SqlQueryExecutor::getModelGroups(const QString& connectionName, int modelID) { QVector groups; QString strSQL = "SELECT attribute_group_id FROM basic.model_group WHERE model_type_id = " + QString::number(modelID); try { - QSqlQuery query = executeSQL(strConnectionName, strSQL); + QSqlQuery query = executeSQL(connectionName, strSQL); while(query.next()) { groups.append(query.value(0).toInt()); @@ -262,6 +265,7 @@ bool SqlQueryExecutor::addModel(const QString& connectionName, Model& model) } catch (const DatabaseException& e) { + LOG_INFO("DB", QString("DB Rollback")); if(!db.rollback()) // 回滚失败时记录警告 { LOG_ERROR("DB", QString("Rollback failed. connectionName: %1. error: %2").arg(connectionName, db.lastError().databaseText())); @@ -287,6 +291,7 @@ bool SqlQueryExecutor::addModel(const QString& connectionName, Model& model) } catch (const DatabaseException& e) { + LOG_INFO("DB", QString("DB Rollback")); if(!db.rollback()) // 回滚失败时记录警告 { LOG_ERROR("DB", QString("Rollback failed. connectionName: %1. error: %2").arg(connectionName, db.lastError().databaseText())); @@ -353,13 +358,13 @@ bool SqlQueryExecutor::removeModel(const QString& connectionName, int modelID) return true; } -const QVector SqlQueryExecutor::getAttributeGroup(const QString& strConnectionName) +const QVector SqlQueryExecutor::getAttributeGroup(const QString& connectionName) { QVector groupList; QString strSQL = "SELECT id, group_type, group_name, remark, is_public FROM basic.attribute_group ORDER BY id ASC"; try { - QSqlQuery query = executeSQL(strConnectionName, strSQL); + QSqlQuery query = executeSQL(connectionName, strSQL); while(query.next()) { int id = query.value(0).toInt(); @@ -377,7 +382,7 @@ const QVector SqlQueryExecutor::getAttributeGroup(const QString& return groupList; } -const QString SqlQueryExecutor::getArributeGropuName(const QString& strConnectionName, int groupID) +const QString SqlQueryExecutor::getArributeGropuName(const QString& connectionName, int groupID) { QString name; QString strSQL = "SELECT group_name FROM basic.attribute_group WHERE id = :id"; @@ -385,7 +390,7 @@ const QString SqlQueryExecutor::getArributeGropuName(const QString& strConnectio params.insert(":id", groupID); try { - QSqlQuery query = executeSQL(strConnectionName, strSQL, params); + QSqlQuery query = executeSQL(connectionName, strSQL, params); if(query.next()) name = query.value(0).toString(); } @@ -398,13 +403,13 @@ const QString SqlQueryExecutor::getArributeGropuName(const QString& strConnectio return name; } -int SqlQueryExecutor::getAttributeCount(const QString& strConnectionName, const QString& tableName) +int SqlQueryExecutor::getAttributeCount(const QString& connectionName, const QString& tableName) { int count = 0; QString strSQL = QString("SELECT COUNT(*) FROM %1").arg(tableName); try { - QSqlQuery query = executeSQL(strConnectionName, strSQL); + QSqlQuery query = executeSQL(connectionName, strSQL); if(query.next()) count = query.value(0).toInt(); } @@ -415,3 +420,83 @@ int SqlQueryExecutor::getAttributeCount(const QString& strConnectionName, const return count; } + +bool SqlQueryExecutor::batchInserAttributis(const QString& connectionName, int modelID, int attributeGroupID, QList attributes) +{ + //属于批量操作,需要开启事务 + QSqlDatabase db = QSqlDatabase::database(connectionName); + if(!db.isOpen()) + { + LOG_ERROR("DB", QString("Database not open. connectionName: %1").arg(connectionName)); + return false; + } + + if(!db.transaction()) + { + LOG_ERROR("DB", QString("Start transaction failed. connectionName: %1. error: %2").arg(connectionName, db.lastError().databaseText())); + return false; + } + + //先插入进属性表attribute,因为要获取插入后的自增id,所以采用逐条插入的方法 + QList attributeIDList; + for(const Attribute& atrribute : attributes) + { + qint64 attributeID = QDateTime::currentDateTime().toMSecsSinceEpoch(); + //先向model_type中插入一条记录 + QString strSQL = "INSERT INTO basic.attribute (attribute, attribute_name, data_type_id, length_precision, default_value) VALUES " + "(:type, :name, :dataType, :dataLength, :defaultValue)"; + QVariantHash params; + params.insert(":type", atrribute.type); + params.insert(":name", atrribute.name); + params.insert(":dataType", atrribute.dataTypeID); + params.insert(":dataLength", atrribute.dataLength); + params.insert(":defaultValue", atrribute.defaultValue); + try + { + QSqlQuery query = executeSQL(connectionName, strSQL, params); + attributeID = query.lastInsertId().toInt(); //lasatInsertId()会返回最近插入行的自增id + } + catch (const DatabaseException& e) + { + LOG_INFO("DB", QString("DB Rollback")); + if(!db.rollback()) // 回滚失败时记录警告 + { + LOG_ERROR("DB", QString("Rollback failed. connectionName: %1. error: %2").arg(connectionName, db.lastError().databaseText())); + } + return false; + } + attributeIDList.append(attributeID); + } + //插入数据到关联表 + QSqlQuery linkQuery(db); + QString strSQL = "INSERT INTO basic.model_attribute (model_type_id, attribute_group_id, attribute_id) VALUES (?, ?, ?)"; + linkQuery.prepare(strSQL); + QVariantList modelIds, groupIds, attributeIds; + for(const qint64& attributeID : attributeIDList) + { + modelIds << modelID; + groupIds << attributeGroupID; + attributeIds << attributeID; + } + linkQuery.addBindValue(modelIds); + linkQuery.addBindValue(groupIds); + linkQuery.addBindValue(attributeIds); + if( !linkQuery.execBatch() ) + { + LOG_ERROR("SQL", QString("SQL '%1' execBatch error: %2").arg(strSQL, linkQuery.lastError().databaseText())); + LOG_INFO("DB", QString("DB Rollback")); + if(!db.rollback()) // 回滚失败时记录警告 + { + LOG_ERROR("DB", QString("Rollback failed. connectionName: %1. error: %2").arg(connectionName, db.lastError().databaseText())); + } + return false; + } + + if(!db.commit()) // 提交 + { + LOG_ERROR("DB", QString("Commit transaction failed. connectionName: %1. error: %2").arg(connectionName, db.lastError().databaseText())); + return false; + } + + return true; +}