简体   繁体   English

C ++ 11中的轻量级数据映射器

[英]Lightweight Data Mapper in C++11

I want to display lists of business layer objects in 'grid' controls. 我想在“网格”控件中显示业务层对象的列表。 I also want to edit any given object in a dialog box. 我也想在对话框中编辑任何给定的对象。

The objects are persisted in a relational database that also performs referential and multi-user integrity. 这些对象保存在关系数据库中,该数据库还执行引用和多用户完整性。 Although the business objects are quite similar to the database tables, I would like to decouple them to a degree using some sort of 'data mapper'. 尽管业务对象与数据库表非常相似,但我想使用某种“数据映射器”将它们分离到一定程度。

My objects are derived from a myDataObject base class. 我的对象是从myDataObject基类派生的。 The base class has a static member as follows: 基类具有一个静态成员,如下所示:

// 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 }
};

This descriptor list allows the grid control to directly render the object list with the correct column titles and widths. 此描述符列表允许网格控件直接使用正确的列标题和宽度呈现对象列表。 I can extend the base class descriptors using a lambda: 我可以使用lambda扩展基类描述符:

// 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; 
} ();

So far so good. 到现在为止还挺好。 Now, I would like to create a list of objects from a database query, the list can be a std::vector<some class derived from myDataObject> . 现在,我想从数据库查询中创建一个对象列表,该列表可以是std::vector<some class derived from myDataObject> My database layer returns a result set which allows rows to be retrieved one at a time. 我的数据库层返回一个结果集,该结果集允许一次检索一行。

How can I write a data mapper that takes a reference to the object list ( std:vector<some class derived from myDataObject>& ) and a reference to the result set and populates the list? 如何编写一个数据映射器,该映射器引用对象列表( std:vector<some class derived from myDataObject>& )和对结果集的引用并填充列表?

Additional info: 附加信息:

Currently I have 2 methods that are overridden in each class derived from myDataObject: 目前,我有2种方法在从myDataObject派生的每个类中被重写:

  • FromData (myResultSet& resultset): populates 'this' object from the current row in the result set. FromData(myResultSet&resultset):从结果集中的当前行填充'this'对象。
  • SetByName (string name, variant value): sets an attribute based on its stringified name. SetByName(字符串名称,变量值):根据字符串名称设置属性。 Used by FromData to set an attribute based on the field name in the result set. FromData使用它来基于结果集中的字段名称设置属性。

There are multiple things that I don't like about this, but mostly: 我对此有很多不满意的地方,但主要是:

  • myDataObject shouldn't know anything about the database layer (too closely coupled). myDataObject对数据库层一无所知(耦合太紧密)。
  • SetByName is a series of if statements that sets an attribute if the 'name' parameter is matched, ie SetByName是一系列if语句,用于在'name'参数匹配时设置属性,即

     if (name == "m_Title") m_Title = value; 

Edit re. 重新编辑。 comment by rumburak: Once I have solved the problem of reading, it is my plan to somehow do the reverse for persisting data. rumburak的评论:解决了阅读问题后,我计划以某种方式对持久数据进行相反的处理。

Since you are asking about reading data from the database, and seem to have no questions about writing: Can't you just do the inverse of whatever you do for persisting the data in the database? 由于您正在询问要从数据库中读取数据,并且似乎对写入没有任何疑问:您是否可以做与将数据持久化到数据库中相反的工作?

If not: 如果不:

You could translate the result rows into intermediate objects which decouple myDataObject and resultRow . 您可以将结果行转换为中间对象,该中间对象将myDataObjectresultRow分离。 This object could have known data members for well-known fields and one or more maps for other fields, eg 该对象可能具有用于已知字段的已知数据成员,以及用于其他字段的一个或多个映射,例如

struct mySerializedObject
{
   int id;
   std::string title;
   std::map<std::string, std::string> texts;
   std::map<std::string, int> numbers;
};

And then the fromSerialized functions of the data objects can just pick what they need. 然后,数据对象的fromSerialized函数可以选择所需的内容。

Thanks to the suggestion by @rumburak and this answer I have solved my problem for now. 感谢@rumburak的建议和这个答案,我现在已经解决了我的问题。

I translate a result row into a general-purpose intermediate object which is defined as follows: 我将结果行转换为如下定义的通用中间对象:

typedef std::unordered_map<std::string, boost::any> myDataRow;

Then in my database layer I have a GetRow() method that returns a reference to a myDataRow. 然后在我的数据库层中,我有一个GetRow()方法,该方法返回对myDataRow的引用。 The method iterates over the columns of the resultset's current record and populates the row object. 该方法遍历结果集当前记录的列,并填充行对象。

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;
}

Now 'data aware' objects can be initialised from the intermediate myDataRow class. 现在,可以从中间myDataRow类初始化“数据感知”对象。

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) : ""; : ""; 
}

With each derived class calling its parent's FromData(). 每个派生类都调用其父级的FromData()。 They also have a convenience constructor myDataObject(const myDataRow&). 它们还具有便利的构造函数myDataObject(const myDataRow&)。

The data mapper layer now consists of querying the database and populating objects from result set rows, ie: 数据映射器层现在包括查询数据库和从结果集行填充对象,即:

myDerivedDataObject temp(results->GetRow());

The 'data mapping' part consists of making sure that the resultset columns have names that correctly map to the data object's members. “数据映射”部分包括确保结果集列具有正确映射到数据对象成员的名称。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM