Можем ли мы использовать функции sqlite3_backup в Android для загрузки баз данных в памяти в постоянное хранилище? - PullRequest
0 голосов
/ 27 сентября 2019

Я хочу разработать приложение для Android для хранения записей в базе данных в памяти, а затем копировать их во внутреннее хранилище при закрытии приложения и загружать данные обратно в базу данных в памяти при повторном открытии приложения.Кроме запросов к базе данных и копирования отдельных записей, есть ли способ использовать sqlite3_backup_init() и sqlite3_backup_step() для этого?

1 Ответ

0 голосов
/ 27 сентября 2019

Я не верю, что доступ к базовому API-интерфейсу является тривиальной задачей, поэтому ответ, вероятно, да, НО.

Альтернативой, которая, к сожалению, недоступна в стандартном SQLite на Android, является использование VACUUM INTO (требуется SQLite 3.27.0 (если я правильно помню)) .

Однако сделать резервную копию из памяти в файл с помощью запросов не так уж сложно, и, вероятно, намного проще, чемвы ожидаете.

Следующий код является очень простым, скопируйте все таблицы (кроме таблиц с префиксом sqlite_ и android_metadata).

public class MainActivity extends AppCompatActivity {

    SQLiteDatabase memorydb;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        memorydb = SQLiteDatabase.create(null);
        memorydb.execSQL("CREATE TABLE tablex (_id INTEGER PRIMARY KEY, name TEXT)");
        memorydb.execSQL("INSERT INTO tablex (name) VALUES ('FRED'),('MARY'),('JANE')");
        copyMemoryDBToFileDB();
        memorydb.close();

    }

    private void copyMemoryDBToFileDB() {
        String attachSchema = "diskdb";
        File db = new File(getDatabasePath("mydb").getPath());
        if (db.exists()) {
            db.delete();
        }
        if (!db.getParentFile().exists()) {
            db.getParentFile().mkdirs();
        }
        String dbFilePath = db.getPath();
        SQLiteDatabase dbFile = SQLiteDatabase.openDatabase(dbFilePath, null, SQLiteDatabase.CREATE_IF_NECESSARY);
        dbFile.setForeignKeyConstraintsEnabled(false);
        dbFile.close();
        memorydb.beginTransaction();
        memorydb.execSQL("ATTACH DATABASE '" + dbFilePath + "' AS " + attachSchema);
        //Cursor csr = memorydb.rawQuery("SELECT * FROM sqlite_master WHERE type=?",new String[]{"table"});
        Cursor csr = memorydb.query("main.sqlite_master", null, "type=?", new String[]{"table"}, null, null, null);
        while (csr.moveToNext()) {
            String currentTable = csr.getString(csr.getColumnIndex("name"));
            String dbfileTable = attachSchema + "." + currentTable;
            if (currentTable.startsWith("sqlite_") || currentTable.equals("android_metadata"))
                continue;

            String crtSQL = csr.getString(csr.getColumnIndex("sql"))
                    .replace(currentTable, dbfileTable).replace("CREATE TABLE ", "CREATE TABLE IF NOT EXISTS ");
            memorydb.execSQL(crtSQL);
            memorydb.execSQL("INSERT INTO " + dbfileTable + " SELECT * FROM " + currentTable);
        }
        memorydb.setTransactionSuccessful();
        memorydb.endTransaction();
        memorydb.execSQL("DETACH DATABASE " + attachSchema);
    }
}
  • Если у вас есть представления /триггеры / индексы, тогда вышеописанное можно было бы расширить

    • было бы лучше всего сделать это, особенно триггеры и индексы, после того, как данные скопированы, так как вы не хотели бы, чтобы триггеры повторно запускали и создавали индексы последанные могут сократить затрачиваемое время.
  • Копия SQL в основном INSERT INTO diskdb.the_current_tableName SELECT * FROM the_current_tableName (обратите внимание, что не нужно отключать поддержку внешнего ключа, так какпо умолчанию он выключен).

