【问题标题】:C++ Object DatabaseC++ 对象数据库
【发布时间】:2016-10-12 06:51:06
【问题描述】:

我正在写我的学士论文。它的目标是用 C++ 编写对象数据库(类似于 CoreData)。其中一项要求是支持反射关系(1-1,1-M,M-M)和动态对象加载。

我当前的设计由带有代码生成功能的简单 DDL 语言组成。 用户编写他想要保留的类,然后编写这些类之间的关系。像这样的:

Person {
    string name;
    int salary;
}
relation Person.boss(1) references Person inverse Person.subs(M);

由此,我生成带有类定义的 C++ 标头,带有方法定义的 C++ 源代码。类的所有原始字段都是公共的,关系是私有的,并且只能通过 get/set 或 get/add/remove/clear 方法访问。 在这些方法中,我保持了自反关系的一致性。 例如:在 setBoss 方法中,我会执行以下操作:

void setBoss(ptr<Person> val) {
        if (boss) {
            boss->subs.remove(this);
        }
        boss = val;
        boss->subs.add(this);
        boss.modified = true
    }

此代码已全部生成,并且可以正常工作。但我的主管要求我在没有任何形式的代码生成的情况下执行此操作,并尝试尽可能接近 CoreData。我认为他的想法是在 C++ 中模拟动态对象,其中可以存储在数据库中的每个对象都包含map&lt;string,value&gt;,以及从该映射中读取的字段。我认为这种设计对于 C++ 来说显然是错误的,因为存储这样的所有内容都需要每个字段都是自定义类,该类在每个字段访问中都引用了所有者和动态转换,而且我什至没有谈论存储其他类,它们使用自定义共享指针(它是一个模板,因此动态转换不起作用)。

另外,元数据有问题,我如何在这个系统中定义模式?我可能可以使用访问者,访问所有字段,并使用一些宏技巧来检索他们的名称和类型,但是自反关系?我不知道。

在我的方法中,我可以以某种方式对添加的字段进行版本化并为它们生成迁移代码。 (将模式版本存储在持久数据库中,然后,当我打开这个数据库时,检查版本并运行生成的迁移代码)。

我在这里缺少什么吗?我的方法完全错误吗?

【问题讨论】:

    标签: c++ core-data object-oriented-database


    【解决方案1】:

    [当然,下面是一个观点,但怪问题,不是我]

    我在这里缺少什么吗?我的方法完全错误吗?

    完全错误 - 不。但并非没有风险。

    您的方法的一个缺点(类似于 ORM 的方法):每次更改结构时,“数据存储迁移”都将成为一场噩梦。否则,您将不得不让用户(程序员同事)面对处理不同版本模式的噩梦。

    查看 Google 的 protobuff,了解生成结构正在演变的问题 - 它们不处理存储,只处理数据传输。然而,您必须仔细导航(哪些成员是必需的/可选的)并遵守一些严格的规则(例如,在您的协议版本之间,只需添加但从不删除字段)。

    另外,元数据有问题,如何在这个系统中定义模式?

    这对你的论文来说不是一个很棒的话题吗?当然这不会是微不足道的,但为什么会这样呢?

    【讨论】:

    • 嗯,用我的方法,生成模式是非常可行的,因为我正在生成代码,我可以生成需要一些 context 对象的方法,并在此调用 generateTableaddAttribute 方法语境。迁移也是可能的(在 DDL 中注释添加的字段,然后使用它们生成迁移代码)。以我主管的方法,没那么多。
    • “迁移也是可能的(在 DDL 中注释添加的字段,然后用它们生成迁移代码)”我不同意。如果您的更改只是添加,那么当然一切都会好起来的。但是,尝试将 1-M 更改为 M-M,看看这对调用旧版本的代码有什么影响。 “以我主管的方法,没那么多。”以你的主管的方法,没有必要。
    • 我不明白,迁移后总是需要更新架构。
    • 这不是关于模式更新,而是关于在它之上运行的代码。使用运行时map&lt;name,value&gt; 方法,乍一看是愚蠢和非结构化的,旧代码将继续编译和工作(这不一定是优势 - 因为它可能工作得不好) - 看看有多少通用 JSON 库就在那里。在您的方法中,架构的任何更改都将要求至少重新编译(如果这种重新编译甚至可以在不使用新结构修改旧代码的情况下进行)
    • 嗯,有 2 个主要操作,读取对象,将对象写入数据库。两者都使用访问所有类字段并执行必要操作的访问者。如果代码在具有附加属性的较新数据库上运行,则这些属性将被忽略,删除的属性可以设置为空/默认值。
    【解决方案2】:

    如果不生成代码,您必须在编译级方法(使用 DSL)或以运行时为中心的方法(使用虚拟函数、动态转换和 typeid/typeindex 的混合)之间做出选择。

    DSL 练习是我们今年碰巧经历的事情,具有讽刺意味的是,它与您的问题密切相关:特别是 1-1、1-N、N-N 集合。当然,这对于学士论文来说是多余的,但它可能看起来像这样:https://github.com/C5T/Current/blob/1b9716438d323b8b47eb8d5bf5907e98ee3be1dc/Storage/test.cc#L92-L120

    在您的情况下,我首先会直接询问主管他们是在寻找编译时解决方案还是运行时解决方案。运行时可能更容易在时间和 LOC 方面实现,但它提供的保证要少得多,结果也不太实用。不过,仅对于字符串 字符串映射,几个集合和虚拟引用的策略类可能会很好地完成这项工作。

    【讨论】:

    • 我尝试了 DSL,但生成反身关系需要 LOT 的宏,相反,我选择使用自己的语言和工具来生成所需的代码。是的,在上次与他会面后,我得到了他希望我做所有动态的想法,比如 DbVal 继承自 DbInt,DbFloat,DbString 的类,并将 map&lt;string,DbVal&gt; 存储在对象内部作为数据存储,但他也想要属性可以通过字段访问进行检索,这意味着类字段必须具有对所有者类的引用,并且从此字段中读取的内容将从所述映射中读取。
    • 这是一个很棒的练习。我很感激有这样的老师。
    猜你喜欢
    • 2010-12-12
    • 1970-01-01
    • 2010-09-29
    • 2010-11-04
    • 2010-10-19
    • 1970-01-01
    • 2012-02-07
    • 2011-04-13
    • 1970-01-01
    相关资源
    最近更新 更多