Удаление из SQLite и запись в него, использование данных JSON для синхронизации удаленных таблиц в MySQL - PullRequest
1 голос
/ 08 августа 2011

Прежде всего, я читал много вопросов, которые кажутся похожими на мои, и даже получил несколько советов от нескольких, чтобы достигнуть точки, где я нахожусь, поэтому я благодарю вас, пользователи stackoverflow.

Хорошо, у меня есть пять таблиц базы данных в SQLite для iOS-приложения и их эквивалент в MySQL для удаленного сервера. Данные, которые я предварительно загрузил с таблицами SQLite, отображаются нормально, а функции приложения ожидаются. Проблема возникает, когда я загружаю данные JSON из удаленной базы данных, чтобы заменить локальную информацию, хранящуюся на устройстве iOS. Я буду упрощать, ссылаясь на одну таблицу:

    // Create an object, reading its information from the local database.
    Pregunta *objPregunta = [[Pregunta alloc] initWithPrimaryKey:0 database:delegate.database];
    // Erase all data in the object's table (preguntas) see method below.
    [objPregunta deleteAllFromDatabase];
    // Obtain the remote objects retrieved via JSON, and cycle them.
    NSArray *preguntas = (NSArray *)[responseDict objectForKey:@"preguntas"];
    for (id pregunta in preguntas) {
        // Create a dictionary storing the JSON object.
        NSDictionary *pregunta_dict = (NSDictionary *) pregunta;

        // Send the dictionary to the function for SQLite insertion.
        [objPregunta insertNewRecordIntoDatabase:pregunta_dict];
    }
    // Free the object resources.
    [objPregunta release];

Это функция удаления SQLite, связанная с объектом Pregunta в приведенном выше коде:

- (void) deleteAllFromDatabase {
    if(delete_statement == nil) {
        const char *sql = "DELETE FROM preguntas";

        if (sqlite3_prepare_v2(database, sql, -1, &delete_statement, NULL) != SQLITE_OK) {
            NSAssert1(0, @"Error: Failed to prepare SQL statement: %s.", sqlite3_errmsg(database));
        }
    }

    if (sqlite3_step(delete_statement) != SQLITE_DONE) {
        NSAssert1(0, @"Failed to save priority with message: %s.", sqlite3_errmsg(database));
    }

    sqlite3_reset(delete_statement);
}

И, наконец, это функция, которая вставляет новую запись в базу данных, используя переданный ей объект словаря:

- (void) insertNewRecordIntoDatabase:(NSDictionary *)pregunta_dict {
    if (insert_statement == nil) {
        const char *sql = "INSERT INTO preguntas (id_forma, numero, seccion, texto, respuesta, comentario, requiere_fotografia, fotografia, tipo) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)";

        self.id_forma            = [[pregunta_dict objectForKey:@"id_forma"] integerValue];
        self.numero              = [[pregunta_dict objectForKey:@"numero"]   integerValue];
       if([pregunta_dict objectForKey:@"seccion"] == [NSNull null]) {
            self.seccion         = nil;
        } else {
            self.seccion         = [pregunta_dict  objectForKey:@"seccion"];
        }
        self.texto               = [pregunta_dict  objectForKey:@"texto"];
        if([pregunta_dict objectForKey:@"respuesta"] == [NSNull null]) {
            self.respuesta       = nil;
        } else {
            self.respuesta       = [pregunta_dict  objectForKey:@"respuesta"];
        }
        if([pregunta_dict objectForKey:@"comentario"] == [NSNull null]) {
            self.comentario      = nil;
        } else {
            self.comentario      = [pregunta_dict  objectForKey:@"comentario"];
        }
        self.requiere_fotografia = [pregunta_dict  objectForKey:@"requiere_fotografia"];
        if([pregunta_dict objectForKey:@"fotografia"] == [NSNull null]) {
            self.fotografia      = nil;
        } else {
            self.fotografia      = [pregunta_dict  objectForKey:@"fotografia"];
        }
        self.tipo                = [pregunta_dict  objectForKey:@"tipo"];

        if (sqlite3_prepare_v2(database, sql, -1, &insert_statement, NULL) != SQLITE_OK) {
            NSAssert1(0, @"Error: Failed to prepare SQL statement: %s.", sqlite3_errmsg(database));
        } else {
            @try {
                sqlite3_bind_int (insert_statement, 1, self.id_forma);
                sqlite3_bind_int (insert_statement, 2, self.numero);
                sqlite3_bind_text(insert_statement, 3, [self.seccion             UTF8String], -1, SQLITE_TRANSIENT);
                sqlite3_bind_text(insert_statement, 4, [self.texto               UTF8String], -1, SQLITE_TRANSIENT);
                sqlite3_bind_text(insert_statement, 5, [self.respuesta           UTF8String], -1, SQLITE_TRANSIENT);
                sqlite3_bind_text(insert_statement, 6, [self.comentario          UTF8String], -1, SQLITE_TRANSIENT);
                sqlite3_bind_text(insert_statement, 7, [self.requiere_fotografia UTF8String], -1, SQLITE_TRANSIENT);
                sqlite3_bind_text(insert_statement, 8, [self.fotografia          UTF8String], -1, SQLITE_TRANSIENT);
                sqlite3_bind_text(insert_statement, 9, [self.tipo                UTF8String], -1, SQLITE_TRANSIENT);
            }
            @catch (NSException *exception) {
                ;
            }
        }
    }

    if (sqlite3_step(insert_statement) != SQLITE_DONE) {
        NSAssert1(0, @"Failed to prepare SQL statement: %s.", sqlite3_errmsg(database));
    } else {
    NSLog(@"Pregunta insert_id: %lld", sqlite3_last_insert_rowid(database));
    }

    sqlite3_reset(insert_statement);
}

Теперь проблема с кодом состоит в том, что каким-то образом кажется, что он вставляет строки неправильно, потому что, когда процессы удаления / вставки завершаются для всех таблиц, и я читаю данные, вставленные из SQLite, я получаю дублированные значения в база данных.

Я думал, что это как-то связано с функцией deleteAllFromDatabase, но, опять же, это кажется слишком простым, чтобы вызывать какие-либо проблемы. Я также попытался напрямую указать, какие значения я хочу сохранить для первичных ключей, но приложение вылетает из-за того, что, по-видимому, ограничение не выполняется (у меня нет внешних ключей, только первичный ключ в каждой таблице).

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

Спасибо за любой вклад, который вы можете предоставить. Я буду публиковать любые дополнительные сведения, которые вам могут понадобиться для раскрытия, чтобы лучше понять, что я делаю.


