NSManagedObjectContext不能正确保存到SQLite [英] NSManagedObjectContext Not saving properly to SQLite

查看:224
本文介绍了NSManagedObjectContext不能正确保存到SQLite的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

SO,

我有一个Core Data的问题,试图将数据正确保存到SQLite数据库。

I have a problem with Core Data and trying to save data out to a SQLite database correctly.

我有两个应用程序:一个用于加载SQLite数据(Lets调用Loader.App),一个用于显示数据(Display.App)。加载器只是一个方便的桥梁之间的基于Web的CMS导出数据作为JSON数据和应用程序,需要一个SQLite DB(再次,由Core Data加载)。

I have two "Apps": one for loading the SQLite data (Lets call that "Loader.App"), and one for displaying the data ("Display.App"). The loader is just a convenience "bridge" between a web-based CMS which exports data as JSON data and the App which needs an SQLite DB (which is, again, loaded by Core Data).

当我在Loader.App中保存上下文时,它将数据保存到SQLite文件中。我可以在SQLite读取器(如Base.App)中打开此文件,它显示所有的数据。 问题是:当我把这个SQLite文件带到Display.App时,它会将文件复制到文档目录中,但它里面没有任何数据。但是,正确的表 - 就像在加载数据之前的SQLite文件。

When I save the context in Loader.App, it saves the data into the SQLite file. I can open this file in a SQLite reader (like Base.App) and it shows all the data. The problem is: when I bring that SQLite file to Display.App, it copies the file into the documents directory but it doesn't have any data inside of it. It does, however, have all of the proper tables - just like the SQLite file before I load the data.

奇怪的是,如果我打开SQLite DB文件在读者.App)和VACUUM数据库,它加载在Display.App非常好。从Python中的文件io的经验我知道,如果你没有正确关闭文件,数据不从io缓冲区写入文件。显然,数据正在写入SQLite文件(因此我可以用阅读器(Base.App)打开它)。但是它让我想知道是否有一个文件关闭方法,我不调用?

The odd thing is that if I open the SQLite DB file in a reader (Base.App) and VACUUM the database, it loads in Display.App perfectly fine. From experience with file io in Python I know that if you don't close the file properly, the data isn't written to the file from the io buffer. Clearly, the data is being written to the SQLite file (thus I can open it with a reader (Base.App)). But it makes me wonder if there a file closing method that I am not calling?

基本上...

方法1:


  1. 运行Loader.App

  2. 复制MyAppDB .sqlite to Display.App

  3. 运行Display.App

strong>:MyAppDB.sqlite中没有数据

Result: MyAppDB.sqlite has no data inside of it

方法2:


  1. 运行Loader.App

  2. 使用读者(Base.App)打开MyAppDB.sqlite

  3. VACUUM

  4. 将MyAppDB.sqlite复制到Display.App

  5. 运行Display.App

  1. Run Loader.App
  2. Open MyAppDB.sqlite with reader (Base.App)
  3. VACUUM
  4. Copy MyAppDB.sqlite to Display.App
  5. Run Display.App


$ b b

结果:MyAppDB.sqlite包含数据,我们有很多快乐。

Result: MyAppDB.sqlite contains data and we have much joy.