Пример

Файл базы данных из серии, описанной выше, был скопирован и открыт в Navicat: -

enter image description here

Пример 2

В этом примере выполняется копирование из существующей копии БД IN MEMORY в БД IN MEMORY, а также копирование на диск базы данных IN MEMORY.и имеет три таблицы: -

Код как действие (поэтому контекст доступен)

: -

public class MainActivity extends AppCompatActivity {

    private static final String ATTACH_DBFILE_SCHEMA = "diskdb";
    private static final String ATTACH_MASTER_SCHEMA = "main";
    private static final String ATTACH_SCHEMA_SEPERATOR = ".";
    private static final String SQLITE_MASTER_TABLE = "sqlite_master";
    private static final String SQLITE_MASTER_NAME_COLUMN = "name";
    private static final String SQLITE_MASTER_TYPE_COLUMN = "type";
    private static final String SQLITE_MASTER_SQL_COLUMN = "sql";
    private static final String SQLITE_MASTER_TYPE_TABLE = "table";
    private static final String SQLITE_SYSTEMDB_PREFIX = "sqlite_";
    private static final String ANDROID_METADATA = "android_metadata";
    public static final String DISKDBNAME = "mydb";
    SQLiteDatabase memorydb;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        loadOrCreateMemoryDB();
        //Add some testing data (each time App is run)
        memorydb.execSQL("INSERT INTO tablex (name) VALUES ('FRED@' || (datetime('now'))),('MARY'),('JANE')");
        memorydb.execSQL("INSERT INTO tabley (name) VALUES ('BERT@' || (datetime('now'))),('SUE'),('TOM')");
        memorydb.execSQL("INSERT INTO tablez (name) VALUES ('JOHN@' || (datetime('now'))),('ALEX'),('CHRISTINE')");

