Как и где закрыть () мою базу данных, чтобы остановить получение E / SQLiteLog: (283) - PullRequest
0 голосов
/ 20 октября 2019

Когда я открываю файл базы данных в браузере БД, он пуст. И я получаю этот E / SQLiteLog: (283) восстановил 16 кадров из файла WAL. Правильно ли использовать Room для сохранения данных и заполнять их обратно, когда приложение закрыто, и наоборот. Буду рад, если кто-то может помочь, спасибо.

/ это мой экземпляр в классе БД /

public static synchronized NoteDataBase getInstance(Context context){

    if (instance == null) {
        instance = Room.databaseBuilder(context.getApplicationContext(),
                NoteDataBase.class , "note_database")
                .fallbackToDestructiveMigration()
                .build();
    }
    return instance;
}
} 
@Override
public void close() {
    super.close();
    instance = null;
}

public void backup(Context context) {
    instance.close();
    //......... backup the file
    getInstance(context);
}

/ и мой репозиторий /

 public NoteRepository(Application application) {
    dataBase = NoteDataBase.getInstance(application);
    noteDao = dataBase.noteDao();
    allNotes = noteDao.getAllNotes();

}

/ некоторые из моей модели представления/

public class NoteViewModel extends AndroidViewModel  {

public NoteViewModel(@NonNull Application application)  {
    super(application);
    repository = new NoteRepository(application);
    allNotes = repository.getAllNotes();
}

public void insert(Note note) {
    repository.insert(note);
}

}

/ И мой фрагмент, где я сохраняю некоторые результаты /

private void saveNote(){

    String savedscore = finalScore.getText().toString();
    ArrayList<String> daycheks =  new ArrayList<>();
    for (int i = 0; i < itemList.size(); i++) {
        daycheks.add(itemList.get(i).toString());
    }

    Note note = new Note(savedscore , daycheks);
    model.insert(note);

    Toast.makeText(getContext() , "Result saved" ,Toast.LENGTH_SHORT).show();

}

1 Ответ

0 голосов
/ 20 октября 2019

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

  • файлы -wal / -shm или -journal имеют одинаковые именав качестве файла базы данных с суффиксом соответствующего расширения.

Другой вариант - открыть базу данных в режиме журнала, используя setJournalMode .

  • Разница между WAL и режимом журнала составляет

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

    • Хотя в режиме журнала база данных изменяется, и эти изменения записываются в файл -journal , откат отменяет изменения в соответствии с файлом журнала.

    • См. Запись с записью впереди для получения более полной информации.

Как видно, еслиВы копируете базу данных без контрольной точки и без файла -wal , поэтому данные могут отсутствовать.

Пример

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

Класс БД: -

@androidx.room.Database(entities = MyTable.class,version = 1)
public abstract class NoteDataBase extends RoomDatabase {

    private static String DBNAME = "mydb";
    static NoteDataBase instance;

    abstract MyTableDao getMyTableDao();
    public static synchronized NoteDataBase getInstance(Context context) {
        if (instance == null) {
            instance = Room.databaseBuilder(context, NoteDataBase.class,DBNAME)
                    .fallbackToDestructiveMigration()
                    .allowMainThreadQueries()
                    .build();
        }
        return instance;
    }

    @Override
    public void close() {
        super.close();
        instance = null;
    }

    public void backup(Context context) {
        instance.close();
        //......... backup the file
        getInstance(context);
    }
}
  • , т. Е. В основном ваш класс, но, что важно, с методом резервного копирования, который закрывает базу данныхи затем устанавливает переменную экземпляра в нуль, эффективно форсируя открытие при следующем его использовании.

Деятельность, которая использует выше: -

public class MainActivity extends AppCompatActivity {

    NoteDataBase mDB;
    MyTableDao mMyTableDao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("APPINFO","Retrieving the Database Instance");
        mDB = NoteDataBase.getInstance(this);
        Log.d("APPINFO","Retrieving the Database DAO");
        mMyTableDao = mDB.getMyTableDao();
        Log.d("APPINFO","Inserting Data (10 rows)");
        for (int i=1;i < 11; i++) {
            mMyTableDao.insertMyTableRow(new MyTable("MYVALUE" + i));
        }
        Log.d("APPINFO","Mock backup/close");
        mDB.backup(this);
        Log.d("APPINFO","Retrieveing the Database Instance after the close");
        mDB = NoteDataBase.getInstance(this);
        Log.d("APPINFO","Retrieving Rows from the Database");
        for(MyTable m: mDB.getMyTableDao().getAllMyTableRows()) {
            Log.d("MYTABLEINFO","ID = " + m.getId() + " VALUE = " + m.getValue());
        }
        Log.d("APPINFO","Closing the database");
        mDB.close();
        MyTable m = mDB.getMyTableDao().getMyTableRowById(1L);
        Log.d("MYTABLEINFO","ID = " + m.getId() + " VALUE = " + m.getValue());
    }
}