Loader.App:

    int main(int argc, const char * argv[])
    {

        @autoreleasepool {
            // Create the managed object context
            NSManagedObjectContext *context = managedObjectContext();

            // Custom code here...
            importDataEntriesFromJSON(context);

            // Save the managed object context
            NSError *error = nil;
            if (![context save:&error]) {
                NSLog(@"Error while saving %@", ([error localizedDescription] != nil) ? [error localizedDescription] : @"Unknown Error");
                exit(1);
            }
        }
        return 0;
    }

    static NSManagedObjectModel *managedObjectModel() {
        static NSManagedObjectModel *model = nil;
        if (model != nil) {
            return model;
        }

        NSString *path = @"MyAppDB";
        path = [path stringByDeletingPathExtension];
        NSURL *modelURL = [NSURL fileURLWithPath:[path stringByAppendingPathExtension:@"mom"]];
        model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

        return model;
    }


    static NSManagedObjectContext *managedObjectContext() {
        static NSManagedObjectContext *context = nil;
        if (context != nil) {
            return context;
        }

        @autoreleasepool {
            context = [[NSManagedObjectContext alloc] init];

            NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedObjectModel()];
            [context setPersistentStoreCoordinator:coordinator];

            NSString *STORE_TYPE = NSSQLiteStoreType;

            NSString *path = @"MyAppDB";
            path = [path stringByDeletingPathExtension];
            NSURL *url = [NSURL fileURLWithPath:[path stringByAppendingPathExtension:@"sqlite"]];

            // Clear old SQLite
            NSFileManager *manager = [NSFileManager defaultManager];
            NSError *error;
            [manager removeItemAtURL:url error:&error];

            //NSError *error;
            NSPersistentStore *newStore = [coordinator addPersistentStoreWithType:STORE_TYPE configuration:nil URL:url options:nil error:&error];

            if (newStore == nil) {
                NSLog(@"Store Configuration Failure %@", ([error localizedDescription] != nil) ? [error localizedDescription] : @"Unknown Error");
            }
        }
        return context;
    }

    void importDataEntriesFromJSON( NSManagedObjectContext *context ) {
        NSError* err = nil;
        NSString* dataPath = [[NSBundle mainBundle] pathForResource:@"data_entries" ofType:@"json"];
        NSArray* data_entries = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:dataPath]
                                                        options:kNilOptions
                                                          error:&err];
        NSLog(@"Imported %lu data_entries from JSON", (unsigned long)[data_entries count]);

        [data_entries enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            DBDataEntry *dataEntry = [NSEntityDescription
                              insertNewObjectForEntityForName:@"DBDataEntry"
                              inManagedObjectContext:context];

            dataEntry.data_entry_id       = [NSNumber numberWithInt:[[obj objectForKey:@"data_entry_id"] integerValue]];
            dataEntry.data_entry_keywords = [obj objectForKey:@"data_entry_keywords"];
            dataEntry.data_entry_name     = [obj objectForKey:@"data_entry_name"];

            NSError *error;
            if (![context save:&error]) {
                NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
            }
        }];

        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"DBDataEntry"
                                          inManagedObjectContext:context];
        [fetchRequest setEntity:entity];

        NSError *error;
        NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
        for( DBDataEntry *dataEntry in fetchedObjects ) {
            //NSLog(@"data_entry_id: %@", dataEntry.data_entry_id);
            //NSLog(@"data_entry_keywords: %@", dataEntry.data_entry_keywords);
            //NSLog(@"data_entry_name: %@", dataEntry.data_entry_name);
            NSLog(@"data_entry_id: %@ name: %@", dataEntry.data_entry_id, dataEntry.data_entry_name);
        }
    }

感谢您的帮助! :)

推荐答案

最可能的原因是你正在复制SQLite文件本身而不是其日志文件。在iOS 7 Core数据通常在WAL(预写日志记录)模式下使用SQLite。这意味着除了 MyAppDB.sqlite ,还会有名为 MyAppDB.sqlite-wal MyAppDB.sqlite-shm 。这些文件是至关重要的。

The most likely reason is that you're copying the SQLite file itself but not its journal files. On iOS 7 Core Data normally uses SQLite in WAL (write-ahead logging) mode. That means that besides MyAppDB.sqlite there will be files named MyAppDB.sqlite-wal and MyAppDB.sqlite-shm. Those files are crucial. If you copy just the SQLite file but not the journals, you'll lose data (as you've seen).

当您在Base.app中打开SQLite文件时,如果您只复制SQLite文件而不是日志,和真空,日志文件中的所有更改都将滚动到主SQLite文件本身。

When you open the SQLite file in Base.app and vacuum, all the changes in the journal files are rolled into the main SQLite file itself. You're doing an extra step that eliminates the need to copy the journal files.

您有几个不同的选项:


  • 简单的方法是复制所有文件。问题已解决。

  • 另一个选项是更改加载器应用程序中的日志模式,以避免复制更多文件。你可以在添加持久存储时传递一个额外的选项:

  • The easy way is to just copy all of the files. Problem solved.
  • Another option is to change the journal mode in your loader app to avoid the need to copy more files. You'd do this by passing an extra option when adding the persistent store:

NSDictionary *options = @{ NSSQLitePragmasOption :  @{ @"journal_mode": @"DELETE" } };


使用 addPersistentStoreWithType:configuration:URL:options:error:

这篇关于NSManagedObjectContext不能正确保存到SQLite的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