#ifndef _DATA_TABLE_ #define _DATA_TABLE_ #define _INSENSETIVE #include #include #include #include #include #include #include #include "DataColumn.h" #include "UtilityGlobal.h" #include "DataTable.h" #include "DataSet.h" using namespace std; class DataColumnCollection; class DataRow; class DataRowCollection; class DataTable; enum class data_type{ INTEGER = 1, /* 整型 */ TEXT, /* 字符串 */ REAL, /* 浮点型 */ BLOB, /* 二进制数据 */ }; /* 定义:表示 DataTable或DataRow列的集合. DataColumnCollection定义 DataTable的架构,并确定每个DataColumn可以包含的数据类型 DataColumnCollection和DataTable是组合关系。整体和个体不可独立存在,生命周期一致 DataColumnCollection使用 Add 和 Remove 方法来插入和删除 DataColumn 对象。 使用Count确定集合中的DataColumn对象数。 使用Contains验证集合中是否存在指定的索引或列名。 */ class UTILITY_API DataColumnCollection { friend class DataTable; friend class DataRow; friend class DataRowCollection; public: DataColumn& operator[](int index); //获取或设置指定索引的列 DataColumn& operator[](const QString& column_name); //获取或设置指定列名的列 DataColumn* getColumnDataByColumnName(const QString &column_name); DataColumn* at(int index); void Add(DataColumn* p_column); //将指定的 DataColumn 对象添加到 DataColumnCollection void Add(DataColumn* p_column, int index); //将指定的 DataColumn 对象添加到 DataColumnCollection对应索引处 DataColumn& Add(const QString& column_name); //创建一个具有指定名称的 DataColumn 对象,并将其添加到 DataColumnCollection 中。返回新创建的DataColumn void Remove(DataColumn& dc); //从DataTable中移除指定的 DataColumn 对象。(需从移除DataRow的DataColumnCollection中移除该列) void Remove(const QString& column_name); //从集合中移除具有指定名称的 DataColumn 对象(需从移除DataRow的DataColumnCollection中移除该列) void RemoveAt(int index); //从集合中移除指定索引位置的列(需从移除DataRow的DataColumnCollection中移除该列) bool Contains(const QString& column_name); //返回集合中是否包含指定列名的列 int Count(); //获取集合中的元素总数 int Count() const; //获取集合中的元素总数 /* .NET 中 clear public 提供,这里沿袭这个做法,但是这个函数极度危险,不要乱用 */ void Clear(); //清除集合中的所有列,不清除DataTable void CopyTo(DataColumnCollection* arr, int index); //将整个集合复制到指定集合中,从该集合内的指定索引处开始复制。 void setFromValue(DataColumnCollection *arr, int index); //void Table(DataTable* tb); //DataTable* Table(); bool isHasKeyAndValue(QString column_name,QString value); DataColumn* getColumnRealValue(const QString& column_name) { DataColumn* p = getColumnDataByColumnName(column_name); return p; } bool isColumnValueNull(const QString& column_name) { DataColumn* p = getColumnDataByColumnName(column_name); if (!p) { return true; } if(p->Value().isNull()) { return true; } return false; } template bool getColumnValue(const QString &column_name,T& value) { DataColumn* p = getColumnDataByColumnName(column_name); if (!p) return false; if constexpr (std::is_same_v) { value = p->toInt(); return true; } else if constexpr (std::is_same_v) { value = p->toDouble(); return true; } else if constexpr (std::is_same_v) { value = p->toBool(); return true; } else if constexpr (std::is_same_v) { value = p->toString().toStdString(); return true; } else if constexpr (std::is_same_v) { value = p->toString(); return true; } else { return false; // Unsupported type } } template T getColumnValue(const QString& column_name) { DataColumn* p = getColumnDataByColumnName(column_name); if (!p) { // 如果没有找到列名对应的列,返回一个默认值(适用于简单类型) return T{}; } // 通用的转换函数 auto convert = [](auto&& value) -> T { return static_cast(value); }; // 如果类型是支持的 if constexpr (std::is_same_v) { return convert(p->toInt()); } else if constexpr (std::is_same_v) { return convert(p->toDouble()); } else if constexpr (std::is_same_v) { if(p->DataType() == QVariant::String ) { QString data_ = p->Value().toString(); if(data_ == "Y") { return true; } else { return false; } } else if(p->DataType() == QVariant::Bool) { return convert(p->toBool()); } } else if constexpr (std::is_same_v) { return convert(p->toString().toStdString()); } else if constexpr (std::is_same_v) { return convert(p->toString()); } else { // 不支持的类型,可以抛出异常,或者返回std::optional来表示错误 static_assert(std::is_same_v, "Unsupported type"); return T{}; // 或者 return std::nullopt; 这取决于你如何处理不支持的类型 } } template bool setColumnValue(const QString &column_name,T value) { DataColumn* p = getColumnDataByColumnName(column_name); if (!p) { return false; } if constexpr (std::is_same_v) { p->Value(value); return true; } else if constexpr (std::is_same_v) { p->Value(value); return true; } else if constexpr (std::is_same_v) { if(value == true) { p->Value("Y"); } else { p->Value("N"); } return true; } else if constexpr (std::is_same_v) { QString str = QString::fromStdString(value); p->Value(str); return true; } else if constexpr (std::is_same_v) { p->Value(value); return true; } return true; } public: void setParentTable(DataTable* p) { _table = p; } private: DataColumnCollection(); //初始化DataRow使用 DataColumnCollection(DataTable* initialized_table); // a null table use this to initialize a null dataClumnCollection. DataColumnCollection(const DataColumnCollection& other); DataColumnCollection& operator=(const DataColumnCollection& other); ~DataColumnCollection(); void Add(DataColumn* dc, int index, DataColumnCollection* collect); void RemoveAt(int index, DataColumnCollection* collect); //只移除DataColumnCollection指定索引处的列 void print(); //打印列名 private: DataTable* _table = nullptr; // the owner of this DataColumnCollection, never free it // 数据存储量不大,同时查询频繁,效率优先,用双索引 vector _column_list; // 存column和Idx关系 map _name_map; // 存column和name关系 }; /* 定义:表示 DataTable 中的一行数据。 DataRow和 DataColumn是主要DataTable组件 DataRow管理该行的DataColumnCollection,每个DataColumn即是一个数据单元 使用使用[]返回或设置DataColumn的值。 若要创建新的 DataRow,请使用 DataTable的 NewRow方法 。创建新的 DataRow后,使用Add 方法将新的 DataRow 添加到DataRowCollection eg:DataRow* dr=dt.NewRow();dt.Rows().Add(dr); */ class UTILITY_API DataRow { friend class DataTable; friend class DataRowCollection; friend class DataColumnCollection; public: DataRow(const DataRow& other); DataRow& operator=(const DataRow& other); ~DataRow(); void updataColumns(DataColumnCollection* p) { if(p) { DataColumnCollection* _columns_tmp = new DataColumnCollection(*(p)); _columns_tmp->setFromValue(_columns,_columns->Count()); DELETE_PTR(_columns) _columns = _columns_tmp; } } public: DataTable* Table(); //获取该行拥有其架构的 DataTable DataColumnCollection& Columns(); //获取或设置改行的DataColumnCollection DataColumn* getColumDataByIndex(int index); DataColumn& operator[](int index); //获取或设置该行中指定索引的某列 const DataColumn& operator[](int index) const; //获取该行中指定索引的某列 DataColumn& operator[](const QString& column_name); //获取或设置该行中指定列名的某列 const DataColumn& operator[] (const QString &column_name) const; //获取该行中指定列名的某列 DataColumn* getColumnDataByColumnName(const QString &columnName); bool isHasKeyAndValue(QString columnName,QString value); int getColumnCount() { return _columns->Count(); } template bool setColumnValue(const QString &column_name,T value) { return _columns->setColumnValue(column_name,value); } template bool getColumnValue(const QString &column_name,T& value) { return _columns->getColumnValue(column_name,value); } template T getColumnValue(const QString &column_name) { return _columns->getColumnValue(column_name); } DataColumn* getColumnRealValue(const QString& column_name) { return _columns->getColumnRealValue(column_name); } bool isColumnValueNull(const QString& column_name) { return _columns->isColumnValueNull(column_name); } void Refresh(); private: DataRow(); //为DataTable提供的构造函数 //void Remove(DataColumn& dc); //void RemoveAt(int index); DataColumnCollection* _columns = nullptr; // 数据信息 DataTable* _table = nullptr; // 位置信息 // int _row_id = 0; // 位置信息 // 单独管理行号太麻烦了,row也没地方用 // 用list idx取代行号 #ifdef QT_DEBUG vector _str_list; #endif }; /* 定义:表示 DataTable 的行的集合。 DataColumnCollection定义表的架构,但 DataRowCollection 包含表的实际数据 DataRowCollection和DataTable是组合关系。整体和个体不可独立存在,生命周期一致 DataRow不可脱离DataTable独立存在,需由DataTable方法新建DataRow eg:DataRow* dr=dt.NewRow();dt.Rows().Add(dr); 可以调用 Add 和 Remove 方法以从 DataRowCollection中插入和删除 DataRow 对象。 使用[]索引数据(列) */ class UTILITY_API DataRowCollection { friend class DataTable; friend class DataColumnCollection; public: DataRow& operator[](int index); //获取或设置指定索引处的行 DataRow* getRowDataByIndex(int index); void Add(DataRow* p_row); //将指定的 DataRow 添加到 DataRowCollection 对象中,并给行号赋值 void Remove(DataRow* p_row); //从集合中移除指定的 DataRow。 void RemoveAt(int index); //从集合中移除指定索引处的行 /* .NET 中 clear public 提供,这里沿袭这个做法,但是这个函数极度危险,不要乱用 */ void Clear(); //清除所有行的集合 int Count() const; //获取该集合中 DataRow 对象的总数 void setParentTable(DataTable* p) { _table = p; } private: DataRowCollection() = delete; DataRowCollection(DataTable* initialized_table); // a null table use this to initialize a null dataRowCollection. DataRowCollection(const DataRowCollection& other); DataRowCollection& operator=(const DataRowCollection& other); ~DataRowCollection(); private: vector _raw_list; DataTable* _table = nullptr; }; class UTILITY_API DataTableSplitBody //DataTable 分身纯粹是对 DataTable 内存数据查询 返回DataTable row的内存指针,实施获取数据和修改value值 { friend class DataColumnCollection; friend class DataRowCollection; friend class DataTableCollection; friend class DataSet; public: DataTableSplitBody() { clear(); } ~DataTableSplitBody() { clear(); } public: int getRowCount() { return _raw_list.size(); } DataRow* getRowDataByIndex(int index) { if(index >=0 && index < _raw_list.size()) { return _raw_list[index]; } return nullptr; } template bool getFirstRowColumnValue(const QString &column_name,T& value) { DataRow* pRow = getRowDataByIndex(0); if(!pRow) { return false; } return pRow->getColumnValue(column_name,value); } template T getFirstRowColumnValue(const QString &column_name) { DataRow* pRow = getRowDataByIndex(0); if(!pRow) { return T{}; } return pRow->getColumnValue(column_name); } template T getLastRowColumnValue(const QString &column_name) { int iCount = getRowCount(); DataRow* pRow = getRowDataByIndex(iCount - 1); if(!pRow) { return T{}; } return pRow->getColumnValue(column_name); } template bool setFirstRowColumnValue(const QString &column_name,T value) { DataRow* pRow = getRowDataByIndex(0); if(!pRow) { return false; } return pRow->setColumnValue(column_name,value); } void addRow(DataRow* p) { if(p) { _raw_list.emplace_back(p); } } void clear() { vector t; _raw_list.swap(t); } void setTableName(QString name) { m_table_name = name; } QString getTableName() { return m_table_name; } private: QString m_table_name; vector _raw_list; }; /* 定义:内存中数据的一个表 DataTable可脱离DataSet单独存在,也可加入DataSet.加入时将DataSet数据库连接(若已连接)同步到DataTable DataTable和DataRowCollection、DataColumnCollection是组合关系。整体和个体不可独立存在,生命周期一致 DataTable通过DataRowCollection和DataColumnCollection提供的Add、Remove等方法管理表的行和列 DataRow不可脱离DataTable独立存在,需由DataTable方法新建DataRow eg:DataRow* dr=dt.NewRow(); DataTable拷贝时,仅复制该 DataTable 的结构和数据。tablename、Dataset、QSqldatabase\QSqlTableModel皆保持原状 使用[]索引某行,使用[][]索引数据.可直接使用=对数据赋值 提供Clear、empty等方法清除和判空 提供Select_bysql方法获取筛选和排序后拷贝的DataTable 注:若需与数据库建立连接或者使用Select方法,请务必指定表名 与.net不同之处在于数据库数据同步部分 DataTable可使用connect建立与数据库的连接,规则如下: 若是游离的table或者所属DataSet并未连接,则建立与内存数据库的连接。只有DataSet有权与数据库文件建立连接。 若所属DataSet存在连接,则使用此连接 每次连接时都会刷新qsqltablemodel。 连接建立后,根据需求,将内存中数据同步到数据库(table_to_db)或者将数据库同名表数据同步到内存(db_to_table) */ class UTILITY_API DataTable { friend class DataColumnCollection; friend class DataRowCollection; friend class DataTableCollection; friend class DataSet; public: DataTable(); //创建空表 DataTable(const QString& table_name, const QString& sql_filter_exp); DataTable(const QString& table_name); //根据指定表名创建空表 DataTable(QSqlTableModel* table_model); //根据QSqltableModel创建表,并同步表名\数据\数据库连接 DataTable(const DataTable& other); DataTable& operator=(const DataTable& other); //只拷贝数据,dataset和connect皆置为默认值,用户可拷贝结束后自行设置 ~DataTable(); public: void updateDataFromOther(DataTable& other); QString get_sql_filter_exp(); DataRow* AddNewRow(); DataRow* NewRow(); //创建与该表具有相同架构的新 DataRow。空表(无表结构)无法插入行。 DataColumnCollection& Columns(); //获取或设置属于该表的列的集合。 const DataColumnCollection& Columns() const; //获取属于该表的列的集合。 DataRowCollection* getRows(); DataRowCollection& Rows(); //获取或设置属于该表的行的集合。 int getColumnCount(); int getRowCount(); const DataRowCollection& Rows() const; //获取属于该表的行的集合。 QSqlTableModel* Model(); //获取或设置_table_model供开发者使用 QString TableName(); //获取DataTable 的名称。 void TableName(const QString& table_name); //设置 DataTable 的名称。 DataSet* get_dataset(); //获取此表所属的 DataSet。 DataRow& operator[](int index); //获取或设置指定行号的行 DataRow* getRowDataByIndex(int index); bool isContainsColumn(QString ColumnName); bool addNewColumn(QString column_name, QVariant::Type column_type); bool addNewColumn(DataColumn *p_column); void setParentTable() { _dc_collect->setParentTable(this); _dr_collect->setParentTable(this); } template bool getFirstRowColumnValue(const QString &column_name,T& value) { DataRow* pRow = _dr_collect->getRowDataByIndex(0); if(!pRow) { return false; } return pRow->getColumnValue(column_name,value); } template T getFirstRowColumnValue(const QString &column_name) { DataRow* pRow = _dr_collect->getRowDataByIndex(0); if(!pRow) { return T{}; } return pRow->getColumnValue(column_name); } template bool setFirstRowColumnValue(const QString &column_name,T value) { DataRow* pRow = _dr_collect->getRowDataByIndex(0); if(!pRow) { return false; } return pRow->setColumnValue(column_name,value); } const DataRow& operator[](int index) const; //获取指定行号的行 /* 用法:dt.Select("column1='row81'"); 浮点数需要加小数点,int和long不需 限制:浮点数只支持查询double 由于QT5.15.2版本QVariant和qsqltablemodel的限制无法查询float转为real存入数据库的值。若需查询float请使用Select_bysql 此接口功能可被Select_bysql取代,为向上兼容保留此接口。此接口未来不会维护,后续开发者尽量使用Select_bysql*/ DataTable Select(const QString& sql_filter_exp); /* 用法:dt.Select("column1=91.0", "column8", Qt::DescendingOrder); 浮点数需要加小数点,int和long不需 ; 可按照数值排序或者字典序排序,取决于用户指定的列原本的类型 此接口功能可被Select_bysql取代,为向上兼容保留此接口。此接口未来不会维护,后续开发者尽量使用Select_bysql*/ DataTable Select(const QString& sql_filter_exp, const QString& column_name, Qt::SortOrder order = Qt::SortOrder::AscendingOrder); /* 用法1:dt.Select("where ifstar = 1 and cacap = 'name' order by yf , ElementID"); 前置select * from tablename会在方法里拼接 用法2:参数化查询,params参数绑定占位符和值(可支持float) const QMap params = { {"age", 1.2f}, //若需要查询float类型,需明确指明1.2f.否则C++默认为double {"name", "yourname"} }; DataTable result =dt.Select_bysql("WHERE age = :age AND name = :name ORDER BY weight ASC, height ASC", params);*/ DataTable Select_bysql(const QString& sql_exp, const QMap params = QMap()); DataRow* Select_bysql_con_one(const std::string& condition,const std::string& sortColumn = ""); vector Select_bysql_con(const std::string& condition,const std::string& sortColumn = ""); bool parseCondition(DataRow* row, const std::string& condition); bool parseSingleCondition(DataRow* row, const std::string& condition); void removeRow(DataRow *p_row); //删除其中一行 index 该行的原始状态的索引 void Clear(); //清空表结构和表数据 const bool empty() const; //行数为0则返回true void connect(const QString &connectionName = QString()); //dt优先连接ds连接的数据库;若ds未连接,则与内存数据库连接 bool table_to_db(); //将datatable数据全转移到_table_model里,并提交到db /* 将db数据全转移到datatable里,使用前必须设置数据库连接(可手动设置,或者加入已连接的ds来设置)和tablename 用法:dt.Select("column1='row81'"); 浮点数需要加小数点,int和long不需 eg:DataSet ds_tmp;ds_tmp.connect("myRefDb.db"); DataTable* td_tmp = new DataTable("Damage_Element");ds_tmp.Add(&td_tmp);td_tmp.db_to_table("ShipID = 1"); */ void db_to_table(const QString& sql_filter_exp = ""); void print_table(); void print_model(); const int Length() const; private: void _init(); QString _get_conn_name(); void _row_to_model(); DataTable _query_to_table(QSqlQueryModel* query_model); //将数据从querytable转移到table void _db_to_model(const QString& sql_filter_exp = ""); void _model_to_table(); void _set_dataset(DataSet* ds); //设置此表所属的 DataSet。用户只可通过Add设置此表所属的 DataSet。此接口提供给数据集拷贝使用 private: QString m_sql_filter_exp = ""; QString _table_name; //加入同一ds的table不可重名。拷贝时拷贝name DataColumnCollection* _dc_collect = nullptr; /* 只存放列名,标题,也可理解为第-1行的所有数据 */ DataRowCollection* _dr_collect = nullptr; /* 存数据,每一行和每一格 */ DataSet* _data_set = nullptr; //拷贝时忽略该项 QSqlDatabase _db; //拷贝时忽略该项 QSqlTableModel* _table_model = nullptr; //拷贝时忽略该项 }; #endif