为ORM c ++实现1到n映射

时间:2009-12-16 11:20:39

标签: c++ orm mapping

我正在编写一个项目,我需要在C ++中实现ORM解决方案的精简版本。我很擅长实现1-n关系。

例如,如果以下是类:

class A
{
    ...
}

class B
{
    ...
    std::list<A> _a_list;
    ...
}

我已经提供了加载/保存方法来加载/保存到db。 现在,如果我采用B的情况和以下工作流程:

  • 删除了_a_list中的1个条目
  • 修改了_a_list中的1个条目
  • 将一个条目添加到_a_list

现在,我需要使用类似“b.save()”的更新数据库。 那么,保存更改的最佳方法是什么,即识别_a_list的添加,删除和更新。

5 个答案:

答案 0 :(得分:3)

我的第一个想法是将所有可能的数据库操作封装为命令对象(命令模式)。这样,您可以创建任意数量的命令,直到调用Save()方法更新数据库。在这里,您需要确保将这些命令作为事务处理。快速实现将是这样的:

标题

#include <vector>

using namespace std;

class B;
class Cmd;

class B
{
    private:
        vector<Cmd*> m_commands;
    public:
        void AddCmd( Cmd* p_command );
        void Save();
};

class Cmd
{
    protected:
        B* m_database;

    public:
        Cmd( B* p_database );
        virtual void Execute() = 0;
        virtual void Undo() = 0;
};

class InsertCmd : public Cmd
{
    private:
        int m_newEntry;
    public:
        InsertCmd( B* p_database, int p_newEntry );
        void Execute() { cout << "insert " << m_newEntry << endl; }
        void Undo()    { /* undo of insert */ }
};

<强>来源:

#include "DbClass.h"

void B::AddCmd( Cmd* p_command )
{
    m_commands.push_back(p_command);
}

void B::Save()
{
    for( unsigned int i=0; i<m_commands.size(); i++ )
        m_commands[i]->Execute();
}

Cmd::Cmd( B* p_database ) : m_database(p_database)
{
    m_database->AddCmd(this);
}

InsertCmd::InsertCmd( B* p_database, int p_newEntry ) 
: Cmd(p_database), m_newEntry(p_newEntry)
{
}

测试主要:

#include "DbClass.h"

int main()
{
    B database;
    InsertCmd  insert( &database, 10 );
    database.Save();

    return 0;
}

答案 1 :(得分:1)

一种策略是使用枚举来表示记录的“状态”。即

enum RecordState {
    RECORD_UNMODIFIED,
    RECORD_NEW,
    RECORD_CHANGED,
    RECORD_DELETED
};

您将为每条记录分配一个RecordState(默认情况下默认为RECORD _NEW / RECORD _UNMODIFIED),并且在调用Save()时,它将对每条记录执行适当的操作并将其状态重置为RECORD _UNMODIFIED。删除将在处理时从列表中删除。

答案 2 :(得分:1)

记录状态确实是一个好主意。

我建议:

(a)应用程序将已删除的对象保留在数组中,并且只有在调用类似ORM的代码进行保存时才会删除它们(即它执行INSERT,UPDATE和DELETE时)

OR

(b)ORM上下文需要在内部维护所有对象的幕后列表,这些对象已从磁盘中选择或在RAM中为每个数据库事务创建(或者如果不使用事务,连接)。当要求ORM保存并且INSERT,UPDATE和DELETE基于此列表时,将重复此列表。

在第二种情况下,您经常会发现一个额外的要求,即能够在系统的某些部分中将对象与ORM分离/分离,以创建状态或修改版本的持久快照

请注意,在第一次保存之前删除的新创建的对象不应生成SQL DELETE,因此实际上UNMODIFED,NEW,CHANGED,DELETED的枚举通常是不够的,您还需要NEW_DELETED并且如果我的理论一致分离。

答案 3 :(得分:1)

有点晚但在我看来你需要/需要的是Unit Of Work。你当前的设计就像一个Registry,与UoW很好地搭配。

答案 4 :(得分:1)

我想分享我对如何实现“1到n”关系的看法。 Side“1”是主表,而side“n”对应于slave(子)表。我想,我们想要操纵双方的关系。从奴隶点来看,关系看起来像一个对象属性,可能具有设置/更改/清除属性指定的对象引用的能力。从主人的角度来看,相同的关系将是类似集合的属性,为我们提供了迭代/添加/删除该集合中的对象引用的方法。 由于对关系一方的任何更改必须立即从另一方提供,我们有两种选择:

  1. 立即将更改传播给关系的所有参与者。在这种情况下,上面提到的类似集合的属性可以使用通用容器类来实现,其中改变方法被覆盖。
  2. 介绍某种中间“关系实例”对象,它将拥有关于主对象的一个​​实例的关系的所有信息。在这种情况下,来自任何一方的每个属性调用都将从该中间对象获取所请求的信息。

在两者之间进行选择涉及回答几个重要问题:

  1. 如何创建映射类的实例?我们可以创建一个实例而不将其保存在IdentityMap中吗?如何创建新创建的对象的链接结构?
  2. 是否可以复制映射类的实例,同时保留一些关于彼此的知识,以传播更改?或者我们每个表记录应该只有一个实例?
  3. 谁负责在所有可能情况下删除对象?

在任何一种情况下都是一些特征,通常存在于任何类似ORM的解决方案中。 例如,IdentityMap设计模式假定您注册所有映射类的实例,这些实例应该在某种注册表中将更改呈现给数据库。这对于稍后执行“刷新”操作是必要的。当然,这需要保持记录状态。 我发现“关系实例”方法相对容易实现。您可以在我仍处于开发阶段的C ++通用ORM解决方案中找到实现:YB.ORM。特别是,看看源文件DataObject.h,DataObject.cpp和TestDataObject.cpp(文件夹lib / orm /)中的测试。 与示例代码相比,YB.ORM库在内部使用变量类型对象和静态类型的“瘦”包装器。 DataObject类表示映射类的实例,其中映射规则在元数据描述中给出。这些对象总是在堆中分配,不可复制。它们存储数据值。它们具有指向映射表的元数据信息的链接。当然,在这些对象中保持当前状态(NewGhostDirtySyncToBeDeletedDeleted之一。为了支持此类呈现“n”侧的关系,它们中的每一个都有一组指向RelationObject类(slave_relations_ member)实例的指针。为了支持此类呈现“1”的关系,每个关系都有一组指向RelationObject类(master_relations_ member)实例的共享指针。

RelationObject类表示关系的实例。这些对象总是在堆中分配,不可复制。它们存储和枚举指向相关DataObject实例的指针:一个指向master的指针,以及一组指向slave的共享指针。因此,它们“拥有”从属DataObject实例,DataObject实例“拥有”(间接)所有从属对象。注意,RelationObject本身维护状态,以支持延迟加载。