COMPASSi/trunk/code/inc/Util/include/DataTable.h

627 lines
21 KiB
C++
Raw Permalink 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.

#ifndef _DATA_TABLE_
#define _DATA_TABLE_
#define _INSENSETIVE
#include <QString>
#include <iostream>
#include <assert.h>
#include <vector>
#include <list>
#include <map>
#include <QString>
#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 <typename T>
bool getColumnValue(const QString &column_name,T& value)
{
DataColumn* p = getColumnDataByColumnName(column_name);
if (!p) return false;
if constexpr (std::is_same_v<T, int>) {
value = p->toInt();
return true;
}
else if constexpr (std::is_same_v<T, double>) {
value = p->toDouble();
return true;
}
else if constexpr (std::is_same_v<T, bool>) {
value = p->toBool();
return true;
}
else if constexpr (std::is_same_v<T, std::string>) {
value = p->toString().toStdString();
return true;
}
else if constexpr (std::is_same_v<T, QString>) {
value = p->toString();
return true;
}
else {
return false; // Unsupported type
}
}
template <typename T>
T getColumnValue(const QString& column_name)
{
DataColumn* p = getColumnDataByColumnName(column_name);
if (!p) {
// 如果没有找到列名对应的列,返回一个默认值(适用于简单类型)
return T{};
}
// 通用的转换函数
auto convert = [](auto&& value) -> T {
return static_cast<T>(value);
};
// 如果类型是支持的
if constexpr (std::is_same_v<T, int>) {
return convert(p->toInt());
}
else if constexpr (std::is_same_v<T, double>) {
return convert(p->toDouble());
}
else if constexpr (std::is_same_v<T, bool>)
{
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<T, std::string>) {
return convert(p->toString().toStdString());
}
else if constexpr (std::is_same_v<T, QString>) {
return convert(p->toString());
}
else {
// 不支持的类型可以抛出异常或者返回std::optional来表示错误
static_assert(std::is_same_v<T, T>, "Unsupported type");
return T{}; // 或者 return std::nullopt; 这取决于你如何处理不支持的类型
}
}
template <typename T>
bool setColumnValue(const QString &column_name,T value)
{
DataColumn* p = getColumnDataByColumnName(column_name);
if (!p)
{
return false;
}
if constexpr (std::is_same_v<T, int>) {
p->Value(value);
return true;
}
else if constexpr (std::is_same_v<T, double>) {
p->Value(value);
return true;
}
else if constexpr (std::is_same_v<T, bool>)
{
if(value == true)
{
p->Value("Y");
}
else
{
p->Value("N");
}
return true;
}
else if constexpr (std::is_same_v<T, std::string>)
{
QString str = QString::fromStdString(value);
p->Value(str);
return true;
}
else if constexpr (std::is_same_v<T, QString>) {
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<DataColumn*> _column_list; // 存column和Idx关系
map<QString, DataColumn*> _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 <typename T>
bool setColumnValue(const QString &column_name,T value)
{
return _columns->setColumnValue(column_name,value);
}
template <typename T>
bool getColumnValue(const QString &column_name,T& value)
{
return _columns->getColumnValue(column_name,value);
}
template <typename T>
T getColumnValue(const QString &column_name)
{
return _columns->getColumnValue<T>(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<string> _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<DataRow*> _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 <typename T>
bool getFirstRowColumnValue(const QString &column_name,T& value)
{
DataRow* pRow = getRowDataByIndex(0);
if(!pRow)
{
return false;
}
return pRow->getColumnValue(column_name,value);
}
template <typename T>
T getFirstRowColumnValue(const QString &column_name)
{
DataRow* pRow = getRowDataByIndex(0);
if(!pRow)
{
return T{};
}
return pRow->getColumnValue<T>(column_name);
}
template <typename T>
T getLastRowColumnValue(const QString &column_name)
{
int iCount = getRowCount();
DataRow* pRow = getRowDataByIndex(iCount - 1);
if(!pRow)
{
return T{};
}
return pRow->getColumnValue<T>(column_name);
}
template <typename T>
bool setFirstRowColumnValue(const QString &column_name,T value)
{
DataRow* pRow = getRowDataByIndex(0);
if(!pRow)
{
return false;
}
return pRow->setColumnValue<T>(column_name,value);
}
void addRow(DataRow* p)
{
if(p)
{
_raw_list.emplace_back(p);
}
}
void clear()
{
vector<DataRow*> 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<DataRow*> _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 <typename T>
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 <typename T>
T getFirstRowColumnValue(const QString &column_name)
{
DataRow* pRow = _dr_collect->getRowDataByIndex(0);
if(!pRow)
{
return T{};
}
return pRow->getColumnValue<T>(column_name);
}
template <typename T>
bool setFirstRowColumnValue(const QString &column_name,T value)
{
DataRow* pRow = _dr_collect->getRowDataByIndex(0);
if(!pRow)
{
return false;
}
return pRow->setColumnValue<T>(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);
/* 用法1dt.Select("where ifstar = 1 and cacap = 'name' order by yf , ElementID");
前置select * from tablename会在方法里拼接
用法2参数化查询,params参数绑定占位符和值可支持float
const QMap<QString, QVariant> 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<QString, QVariant> params = QMap<QString, QVariant>());
DataRow* Select_bysql_con_one(const std::string& condition,const std::string& sortColumn = "");
vector<DataRow*> 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