2012-05-07 4 views
2

Просто наткнулся на то, что внешние ключи по умолчанию отключены в sqlite. У меня есть «on delete casdade» внешний ключ, и удаление записей родительской таблицы не удаляет дочерние записи. Различные сообщения указывают, что вам нужно включить это для каждого соединения «PRAGMA foreign_keys = ON;». Итак, где его включить в FMDatabase? Я предпочел бы установить некоторые настройки вместо запуска команды перед каждым SQL stmt. PS. Я использую FMDatabaseQueue.Как включить внешние ключи в FMDatabase?

ответ

8

Я быстро проверил и PRAGMA foreign_keys = ON; отлично работает для меня как на 5.1-симуляторе, так и на 5.1 iPod Touch. Как предполагает ccgus, вы должны кэшировать соединение с базой данных. Если вы используете очередь, просто кешируйте очередь и реорганизуйте свой код, чтобы он не создавал новую очередь каждый раз, когда вам нужно использовать базу данных. С вашим текущим подходом, какой смысл иметь очередь, если вы на самом деле не используете ее, но каждый раз создаете новую?

Но вернемся к вопросу, как вы уже знаете, внешние ключи отключены по умолчанию, поэтому вам нужно сначала включить его. Мне удалось сделать это с PRAGMA foreign_keys = ON;, вот еще некоторые из тестового кода я использовал:

//create database 
NSString* dbPath = [(NSArray*)NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; 
dbPath = [dbPath stringByAppendingPathComponent:@"test.db"]; 
db = [FMDatabase databaseWithPath:dbPath]; 
if ([db open]) { 
    NSLog(@"Database %@ opened", dbPath); 
    //check for foreign_key 
    NSString* sql = @"PRAGMA foreign_keys"; 
    FMResultSet *rs = [db executeQuery:sql]; 
    int enabled; 
    if ([rs next]) { 
     enabled = [rs intForColumnIndex:0]; 
    } 
    [rs close]; 
    if (!enabled) { 
     // enable foreign_key 
     sql = @"PRAGMA foreign_keys = ON;"; 
     [db executeUpdate:sql]; 
     // check if successful 
     sql = @"PRAGMA foreign_keys"; 
     FMResultSet *rs = [db executeQuery:sql]; 
     if ([rs next]) { 
      enabled = [rs intForColumnIndex:0]; 
     } 
     [rs close]; 
    } 
    // do your stuff here, or just cache the connection 
} else { 
    NSLog(@"Failed to open %@", dbPath); 
} 

Выглядит довольно просто, единственное, что приходит на ум, что вы использовали executeQuery вместо executeUpdate.

+0

Хорошо, но вы не закрываете 'rs'. –

+0

Это правда, однако в документации fmdb говорится: «Как правило, нет необходимости - закрывать сам FMResultSet, поскольку это происходит, когда либо результирующий набор освобождается, либо родительская база данных закрыта.» – lawicko

+0

Это дало мне предупреждения в консоли пока я не стал закрывать их явно. –

1

Зачем вам нужно запускать его перед каждым заявлением? Просто откройте базу данных, запустите свои прагмы, а затем кешируйте соединение для использования позже (что вы должны делать в любом случае ...).

0

В Swift 3 я использую эту функцию для открытия базы данных с включенными иностранными ключами.

func openDB()-> FMDatabase? { 
    let fileURL = try! FileManager.default 
     .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) 
     .appendingPathComponent("name.sqlite") 

    let database = FMDatabase(url: fileURL) 

    guard database.open() else { 
     print("Unable to open database") 
     return nil 
    } 
    do { 
     try database.executeQuery("PRAGMA foreign_keys = ON", values: nil) 
    } catch { 
     print("failed: \(error.localizedDescription)") 
    } 
    return database 
} 
Смежные вопросы