SQLite и QSqlQuery - несколько вызовов execBatch() не выполняются

У меня есть класс, который должен многократно выполнять пакетные вставки. Экземпляр QSqlQuery является членом этого класса. Сначала я попробовал этот код:

TOMCache::TOMCache(QObject *parent): QObject(parent) {    
    m_setWithGeoQuery.prepare("INSERT OR REPLACE INTO cache_storage(key, value, lat, lng, expiration) VALUES(?, ?, ?, ?, ?)");
}

void TOMCache::flushBuffer() {
    QSqlQuery &query = m_setWithGeoQuery;
    query.addBindValue(m_buffer.at(0));
    query.addBindValue(m_buffer.at(1));
    query.addBindValue(m_buffer.at(2));
    query.addBindValue(m_buffer.at(3));
    query.addBindValue(m_buffer.at(4));
    if(!query.execBatch())
        qWarning() << "-- execBatch() failed: " << query.lastError().text();
    m_buffer.clear();
    m_buffer << QVariantList() << QVariantList() << QVariantList() << QVariantList() << QVariantList();
}

Это не сработало. В созданном файле SQLite было 65536 байт, но в таблице cache_storage была только 1 строка (flushBuffer() вызывалась много раз). Похоже, что первый вызов execBatch() был выполнен успешно, а последующие вызовы молча терпели неудачу (они продолжали возвращать true). Я понял, что работает следующий код:

void TOMCache::flushBuffer() {

    QSqlQuery query;
    query.prepare("INSERT OR REPLACE INTO cache_storage(key, value, lat, lng, expiration) VALUES(?, ?, ?, ?, ?)");
    query.addBindValue(m_buffer.at(0));
    query.addBindValue(m_buffer.at(1));
    query.addBindValue(m_buffer.at(2));
    query.addBindValue(m_buffer.at(3));
    query.addBindValue(m_buffer.at(4));
    if(!query.execBatch())
        qWarning() << "-- execBatch() failed: " << query.lastError().text();
    m_buffer.clear();
    m_buffer << QVariantList() << QVariantList() << QVariantList() << QVariantList() << QVariantList();
}

У меня есть пара вопросов:

  1. Нужно ли создавать и уничтожать QSqlQuery для каждого вызова execBatch()?

  2. Если да, то почему последующие вызовы execBatch() делают вид, что выполняются успешно?

  3. Как насчет большого размера файла в первом случае? (Там всего 2 столика, другой пустой)

Я должен упомянуть, что это часть приложения, работающего на ОС BlackBerry 10. Чтобы каждый раз не создавать и не уничтожать QSqlQuery, я также пытался вызвать query.clear() в конце flushBuffer(), но это, похоже, также удалило подготовленный SQL из экземпляра QSqlQuery (следующий вызов execBatch() не удался) .

РЕДАКТИРОВАТЬ: я старался каждый раз избегать синтаксического анализа SQL, поэтому я попытался сохранить m_setWithGeoQuery без изменений и просто сделать его копию в flushBuffer():

    QSqlQuery query(m_setWithGeoQuery);
    query.addBindValue(m_buffer.at(0));
    ...

Удивительно, но это не работает. Он ведет себя так же, как и первый фрагмент.


person Andrej Repiský    schedule 04.03.2014    source источник
comment
У меня точно такая же проблема с sqlite и mysql. Вы нашли решение?   -  person Eric Lemanissier    schedule 12.01.2015
comment
Ну, я просто использую код во втором фрагменте кода. Стоимость синтаксического анализа SQL для больших пакетных вставок незначительна.   -  person Andrej Repiský    schedule 12.01.2015


Ответы (1)


Это ошибка в Qt: https://bugreports.qt.io/browse/QTBUG-43874< /а>. Это происходит только в том случае, если выполняются оба условия:

  • несколько раз вызывая execBatch по одному и тому же запросу
  • Значения связаны с использованием QSqlQuery::addBindValue.

Как следствие, вы можете обойти эту ошибку, вызвав QSqlQuery::bindValue, поэтому следующий код должен работать

TOMCache::TOMCache(QObject *parent): QObject(parent) {    
    m_setWithGeoQuery.prepare("INSERT OR REPLACE INTO cache_storage(key, value, lat, lng, expiration) VALUES(?, ?, ?, ?, ?)");
}

void TOMCache::flushBuffer() {
    QSqlQuery &query = m_setWithGeoQuery;
    query.bindValue(0, m_buffer.at(0));
    query.bindValue(1, m_buffer.at(1));
    query.bindValue(2, m_buffer.at(2));
    query.bindValue(3, m_buffer.at(3));
    query.bindValue(4, m_buffer.at(4));
    if(!query.execBatch())
        qWarning() << "-- execBatch() failed: " << query.lastError().text();
    m_buffer.clear();
    m_buffer << QVariantList() << QVariantList() << QVariantList() <<     QVariantList() << QVariantList();
}
person Eric Lemanissier    schedule 16.01.2015
comment
Теперь я вызываю bindValue(), как вы предлагаете, и prepare() в конструкторе. Скорость пакетных вставок совсем не улучшилась. Я вставляю 2000 строк в каждый execBatch(). Но все равно спасибо. - person Andrej Repiský; 12.02.2015
comment
Если я не ошибаюсь, QSQLiteDriver не имеет функции BatchOperations, поэтому он эмулирует execBatch, запуская один exec для каждого элемента пакета. Можно легко улучшить производительность, вызвав QSqlDatabase::transaction перед execBatch и QSqlDatabase::commit после - person Eric Lemanissier; 13.02.2015