Вот некоторые выводы, которые мне удалось поймать:

  • Поскольку я отображаю строки в TableView, я вижу, что все значения в таблице базы данных точно такие же (я также NSLogged их), за исключением первичного ключа (который автоматически генерируется SQLite INTEGER AUTOINCREMENT) ... так что это, вероятно, означает, что есть что-то не так с моими инструкциями по вставке или способом отправки словарные данные для каждой вставляемой строки.

  • Что касается данных в ресурсе JSON, то проблем вообще нет с его помощью я захватил всю строку, отступил и отобразил это, чтобы проверить полную целостность информации, и все это соответствует что из удаленных таблиц MySQL.

  • В первом блоке кода цикл foreach гласит: for (id pregunta in preguntas) {, однако в этой статье вы обнаружите, что вместо них используется for (id *item in items) { (с * в качестве указателя для ). Я пытался сделать его похожим, но я получаю сообщение об ошибке, и код не будет компилироваться, несмотря ни на что.

  • Есть ли вероятность, что мне потребуется каким-то образом сбросить DSDictionary, который я передам функции insertNewRecordIntoDatabase:, чтобы она не содержала предыдущих значений? Я бы предположил, что для каждой итерации данные будут разными, но это доказывает, что я ошибаюсь.

Ответы [ 2 ]

1 голос
/ 11 августа 2011

Я наконец-то решил проблему. Переменная класса static insert_statement (из sqlite3_stmt) вызывала проблему, потому что, когда я удалил их из объявления класса и вместо этого создал / завершил их в функции - (void) insertNewRecordIntoDatabase:(NSDictionary *)pregunta_dict, все вставки работали отлично. У меня нет повторяющихся строк в локальной базе данных SQLite.

Итак, извлеченный урок: по крайней мере для операторов INSERT убедитесь, что вы используете новые sqlite3_stmt объекты и sqlite3_finalize() их, вместо того, чтобы использовать их через sqlite3_reset(), как я делал, в противном случае вы можете получить оператор вставки тех же данных при последующих вызовах.

Я также внес несколько изменений в функцию вставки всего класса, поскольку теперь я передаю ему объект класса того же типа, что и параметр, и получаю доступ к его свойствам для сопоставления столбцов во время вставки. Вот мой окончательный код:

- (void) insertInstanceIntoDatabase: (Pregunta *)pregunta database: (sqlite3 *)db {
    sqlite3_stmt *stmt_insert = nil;
    const char *sql = "INSERT INTO preguntas (id_pregunta, id_forma, numero, seccion, texto, respuesta, comentario, requiere_fotografia, fotografia, tipo) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";

    if (sqlite3_prepare_v2(db, sql, -1, &stmt_insert, NULL) != SQLITE_OK) {
        NSAssert1(0, @"Error: Failed to prepare SQL statement: %s.", sqlite3_errmsg(db));
    } else {
        @try {
            sqlite3_bind_int (stmt_insert, 1,  pregunta.id_pregunta);
            sqlite3_bind_int (stmt_insert, 2,  pregunta.id_forma);
            sqlite3_bind_int (stmt_insert, 3,  pregunta.numero);
            sqlite3_bind_text(stmt_insert, 4, [pregunta.seccion             UTF8String], -1, SQLITE_TRANSIENT);
            sqlite3_bind_text(stmt_insert, 5, [pregunta.texto               UTF8String], -1, SQLITE_TRANSIENT);
            sqlite3_bind_text(stmt_insert, 6, [pregunta.respuesta           UTF8String], -1, SQLITE_TRANSIENT);
            sqlite3_bind_text(stmt_insert, 7, [pregunta.comentario          UTF8String], -1, SQLITE_TRANSIENT);
            sqlite3_bind_text(stmt_insert, 8, [pregunta.requiere_fotografia UTF8String], -1, SQLITE_TRANSIENT);
            sqlite3_bind_text(stmt_insert, 9, [pregunta.fotografia          UTF8String], -1, SQLITE_TRANSIENT);
            sqlite3_bind_text(stmt_insert,10, [pregunta.tipo                UTF8String], -1, SQLITE_TRANSIENT);
        }
        @catch (NSException *exception) {
            ;
        }
    }

    if (sqlite3_step(stmt_insert) != SQLITE_DONE) {
        NSAssert1(0, @"Failed to prepare SQL statement: %s.", sqlite3_errmsg(db));
    }

    sqlite3_finalize(stmt_insert);
}
1 голос
/ 08 августа 2011

Вот возможные объяснения дубликатов. Сначала проверьте это:

  1. Вы неправильно читаете и отображаете записи.
  2. Когда вы вставляете записи, вы вставляете дважды.
    • (Возможно, функция, выполняющая вставку, вызывается более одного раза.)
  3. База данных MySQL на сервере содержит дубликаты.
  4. Вы не удалили исходные записи.
    • (Полагаю, вы это исключили.)

Надеюсь, это поможет. Cheers,
Sascha

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...