Результат (Журнал):-

2019-10-21 15:47:58.689 16269-16269/? D/APPINFO: Retrieving the Database Instance
2019-10-21 15:47:58.699 16269-16269/? D/APPINFO: Retrieving the Database DAO
2019-10-21 15:47:58.701 16269-16269/? D/APPINFO: Inserting Data (10 rows)
2019-10-21 15:47:58.743 16269-16269/? D/APPINFO: Mock backup/close
2019-10-21 15:47:58.752 16269-16269/? D/APPINFO: Retrieveing the Database Instance after the close
2019-10-21 15:47:58.752 16269-16269/? D/APPINFO: Retrieving Rows from the Database
2019-10-21 15:47:58.773 16269-16269/? D/MYTABLEINFO: ID = 1 VALUE = MYVALUE1
2019-10-21 15:47:58.773 16269-16269/? D/MYTABLEINFO: ID = 2 VALUE = MYVALUE2
2019-10-21 15:47:58.773 16269-16269/? D/MYTABLEINFO: ID = 3 VALUE = MYVALUE3
2019-10-21 15:47:58.773 16269-16269/? D/MYTABLEINFO: ID = 4 VALUE = MYVALUE4
2019-10-21 15:47:58.773 16269-16269/? D/MYTABLEINFO: ID = 5 VALUE = MYVALUE5
2019-10-21 15:47:58.773 16269-16269/? D/MYTABLEINFO: ID = 6 VALUE = MYVALUE6
2019-10-21 15:47:58.773 16269-16269/? D/MYTABLEINFO: ID = 7 VALUE = MYVALUE7
2019-10-21 15:47:58.773 16269-16269/? D/MYTABLEINFO: ID = 8 VALUE = MYVALUE8
2019-10-21 15:47:58.774 16269-16269/? D/MYTABLEINFO: ID = 9 VALUE = MYVALUE9
2019-10-21 15:47:58.774 16269-16269/? D/MYTABLEINFO: ID = 10 VALUE = MYVALUE10
2019-10-21 15:47:58.774 16269-16269/? D/APPINFO: Closing the database
2019-10-21 15:47:58.784 16269-16269/? E/ROOM: Invalidation tracker is initialized twice :/.
2019-10-21 15:47:58.784 16269-16269/? D/MYTABLEINFO: ID = 1 VALUE = MYVALUE1
  • Обратите внимание на последнюю, кроме 1 строки, это потому, что вместо получения нового экземпляра использовался старый экземпляр (на самом деле mDB = NoteDataBase.getInstance(this); следовало использовать) .

Следует отметить, что в базе данных после выполнения вышеуказанного есть пустой файл -wal : -

enter image description here

  • Поэтому база данных полностью проверена.

Копирование этого файла из проводника устройств и открытие в инструменте SQLite (Navicat) показывает: -

enter image description here

  • т.е. вставленные данные существуют, как и room_master_table итаблица android_metadata

Альтернатива закрытию

Альтернативой закрытию базы данных может быть использование PRAGMA wal_checkpoint (?)

ЕслиКласс БД включал следующие методы: -

public void checkpoint() {
    int attempts = 0;
    int max_attempts = 10;
    Cursor csr;
    SupportSQLiteDatabase ssd  = instance.getOpenHelper().getWritableDatabase();
    if (isWALOn(ssd)) {
        Log.d("CHKPOINTINFO","Attempt " +  (attempts + 1));
        while (checkpoint(ssd) > 0 && attempts++ < max_attempts) { }
    }
}

private boolean isWALOn(SupportSQLiteDatabase db) {
    boolean rv = false;
    Cursor csr = db.query("PRAGMA journal_mode");
    if  (csr.moveToFirst()) {
        if (csr.getString(0).toLowerCase().equals("wal")) rv = true;
    }
    csr.close();
    return rv;
}

private int checkpoint(SupportSQLiteDatabase db) {
    Log.d("CHKPOINTINFO","Attempting Database Ceckpoint");
    int blocked = 0;
    int pages_to_checkpoint = 0;
    int checkpointed_pages = 0;
    Cursor csr = db.query("PRAGMA wal_checkpoint(TRUNCATE)");
    if (csr.moveToFirst()) {
        blocked = csr.getInt(0);
        pages_to_checkpoint = csr.getInt(1);
        checkpointed_pages = csr.getInt(2);
    }
    csr.close();
    Log.d("CHKPOINTINFO",
            "Checkpoint values Blocked = " + blocked +
                    " Pages to Checkpoint = " + pages_to_checkpoint +
                    " Pages Checkpointed = " + checkpointed_pages
    );
    if (blocked > 0) return -1;
    if (checkpointed_pages < pages_to_checkpoint) return 1;
    return 0;
}
  • note Ведение журнала добавлено для тестирования, обычно это будет удалено.

Вместо использования mDB.backup(), mDB.checkpoint() можно использовать. Преимущество этого состоит в том, что база данных остается открытой.

...