0

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

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 中链接(事务中的查询永远不会找到先前提交的对象)。正如我从文档中了解到的那样,提交不是事务性提交,而是插入或更新。

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

谢谢,

麦克风

4

1 回答 1

0

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

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

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

[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的任何调用记录了所有对象更改和指令。

这些都包含在一个事务中,因此当块中的代码运行时,我们按照它们被调用的顺序建立一个指令列表,然后一旦块运行,我们执行事务并查找错误.

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

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

我们最初的实现如你所料,事务只是启动了一个新事务,然后按顺序执行 SQL 并提交它。

问题出现了,因为您可以在entityWillDeleteUpdateInsert方法中的事务或事务中进行事务。甚至只是取消从事件模型生成的事件,SQL 对此一无所知。

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

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

那么,我们该何去何从 ?

这取决于使用事务背后的原因。如果是出于性能原因,那么您可以使用 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 没有任何事务支持,原因很简单,除了数据库损坏或空间不足之外,不可能意外或故意生成错误,从而阻止对象提交到数据库。对对象的所有分配都经过持久性能力测试,如果开发人员尝试存储 ORM 中不支持的对象,则会在委托上引发错误,但这将是一个编码错误,在所有版本的应用程序,并将在测试中找到。

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

对象也可以保存在树中,因此,如果您有一个 person 类并且将相关对象定义为属性,则对 person 的提交也将在单个操作中提交任何子对象,并向下级联。

并不是每个人都对这种实现感到满意,所以我们将交易作为一种人们感觉更舒服的方法来实现。

所以现在怎么办 ?

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

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

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

谢谢阿德里安

于 2015-07-27T11:26:12.703 回答