        //Done so save In Memory DB to file
        copyMemoryDBToFileDB();
        memorydb.close();
    }

    /**
     * Copy the IN MEMORY DB to file (assumes App's)
     */
    private void copyMemoryDBToFileDB() {
        File db = new File(getDatabasePath(DISKDBNAME).getPath()); //Path to the disk based backup
        // delete the file if it exists and also create directories (i.e. databases directory)
        if (db.exists()) {
            db.delete();
        }
        if (!db.getParentFile().exists()) {
            db.getParentFile().mkdirs();
        }

        //Initialise the file as an SQLite database (for Android so it has android_metadata)
        SQLiteDatabase dbFile = SQLiteDatabase.openDatabase(db.getPath(), null, SQLiteDatabase.CREATE_IF_NECESSARY);
        dbFile.setForeignKeyConstraintsEnabled(false); //<<<<<<<<<< not needed
        dbFile.close(); //<<<<<<<<<< close the database so it will be initialised but empty

        // Do everything in a single transaction
        memorydb.beginTransaction();

        // Attach the disk dataabse
        memorydb.execSQL("ATTACH DATABASE '" + db.getPath() + "' AS " + ATTACH_DBFILE_SCHEMA);

        //Get the tables in the memory DB from the sqlite_master table
        Cursor csr = memorydb.query(
                ATTACH_MASTER_SCHEMA + "." + SQLITE_MASTER_TABLE,
                null,
                SQLITE_MASTER_TYPE_COLUMN + "=?",
                new String[]{SQLITE_MASTER_TYPE_TABLE},
                null, null, null
        );
        //Iterate through the tables
        while (csr.moveToNext()) {

            //get the current table and the generate the table to save to in the disk based DB
            String currentTable = csr.getString(csr.getColumnIndex("name"));
            String dbfileTable = ATTACH_DBFILE_SCHEMA + ATTACH_SCHEMA_SEPERATOR + currentTable;

            //Skip system tables (probably ok to skip) and also android_metadata
            if (currentTable.startsWith(SQLITE_SYSTEMDB_PREFIX) || currentTable.equals(ANDROID_METADATA))
                continue;

            //If not a skipped table then create it on disk db, using schema (as attached) and then load it
            String crtSQL = csr.getString(csr.getColumnIndex(SQLITE_MASTER_SQL_COLUMN))
                    .replaceFirst(currentTable, dbfileTable).replaceFirst("CREATE TABLE ", "CREATE TABLE IF NOT EXISTS ");

            memorydb.execSQL(crtSQL);
            memorydb.execSQL("INSERT INTO " + dbfileTable + " SELECT * FROM " + currentTable);
        }
        csr.close();

        // All done so end the transaction (commit changes)
        memorydb.setTransactionSuccessful();
        memorydb.endTransaction();
        // No longer need to have the database attached and detach it
        memorydb.execSQL("DETACH DATABASE '" + ATTACH_DBFILE_SCHEMA + "'");
    }

    /**
     * If a disk database exists then load it into the IN MEMORY DB, else create the In MEMORY DB with tables
     */
    private void loadOrCreateMemoryDB() {

        // Get the File of the disck based DB
        File dbFile = new File(getDatabasePath(DISKDBNAME).getPath());

        // Create the In MEMORY DB
        memorydb = SQLiteDatabase.create(null);

        //If the disk based db exists copy the tables to the In MEMEORY DB
        if (dbFile.exists()) {

            // Attach the disk based db
            memorydb.execSQL("ATTACH DATABASE '" + dbFile.getPath() + "' AS " +ATTACH_DBFILE_SCHEMA);

            // Do everything in a single transaction
            memorydb.beginTransaction();

            // Get the tables in the disk based DB
            Cursor csr = memorydb.query(
                    ATTACH_DBFILE_SCHEMA + ATTACH_SCHEMA_SEPERATOR + SQLITE_MASTER_TABLE,
                    null,
                    SQLITE_MASTER_TYPE_COLUMN +"=?",
                    new String[]{SQLITE_MASTER_TYPE_TABLE},
                    null,null,null
            );

            // Iterate through the tables
            while (csr.moveToNext()) {
                //get the current table tfor the IN MEMORY DB and the table on disk (append Schema)
                String currentTable = csr.getString(csr.getColumnIndex(SQLITE_MASTER_NAME_COLUMN));
                String dbFileTable = ATTACH_DBFILE_SCHEMA + ATTACH_SCHEMA_SEPERATOR + currentTable;

                // Skip system tables and also android_metadata
                if (currentTable.startsWith(SQLITE_SYSTEMDB_PREFIX) || currentTable.equals(ANDROID_METADATA))
                    continue;

                // Otherwise create the table based upon the SQL from sqlite_master (should not need IF NOT EXISTS)
                memorydb.execSQL(csr.getString(csr.getColumnIndex(SQLITE_MASTER_SQL_COLUMN)));
                // Load the data extracted from the disk based table
                memorydb.execSQL("INSERT INTO main." + currentTable + " SELECT * FROM " + dbFileTable);
            }
            // Done with the Cursor
            csr.close();
            // All done so end the transaction (commit changes)
            memorydb.setTransactionSuccessful();
            memorydb.endTransaction();
            // detach the disk based database
            memorydb.execSQL("DETACH DATABASE '" + ATTACH_DBFILE_SCHEMA + "'");
        } else {
            // No datbase file so just create tables (as required)
            memorydb.execSQL("CREATE TABLE tablex (_id INTEGER PRIMARY KEY, name TEXT)");
            memorydb.execSQL("CREATE TABLE tabley (_id INTEGER PRIMARY KEY, name TEXT)");
            memorydb.execSQL("CREATE TABLE tablez (_id INTEGER PRIMARY KEY, name TEXT)");
        }
    }
}

Результат

  • снимок экрана с Navicat, после 3 прогонов (таким образом, 9 строк в каждой таблице, показаны таблицы)

enter image description here

...