C++11 中的轻量级数据映射器
Lightweight Data Mapper in C++11
我想在 'grid' 控件中显示业务层 object 的列表。我还想在对话框中编辑任何给定的 object。
object 保存在关系数据库中,该数据库还执行引用和 multi-user 完整性。尽管业务 object 与数据库表非常相似,但我想使用某种 'data mapper'.
将它们解耦到一定程度
我的 object 派生自 myDataObject 基础 class。基class有一个静态成员如下:
// Initialise a static list of member names for this class. Used when the object is displayed in a grid.
const std::vector<myDataObjectDescriptor> myDataObject::m_Descriptors
{
{ myDO_INT_FIELD, "UID", "m_UID", 40, false, false },
{ myDO_STRING_FIELD, "TITLE", "m_Title", 200, true, false },
{ myDO_STRING_FIELD, "DESCRIPTION", "m_Description", 400, true, false }
};
此描述符列表允许网格控件直接呈现具有正确列标题和宽度的 object 列表。我可以使用 lambda 扩展基础 class 描述符:
// Static initialisation of descriptor list using a lamda.
const std::vector<myDataObjectDescriptor> myDerivedDataObject::m_Descriptors = []
{
std::vector<myDataObjectDescriptor> v = myDataObject::m_Descriptors;
v.push_back ({myDO_STRING_FIELD, "BACKGROUND", "m_Background", 120, false, false});
v.push_back ({myDO_STRING_FIELD, "FONT", "m_Font", 120, false, false});
return v;
} ();
到目前为止一切顺利。现在,我想从数据库查询创建一个 object 的列表,该列表可以是 std::vector<some class derived from myDataObject>
。我的数据库层 returns 一个允许一次检索一行的结果集。
我如何编写一个数据映射器来引用 object 列表 (std:vector<some class derived from myDataObject>&
) 和对结果集的引用并填充列表?
附加信息:
目前我有 2 个方法在每个 class 派生自 myDataObject 中被覆盖:
- FromData (myResultSet& resultset):从结果集中的当前行填充 'this' object。
- SetByName(string name, variant value):根据字符串化名称设置属性。由 FromData 用于根据结果集中的字段名称设置属性。
对此我有很多不喜欢的地方,但主要是:
- myDataObject 不应该知道任何关于数据库层的信息(也
紧密耦合)。
SetByName 是一系列 if 语句,如果 'name' 参数匹配则设置属性,即
if (name == "m_Title")
m_Title = value;
重新编辑。 rumburak 的评论:一旦我解决了读取问题,我计划以某种方式做相反的事情来持久化数据。
既然你问的是关于从数据库中读取数据,而似乎没有关于写入的问题:你就不能做与你所做的相反的事情来持久化数据库中的数据吗?
如果没有:
您可以将结果行转换为解耦 myDataObject
和 resultRow
的中间对象。该对象可以具有已知字段的已知数据成员和其他字段的一个或多个映射,例如
struct mySerializedObject
{
int id;
std::string title;
std::map<std::string, std::string> texts;
std::map<std::string, int> numbers;
};
然后数据对象的fromSerialized
函数就可以随意挑选了。
感谢@rumburak 和 this answer 的建议,我现在已经解决了我的问题。
我将结果行转换为通用中间对象,其定义如下:
typedef std::unordered_map<std::string, boost::any> myDataRow;
然后在我的数据库层中,我有一个 GetRow() 方法,该方法 returns 对 myDataRow 的引用。该方法遍历结果集的当前记录的列并填充行对象。
myDataRow& myDataResultSet::GetRow()
{
// Get the column names.
std::vector<myDataColumn>& cols = GetColumns();
// Iterate through the columns, setting the mapped value against the column name.
// Note: the map's columns are automatically be created on the first use of this function and their contents updated thereafter.
int i = 0; for (myDataColumn col : cols)
{
switch(col.m_Type)
{
case dtInteger:
m_Data[col.m_Name] = GetInt(i++);
break;
case dtString:
m_Data[col.m_Name] = GetString(i++);
break;
}
return m_Data;
}
现在 'data aware' 对象可以从中间的 myDataRow class.
初始化
void myDataObject::FromData(const myDataRow& row)
{
auto it = row.find("UID");
m_UID = it != row.end() ? boost::any_cast<int>(it->second) : 0;
it = row.find("TITLE");
m_Title = it != row.end() ? boost::any_cast<std::string>(it->second) : "";
it = row.find("DESCRIPTION");
m_Description = it != row.end() ? boost::any_cast<std::string>(it->second) : ""; : "";
}
每个派生 class 调用其父级的 FromData()。他们还有一个方便的构造函数 myDataObject(const myDataRow&).
数据映射器层现在包括查询数据库和从结果集行填充对象,即:
myDerivedDataObject temp(results->GetRow());
'data mapping' 部分包括确保结果集列的名称正确映射到数据对象的成员。
我想在 'grid' 控件中显示业务层 object 的列表。我还想在对话框中编辑任何给定的 object。
object 保存在关系数据库中,该数据库还执行引用和 multi-user 完整性。尽管业务 object 与数据库表非常相似,但我想使用某种 'data mapper'.
将它们解耦到一定程度我的 object 派生自 myDataObject 基础 class。基class有一个静态成员如下:
// Initialise a static list of member names for this class. Used when the object is displayed in a grid.
const std::vector<myDataObjectDescriptor> myDataObject::m_Descriptors
{
{ myDO_INT_FIELD, "UID", "m_UID", 40, false, false },
{ myDO_STRING_FIELD, "TITLE", "m_Title", 200, true, false },
{ myDO_STRING_FIELD, "DESCRIPTION", "m_Description", 400, true, false }
};
此描述符列表允许网格控件直接呈现具有正确列标题和宽度的 object 列表。我可以使用 lambda 扩展基础 class 描述符:
// Static initialisation of descriptor list using a lamda.
const std::vector<myDataObjectDescriptor> myDerivedDataObject::m_Descriptors = []
{
std::vector<myDataObjectDescriptor> v = myDataObject::m_Descriptors;
v.push_back ({myDO_STRING_FIELD, "BACKGROUND", "m_Background", 120, false, false});
v.push_back ({myDO_STRING_FIELD, "FONT", "m_Font", 120, false, false});
return v;
} ();
到目前为止一切顺利。现在,我想从数据库查询创建一个 object 的列表,该列表可以是 std::vector<some class derived from myDataObject>
。我的数据库层 returns 一个允许一次检索一行的结果集。
我如何编写一个数据映射器来引用 object 列表 (std:vector<some class derived from myDataObject>&
) 和对结果集的引用并填充列表?
附加信息:
目前我有 2 个方法在每个 class 派生自 myDataObject 中被覆盖:
- FromData (myResultSet& resultset):从结果集中的当前行填充 'this' object。
- SetByName(string name, variant value):根据字符串化名称设置属性。由 FromData 用于根据结果集中的字段名称设置属性。
对此我有很多不喜欢的地方,但主要是:
- myDataObject 不应该知道任何关于数据库层的信息(也 紧密耦合)。
SetByName 是一系列 if 语句,如果 'name' 参数匹配则设置属性,即
if (name == "m_Title") m_Title = value;
重新编辑。 rumburak 的评论:一旦我解决了读取问题,我计划以某种方式做相反的事情来持久化数据。
既然你问的是关于从数据库中读取数据,而似乎没有关于写入的问题:你就不能做与你所做的相反的事情来持久化数据库中的数据吗?
如果没有:
您可以将结果行转换为解耦 myDataObject
和 resultRow
的中间对象。该对象可以具有已知字段的已知数据成员和其他字段的一个或多个映射,例如
struct mySerializedObject
{
int id;
std::string title;
std::map<std::string, std::string> texts;
std::map<std::string, int> numbers;
};
然后数据对象的fromSerialized
函数就可以随意挑选了。
感谢@rumburak 和 this answer 的建议,我现在已经解决了我的问题。
我将结果行转换为通用中间对象,其定义如下:
typedef std::unordered_map<std::string, boost::any> myDataRow;
然后在我的数据库层中,我有一个 GetRow() 方法,该方法 returns 对 myDataRow 的引用。该方法遍历结果集的当前记录的列并填充行对象。
myDataRow& myDataResultSet::GetRow()
{
// Get the column names.
std::vector<myDataColumn>& cols = GetColumns();
// Iterate through the columns, setting the mapped value against the column name.
// Note: the map's columns are automatically be created on the first use of this function and their contents updated thereafter.
int i = 0; for (myDataColumn col : cols)
{
switch(col.m_Type)
{
case dtInteger:
m_Data[col.m_Name] = GetInt(i++);
break;
case dtString:
m_Data[col.m_Name] = GetString(i++);
break;
}
return m_Data;
}
现在 'data aware' 对象可以从中间的 myDataRow class.
初始化void myDataObject::FromData(const myDataRow& row)
{
auto it = row.find("UID");
m_UID = it != row.end() ? boost::any_cast<int>(it->second) : 0;
it = row.find("TITLE");
m_Title = it != row.end() ? boost::any_cast<std::string>(it->second) : "";
it = row.find("DESCRIPTION");
m_Description = it != row.end() ? boost::any_cast<std::string>(it->second) : ""; : "";
}
每个派生 class 调用其父级的 FromData()。他们还有一个方便的构造函数 myDataObject(const myDataRow&).
数据映射器层现在包括查询数据库和从结果集行填充对象,即:
myDerivedDataObject temp(results->GetRow());
'data mapping' 部分包括确保结果集列的名称正确映射到数据对象的成员。