PowerModeler/source/sqlQueryExecutor.cpp

389 lines
13 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "global.h"
#include "sqlQueryExecutor.h"
#include "logger.h"
#include <QSqlDatabase>
SqlQueryExecutor& SqlQueryExecutor::instance()
{
//采用静态局部变量的方式,静态局部变量的初始化是在第一次访问时,以后的调用不会多次初始化,并且生命周期和程序一致
static SqlQueryExecutor instance;
return instance;
}
SqlQueryExecutor::SqlQueryExecutor()
{}
SqlQueryExecutor::~SqlQueryExecutor()
{}
//单条SQL语句执行接口
QSqlQuery SqlQueryExecutor::executeSQL(const QString& strConnectionName, const QString& strSQL, const QVariantHash& params, bool useTranscation)
{
QSqlDatabase db = QSqlDatabase::database(strConnectionName);
if(!db.isOpen())
{
LOG_ERROR("DB", QString("Database not open. connectionName: %1").arg(strConnectionName));
throw DatabaseException(db.lastError());
}
//事务
bool transactionStarted = false;
if(useTranscation)
{
if(!db.transaction())
{
LOG_ERROR("DB", QString("Start transaction failed. connectionName: %1. error: %2").arg(strConnectionName, db.lastError().databaseText()));
throw DatabaseException(db.lastError());
}
transactionStarted = true;
}
QSqlQuery sqlQuery(db);
try
{
if(!sqlQuery.prepare(strSQL))
{
LOG_ERROR("SQL", QString("SQL '%1' prepare fialed. error: %2").arg(strSQL, sqlQuery.lastError().databaseText()));
throw DatabaseException(db.lastError());
}
//绑定参数
for(auto it = params.constBegin(); it != params.constEnd(); it++)
{
sqlQuery.bindValue(it.key(), it.value());
}
if (!sqlQuery.exec())
{
LOG_ERROR("SQL", QString("SQL '%1' execute error: %2").arg(strSQL, sqlQuery.lastError().databaseText()));
throw DatabaseException(sqlQuery.lastError());
}
// 提交事务(如果已开启)
if(transactionStarted && !db.commit())
{
throw DatabaseException(db.lastError());
LOG_ERROR("DB", QString("Commit transaction failed. connectionName: %1. error: %2").arg(strConnectionName, db.lastError().databaseText()));
}
}
catch (const DatabaseException& e)
{
// 错误处理:回滚事务(如果已开启)
if(transactionStarted)
{
if(!db.rollback()) // 回滚失败时记录警告
{
LOG_ERROR("DB", QString("Rollback failed. connectionName: %1. error: %2").arg(strConnectionName, db.lastError().databaseText()));
}
}
throw; // 重新抛出异常
}
return sqlQuery;
}
//多条批量SQL语句执行接口
QSqlQuery SqlQueryExecutor::executeBatchSQL(const QString& strConnectionName, const QStringList& sqlStatements, const QList<QVariantHash>& paramsList, bool useTranscation)
{
QSqlDatabase db = QSqlDatabase::database(strConnectionName);
if(!db.isOpen())
{
LOG_ERROR("DB", QString("Database not open. connectionName: %1").arg(strConnectionName));
throw DatabaseException(db.lastError());
}
//参数数量校验
if(!paramsList.isEmpty() && sqlStatements.size() != paramsList.size())
{
LOG_ERROR("SQL", QString("SQL statement does not match the number of parameters"));
throw DatabaseException(QSqlError("SQL statement does not match the number of parameters"));
}
//事务
bool transactionStarted = false;
if(useTranscation)
{
if(!db.transaction())
{
LOG_ERROR("DB", QString("Start transaction failed. connectionName: %1. error: %2").arg(strConnectionName, db.lastError().databaseText()));
throw DatabaseException(db.lastError());
}
transactionStarted = true;
}
QSqlQuery lastQuery(db);
try
{
for(int i = 0; i < sqlStatements.size(); i++)
{
const QString& strSQL = sqlStatements.at(i);
const QVariantHash& params = paramsList.isEmpty() ? QVariantHash() : paramsList.at(i);
QSqlQuery sqlQuery(db);
if(!sqlQuery.prepare(strSQL))
{
LOG_ERROR("SQL", QString("SQL '%1' prepare fialed. error: %2").arg(strSQL, sqlQuery.lastError().databaseText()));
throw DatabaseException(db.lastError());
}
//绑定参数
for(auto it = params.constBegin(); it != params.constEnd(); it++)
{
sqlQuery.bindValue(it.key(), it.value());
}
if (!sqlQuery.exec())
{
LOG_ERROR("SQL", QString("SQL '%1' execute error: %2").arg(strSQL, sqlQuery.lastError().databaseText()));
throw DatabaseException(sqlQuery.lastError());
}
lastQuery = std::move(sqlQuery);
// 提交事务(如果已开启)
if(transactionStarted && !db.commit())
{
throw DatabaseException(db.lastError());
LOG_ERROR("DB", QString("Commit transaction failed. connectionName: %1. error: %2").arg(strConnectionName, db.lastError().databaseText()));
}
}
}
catch (const DatabaseException& e)
{
// 错误处理:回滚事务(如果已开启)
if(transactionStarted)
{
if(!db.rollback()) // 回滚失败时记录警告
{
LOG_ERROR("DB", QString("Rollback failed. connectionName: %1. error: %2").arg(strConnectionName, db.lastError().databaseText()));
}
}
throw; // 重新抛出异常
}
return lastQuery;
}
//具体业务查询接口
const QVector<Model> SqlQueryExecutor::getModels(const QString& strConnectionName)
{
QVector<Model> models;
QString strSQL = "SELECT id, model_type, model_name, remark FROM basic.model_type ORDER BY id ASC";
try
{
QSqlQuery query = executeSQL(strConnectionName, 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<int> groups = getModelGroups(strConnectionName, id);
models.emplace_back(id, name, type, remark, groups);
}
}
catch (const DatabaseException& e)
{
emit errorOccurred(QString::fromWCharArray(L"获取模型信息失败,详情可见日志文件"));
}
return models; //编译器的RVO/NRVO会自动优化、避免临时拷贝
}
QVector<int> SqlQueryExecutor::getModelGroups(const QString& strConnectionName, int modelID)
{
QVector<int> groups;
QString strSQL = "SELECT attribute_group_id FROM basic.model_group WHERE model_type_id = " + QString::number(modelID);
try
{
QSqlQuery query = executeSQL(strConnectionName, strSQL);
while(query.next())
{
groups.append(query.value(0).toInt());
}
}
catch (const DatabaseException& e)
{
LOG_ERROR("SQL", QString::fromWCharArray(L"获取模型所含属性组名称失败id:%1").arg(QString::number(modelID)));
return groups;
}
return groups;
}
bool SqlQueryExecutor::addModel(const QString& connectionName, Model& model)
{
//属于批量操作需要开启事务因为后续插入映射表时需要先插入进model_type记录的自增id所以无法在一个接口函数中执行所以事务放在在接口外部执行
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;
}
int modelID = -1;
//先向model_type中插入一条记录
QString strSQL = "INSERT INTO basic.model_type (model_type, model_name, remark) VALUES "
"(:type, :name, :remark)";
QVariantHash params;
params.insert(":type", model.type);
params.insert(":name", model.name);
params.insert(":remark", model.remark);
try
{
QSqlQuery query = executeSQL(connectionName, strSQL, params);
modelID = query.lastInsertId().toInt(); //lasatInsertId()会返回最近插入行的自增id
model.id = modelID;
}
catch (const DatabaseException& e)
{
if(!db.rollback()) // 回滚失败时记录警告
{
LOG_ERROR("DB", QString("Rollback failed. connectionName: %1. error: %2").arg(connectionName, db.lastError().databaseText()));
}
return false;
}
//然后向model_group中插入记录采用批量接口
QStringList sqlStatements;
QList<QVariantHash> paramsList;
for(int groupID : model.groups)
{
sqlStatements << "INSERT INTO basic.model_group (model_type_id, attribute_group_id) VALUES "
"(:modelID, :groupID)";
params.clear();
params.insert(":modelID", modelID);
params.insert(":groupID", groupID);
paramsList << params;
}
try
{
executeBatchSQL(connectionName, sqlStatements, paramsList);
}
catch (const DatabaseException& e)
{
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;
}
bool SqlQueryExecutor::modelNameExistsInDB(const QString& connectionName, const QString& name)
{
bool exists = false;
QString strSQL = "SELECT id FROM basic.model_type WHERE model_name = \'" + name + "\'";
try
{
QSqlQuery query = executeSQL(connectionName, strSQL);
if(query.next())
exists = true;
}
catch (const DatabaseException& e)
{
exists = true;
}
return exists;
}
bool SqlQueryExecutor::modelTypeExistsInDB(const QString& connectionName, const QString& type)
{
bool exists = false;
QString strSQL = "SELECT id FROM basic.model_type WHERE model_type = \'" + type + "\'";
try
{
QSqlQuery query = executeSQL(connectionName, strSQL);
if(query.next())
exists = true;
}
catch (const DatabaseException& e)
{
exists = true;
}
return exists;
}
bool SqlQueryExecutor::removeModel(const QString& connectionName, int modelID)
{
QString strSQL = "DELETE FROM basic.model_type WHERE id = :id"/* + QString::number(modelID)*/;
QVariantHash params;
params.insert(":id", modelID);
try
{
executeSQL(connectionName, strSQL, params, true);
}
catch (const DatabaseException& e)
{
return false;
}
return true;
}
const QVector<AttributeGroup> SqlQueryExecutor::getAttributeGroup(const QString& strConnectionName)
{
QVector<AttributeGroup> 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);
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();
bool isPublic = query.value(4).toBool();
groupList.emplace_back(id,name,type,remark,isPublic); //直接调用构造函数,避免拷贝
}
}
catch (const DatabaseException& e)
{
emit errorOccurred(QString::fromWCharArray(L"获取属性组别信息失败,详情可见日志文件"));
}
return groupList;
}
const QString SqlQueryExecutor::getArributeGropuName(const QString& strConnectionName, int groupID)
{
QString name;
QString strSQL = "SELECT group_name FROM basic.attribute_group WHERE id = :id";
QVariantHash params;
params.insert(":id", groupID);
try
{
QSqlQuery query = executeSQL(strConnectionName, strSQL, params);
if(query.next())
name = query.value(0).toString();
}
catch (const DatabaseException& e)
{
LOG_ERROR("SQL", QString::fromWCharArray(L"获取属性组名称失败id:%1").arg(QString::number(groupID)));
name = "groupID-" + QString::number(groupID);
}
return name;
}
int SqlQueryExecutor::getAttributeCount(const QString& strConnectionName, const QString& tableName)
{
int count = 0;
QString strSQL = QString("SELECT COUNT(*) FROM %1").arg(tableName);
try
{
QSqlQuery query = executeSQL(strConnectionName, strSQL);
if(query.next())
count = query.value(0).toInt();
}
catch (const DatabaseException& e)
{
LOG_ERROR("SQL", QString::fromWCharArray(L"获取属性数量失败"));
}
return count;
}