当我更新我的sqlite核心数据存储时,如何避免在SQL执行期间出现“错误:约束失败”?

时间:2022-06-01 21:27:14

Our app lets the user choose a location from a list which is provided by an API. The list updates infrequently and only by adding items, so instead of hitting the API every time, the app comes with a snapshot in a Core Data sqlite store, and we want it to periodically update the list. The code to do so works like this:

我们的应用程序允许用户从API提供的列表中选择位置。该列表不经常更新,只能通过添加项目进行更新,因此应用程序在Core Data sqlite商店中提供快照,而不是每次都访问API,我们希望它定期更新列表。这样做的代码是这样的:

  • create a managed object context for the thread
  • 为线程创建托管对象上下文

  • get the full list from the API
  • 从API获取完整列表

  • for each one:
    • find the Location in the context with a matching locationID
    • 在上下文中找到匹配的locationID的位置

    • if not found, insert a new one into the context
    • 如果未找到,请在上下文中插入新的

    • update the Location with the new information
    • 使用新信息更新位置

  • 对于每一个:在上下文中找到具有匹配的locationID的位置(如果未找到),在上下文中插入新的位置更新具有新信息的位置

  • save the context
  • 保存上下文

When starting with a blank DB, this works fine. However, when we run it a second time it fails during the save with the message "error during SQL execution : constraint failed". It does this even if I limit it to one location. If I turn on SQL debugging, I see the following:

从空白数据库开始时,这很好用。但是,当我们第二次运行它时,它在保存期间失败,并显示消息“SQL执行期间出错:约束失败”。即使我将它限制在一个位置,它也会这样做。如果我打开SQL调试,我会看到以下内容:

CoreData: sql: BEGIN EXCLUSIVE
CoreData: sql: COMMIT
CoreData: sql: BEGIN EXCLUSIVE
CoreData: sql: INSERT INTO ZLOCATION(Z_PK, Z_ENT, Z_OPT, ZGEOID, ZCOUNTY, ZCOUNTRYCODE, ZNAME, ZLATITUDE, ZLONGITUDE, ZLANGUAGECODE) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
CoreData: error: (19) constraint failed
CoreData: annotation: Disconnecting from sqlite database due to an error.

Then it reconnects and tries again a few times before giving up.

然后重新连接并再次尝试几次然后放弃。

My code is definitely finding the old Locations and the objects are all valid - or at least [object validateForUpdate] returns YES. What does the error mean? Is there a way of finding out which constraint is failing?

我的代码肯定是找到旧的Locations并且对象都是有效的 - 或者至少[object validateForUpdate]返回YES。错误是什么意思?有没有办法找出哪个约束失败?

If I use the binary store, the error goes away - but the binary store is atomic and blocks for ages on writes. It looks like a bug in the sqlite store - has anyone found a workaround?

如果我使用二进制存储,则错误消失 - 但二进制存储是原子的并且在写入时会阻塞多年。它看起来像sqlite商店中的一个错误 - 有没有人找到一个解决方法?

2 个解决方案

#1


6  

I noticed that it was doing INSERT INTO ZLOCATION not UPDATE ZLOCATION, so I had a look at the bit where I was inserting the locations into the context. I had:

我注意到它正在执行INSTO INTO ZLOCATION而不是更新ZLOCATION,所以我看了一下我将位置插入上下文的位置。我有:

if ([object isInserted])
{
    if (![object validateForUpdate:&error])
    {
        NSLog(@"Invalid object %@: %@, %@", object, error, [error userInfo]);   
        break;
    }
}
else
{
    if ([object validateForInsert:&error])
    {
        [context insertObject:object];
    }
    else
    {
        NSLog(@"Invalid object %@: %@, %@", object, error, [error userInfo]);   
        break;
    }
}

What I didn't know is that, for objects coming out of the database, [object isInserted] is false. So, I was inserting an already-inserted object and it was crashing as a result. When I changed it to [object managedObjectContext], my problem went away.

我不知道的是,对于来自数据库的对象,[object isInserted]为false。所以,我插入一个已经插入的对象,结果就崩溃了。当我将其更改为[object managedObjectContext]时,我的问题就消失了。

#2


1  

It's most likely a validation error. If you have custom validation code, check that.

这很可能是验证错误。如果您有自定义验证码,请检查。

A suprising number of weird errors with context result from problems with threads. I would run the code on the front thread. If it works, then you have a threading problem.

上下文中出现了一些奇怪的错误,这些错误来自线程问题。我会在前端线程上运行代码。如果它工作,那么你有一个线程问题。

#1


6  

I noticed that it was doing INSERT INTO ZLOCATION not UPDATE ZLOCATION, so I had a look at the bit where I was inserting the locations into the context. I had:

我注意到它正在执行INSTO INTO ZLOCATION而不是更新ZLOCATION,所以我看了一下我将位置插入上下文的位置。我有:

if ([object isInserted])
{
    if (![object validateForUpdate:&error])
    {
        NSLog(@"Invalid object %@: %@, %@", object, error, [error userInfo]);   
        break;
    }
}
else
{
    if ([object validateForInsert:&error])
    {
        [context insertObject:object];
    }
    else
    {
        NSLog(@"Invalid object %@: %@, %@", object, error, [error userInfo]);   
        break;
    }
}

What I didn't know is that, for objects coming out of the database, [object isInserted] is false. So, I was inserting an already-inserted object and it was crashing as a result. When I changed it to [object managedObjectContext], my problem went away.

我不知道的是,对于来自数据库的对象,[object isInserted]为false。所以,我插入一个已经插入的对象,结果就崩溃了。当我将其更改为[object managedObjectContext]时,我的问题就消失了。

#2


1  

It's most likely a validation error. If you have custom validation code, check that.

这很可能是验证错误。如果您有自定义验证码,请检查。

A suprising number of weird errors with context result from problems with threads. I would run the code on the front thread. If it works, then you have a threading problem.

上下文中出现了一些奇怪的错误,这些错误来自线程问题。我会在前端线程上运行代码。如果它工作,那么你有一个线程问题。