DBAccess - 是否可以在单个事务中创建和 link 个实体?

DBAccess - Is it possible to create and link entities in a single Transaction?

我遇到了交易和 linked 实体的问题。我有这样的代码(不是真正的代码,只是为了理解):

User *user = [User new];
user.username = "test";
[user commit];
user = [[[User query]where:@"username = \"test\""]fetch][0];

Session *session = [Session new];
session.user = user;
[session commit];
session = [[[Session query]whereWithFormat:@"user = %@", user.Id]fetch][0];

Config *config = [Config new];
config.user = user;
[config commit];
config = [[[Config query]whereWithFormat:@"user = %@", user.Id]fetch][0];

使用这种方案在没有交易的情况下进行编码,效果很好。但是,一旦我尝试在事务中执行此操作,我就会以用户对象未在 Session 和 Config 中被 linked 结束(事务中的查询永远找不到先前提交的对象)。正如我从文档中了解到的那样,提交不是事务性提交,而是插入或更新。

有没有办法让像这样的操作(创建和 link 多个实体)在使用 DBAccess 的事务中工作?

谢谢,

麦克风

抱歉耽搁了,我以为我已经对此做出了回应,但显然我分心了。

我已经完全复制了你的问题,我想解释一下你为什么看到你是什么。

如果我们以下面的例子为例,根据您的反馈:

[DBTransaction transaction:^{

            Department *dept = [Department new];
            dept.name = @"test dept";
            [dept commit];

            dept = [[[Department query] where:@"name = 'test dept'"] fetch].firstObject;

            Person *person = [Person new];
            person.Name = @"test";
            person.department = dept;
            [person commit];

            person = [[[Person query] whereWithFormat:@"department = %@", dept.Id] fetch].firstObject;

        } withRollback:^{

        }];

之所以会出现这种情况,是因为 DBAccess 根据对 commitremove[ 的任何调用记录了所有对象更改和指令。 =65=].

这些都包含在交易中,因此由于块中的代码是 运行 我们按照指令的调用顺序构建了一个指令列表,然后一旦块 运行 =81=],我们执行交易并查找错误。

这意味着,当您在块内时,查询不会 return 结果,因为记录还不在数据库中。

因此,DBAccess 事务在概念上与 SQL 事务不同。我想解释一下,为什么会出现这种情况,主要是处理数据库命令与处理 ORM 对象之间的区别。

我们最初的实现正如您所期望的那样,事务只是启动一个新的事务然后按顺序执行SQL并提交。

问题出现了,因为您可能有交易,在交易中或在 entityWillDeleteUpdate 中的交易插入 方法。或者甚至只是取消从事件模型生成的事件,SQL 对此一无所知。

因此,在事务中锁定 table 的数据写入器对象只会导致锁定线程。因此,我们重新编写了事务系统以记录要进行的更改列表,然后提交它们,包括可能使用触发器或事件创建的任何子更改或相关更改。

所以这就是我们现在所处位置的原因。我首先承认,这并不理想。

那么,我们从这里走向何方?

嗯,这取决于使用事务背后的原因。如果是出于性能原因,那么您可以改用 DBContext,它的工作方式与正常的 SQL 事务非常相似,但也有不跟踪任何其他代码对任何对象所做的所有更改的所有缺点,这些更改可能无意中从块内的代码调用。

下面是上下文正确处理嵌入对象的示例,但您原来的查询问题仍然是个问题。

DBContext* c = [DBContext new];

        Department *dept = [Department new];
        dept.name = @"test dept";
        [c addEntityToContext:dept];

        Person *person = [Person new];
        person.Name = @"test";
        person.department = dept;
        [c addEntityToContext:person];

        [c commit];

但如果您的目的是避免孤立记录或数据完整性问题,那么上下文并不理想,因为没有回滚条款。

最初 DBAccess 没有任何事务支持,原因很简单,除了数据库损坏,或者 运行宁出 space 不可能意外或故意生成会阻止将对象提交到数据库的错误。所有对对象的赋值都经过持久化能力测试,如果开发人员试图存储 ORM 不支持的对象,就会在委托上引发错误,但这将是一个编码错误,在所有版本的 ORM 中都很常见应用程序并将在测试中找到。

我很欣赏这是一个有点大的主张,但没有限制,一个灵活的类型存储系统,如果需要,它将在数字列中存储一个字符串,线程安全和 WAL 的使用来克服SQLite 的遗留问题与这些并发问题。

对象也可以保存在树中,所以如果你有一个人 class 并且有一个定义为 属性 的相关对象,对人的提交将在单个人中提交也可以操作任何子对象,层叠向下。

并不是每个人都对这种实现方式感到满意,因此我们将交易实现为一种人们感觉更舒适的方法table。

那么现在怎么办?

好吧,你的例子中似乎确实存在一个有效的错误,因为对象 comital 和赋值的级联被破坏了。由于 object.Id 被拉出并作为参数添加到 SQL 语句中,但此时尚未为该对象分配 PK 值。所以我们将解决这个问题,以存储实际的 DBObject 本身,在执行特定语句时,它会被分配一个 PK 值,从而解决这个问题。

作为现在应该起作用的解决方法,使用基于 NSString* 的 Id 列会生成一个 GUID,该 GUID 在持久化之前可用。

如果您愿意给我们发送电子邮件,我们会及时通知您,或者如果您确实想向我们提供反馈或实施想法,我们将不胜感激。

谢谢 阿德里安