Как решить проблему SQLiteException: нет такой таблицы: Cars (код 1 SQLITE_ERROR): при компиляции: SELECT DISTINCT * FROM Cars? - PullRequest
0 голосов
/ 17 января 2020

Я играю с SQLite в своем приложении и пытаюсь импортировать резервную копию базы данных в приложение. Все отлично работает для Oreo и ниже SDK версий (до SDK 16), но не для Android P и выше. Моя база данных:

    private val dbTemp = DB_NAME + "_tmp"
    private val dbBackup = context.openOrCreateDatabase(dbTemp, Context.MODE_PRIVATE, null)

    override fun onCreate(db: SQLiteDatabase) {
            db.execSQL("CREATE TABLE $TABLE ($ID INTEGER PRIMARY KEY AUTOINCREMENT, $TITLE TEXT, $CAR TEXT, $DATE TEXT);")
        }

        override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
            db.execSQL("DROP TABLE IF EXISTS $TABLE")
            onCreate(db)
        }

Я копирую резервную копию базы данных, заменяя текущую базу следующим образом:

private fun copyData() {
        db.delete(TABLE, null, null)
        val cursor = dbBackup.query(  // <- here occurs the problem!
            true,
            TABLE,
            null,
            null,
            null,
            null,
            null,
            null,
            null
        )
        cursor.moveToFirst()
        while (!cursor.isAfterLast) {
            db.insert(TABLE, null, modelToValues(cursorToModel(cursor)))
            cursor.moveToNext()
        }
        cursor.close()
        context.deleteDatabase(dataTmp)
    }

Это брошенная проблема:

Process: com.example.cars, PID: 18674
        android.database.sqlite.SQLiteException: no such table: Cars (code 1 SQLITE_ERROR): , while compiling: SELECT DISTINCT * FROM Cars
            at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
            at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:903)
            at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:514)
            at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
            at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)
            at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
            at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:46)
            at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1408)
            at android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.java:1255)
            at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1126)
            at com.easyapps.cryptnote.ListDatabase.copyData(CarsDatabase.kt:163)
            at com.easyapps.cryptnote.ListDatabase.importToApp(CarsDatabase.kt:155)
            at com.example.cars.BackupActivity$onCreate$3$$special$$inlined$apply$lambda$2.onClick(BackupActivity.kt:104)
            at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:172)
            at android.os.Handler.dispatchMessage(Handler.java:106)
            at android.os.Looper.loop(Looper.java:193)
            at android.app.ActivityThread.main(ActivityThread.java:6669)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

Я пытался объединить решения из этих ссылок ссылка и ссылка , но безуспешно. Для получения дополнительной информации мое решение для резервного копирования и импорта баз данных основано на демонстрационном проекте , который слишком стар, но, тем не менее, я мог бы использовать его для своего проекта в Kotlin.

Метод резервного копирования:

fun exportToSD() {
        createFolderOnSD()
        val data = Environment.getDataDirectory()
        val backupDbPath = File(sdFolder, "/(${utilities.getDate("dd_MM_yyyy_HH_mm")}) " + DB_NAME)

        try {
            val source = FileInputStream(File(data, currentDbPath)).channel
            val destination = FileOutputStream(backupDbPath).channel
            destination.transferFrom(source, 0, source.size())
            source.close()
            destination.close()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

Метод импорта в приложение:

fun importToApp(fileNameOnSD: String) {
        val sd = File(sdFolder)
        if (sd.canWrite()) {
            val currentDB = File(Environment.getDataDirectory(), dataTmp)
            val backupDB = File(sd, fileNameOnSD)

            if (currentDB.exists()) {
                try {
                    val src = FileInputStream(backupDB).channel
                    val dst = FileOutputStream(currentDB).channel
                    dst.transferFrom(src, 0, src.size())
                    src.close()
                    dst.close()
                } catch (e: FileNotFoundException) {
                    e.printStackTrace()
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }
        }
        copyData()
    }

Ответы [ 3 ]

1 голос
/ 17 января 2020

Вы можете отключить параметр Write Ahead Logging, добавив следующее в свой класс SQLiteOpenHelper (обратите внимание на onConfigure, а не onOpen, как в других связанных ответах) - это работает в моем приложении.

// Called when the database connection is being configured.
    // Configure database settings for things like foreign key support, write-ahead logging, etc.
    @Override
    public void onConfigure(SQLiteDatabase db) {
        super.onConfigure(db);
        // Later version of Android seem to enable writeAhead by default, so for consistency explicitly disable it
        db.disableWriteAheadLogging();
    }
0 голосов
/ 17 января 2020

Когда вы делаете резервную копию, файл -wal (имя файла базы данных с суффиксом -wal) не пустой, а также не резервируется / не восстанавливается вместе с файлом базы данных?

Что вы описан симптом c проблем, связанных с тем, что файл -wal , который не пуст (полностью проверен) и не скопирован и впоследствии восстановлен.

Правильный способ резервного копирования базы данных Режим WAL используется для резервного копирования файла базы данных и файла -wal и восстановления обоих, либо для обеспечения того, чтобы база данных была полностью проверена (т.е. изменения в файле -wal были применены к файлу базы данных), а затем требуется только резервное копирование файла базы данных.

  • закрытие всех соединений с базой данных будет полностью проверять базу данных.

Причина этого заключается в том, что с Android 9+ режим по умолчанию - WAL (запись в журнал впереди) вместо журнала. В режиме журнала изменения применяются к базе данных, а журнал / журнал этих изменений сохраняется в файле журнала. Потеря файла журнала означает, что вы не можете откатить изменения. В то время как в режиме WAL изменения записываются в файл WAL, который фактически является частью базы данных. В случае потери файла WAL изменения в лучшем случае будут потеряны.

0 голосов
/ 17 января 2020

Попробуйте вызвать db.close () один раз после копирования данных в метод copyData ()

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