Почему onUpgrade() не вызывается в базе данных Android sqlite?

#android #sqlite

#Android #sqlite

Вопрос:

Я хочу обновить свою базу данных, когда она будет установлена на мой эмулятор Android. Я установил версию db в моем DBHelper, которая наследуется от SQLiteOpenHelper на 1.

Однако, когда загружается мое первое действие, я создаю экземпляр своего DBHelper, который, как я ожидаю, SQLiteOpenHelper вызовет onUpgrade, поскольку версия db теперь новее. Однако он никогда не вызывается. Мне интересно, есть ли что-то, чего мне не хватает. Где хранится версия, которую использует DBHelper для сравнения с новой версией? Почему это не работает?

На самом деле я копирую базу данных из папки assets в папку data, а не заново создаю схему.

 public class DbHelper extends SQLiteOpenHelper {
    private static final String TAG = "DbHelper";

    static final String DB_NAME = "caddata.sqlite";
    static final int DB_VERSION = 4;

    private static String DB_PATH = "";
    private Context myContext;
    private SQLiteDatabase myDataBase;

    public DbHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
        this.myContext = context;

        DB_PATH = "/data/data/"
                  context.getApplicationContext().getPackageName()
                  "/databases/";            
    }

    public DbHelper open() throws SQLException {        
        myDataBase =  getWritableDatabase();

        Log.d(TAG, "DbHelper Opening Version: "    this.myDataBase.getVersion());
        return this;
    }

    @Override
    public synchronized void close() {

        if (myDataBase != null)
            myDataBase.close();

        super.close();

    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        Log.d(TAG, "onCreate called");

        try {           
            createDataBase();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if ( newVersion > oldVersion)
        {
            Log.d(TAG, "New database version exists for upgrade.");         
            try {
                Log.d(TAG, "Copying database...");
                copyDataBase();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }       
        }
    }

    public void createDataBase() throws IOException {

        boolean dbExist = checkDataBase();

        if (!dbExist) {         

            try {
                copyDataBase();
            } catch (IOException e) {
                throw new Error("Error copying database");
            }
        }

        openDataBaseForRead();
    }


    private boolean checkDataBase() {

        SQLiteDatabase checkDB = null;

        try {
            String myPath = DB_PATH   DB_NAME;
            checkDB = SQLiteDatabase.openDatabase(myPath, null,
                    SQLiteDatabase.OPEN_READONLY
                            | SQLiteDatabase.NO_LOCALIZED_COLLATORS);
            Log.d(TAG, "db exists");
        } catch (SQLiteException e) {
            // database does't exist yet.
            Log.d(TAG, "db doesn't exist");

        }

        if (checkDB != null) {
            checkDB.close();            
        }

        return checkDB != null ? true : false;
    }


    private void copyDataBase() throws IOException {

        // Open your local db as the input stream
        InputStream myInput = myContext.getAssets().open(DB_NAME);

        // Path to the just created empty db
        String outFileName = DB_PATH   DB_NAME;

        // Open the empty db as the output stream
        OutputStream myOutput = new FileOutputStream(outFileName);

        // transfer bytes from the inputfile to the outputfile
        byte[] buffer = new byte[2048];
        int length;
        while ((length = myInput.read(buffer)) > 0) {
            myOutput.write(buffer, 0, length);
        }

        // Close the streams
        myOutput.flush();
        myOutput.close();
        myInput.close();

        myDataBase.setVersion(DB_VERSION);
    }

    public void openDataBaseForRead() throws SQLException {

        // Open the database
        String myPath = DB_PATH   DB_NAME;      
        myDataBase = SQLiteDatabase.openDatabase(myPath, null,  SQLiteDatabase.OPEN_READONLY);
    }

    public void openDataBaseForWrite() throws SQLException {

        // Open the database
        String myPath = DB_PATH   DB_NAME;      
        myDataBase = SQLiteDatabase.openDatabase(myPath, null,  SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.NO_LOCALIZED_COLLATORS );
    }


}
  

Комментарии:

1. Было бы здорово, если бы вы показали код для увеличения версии базы данных.

2. Увеличение версии базы данных? Я думал, что Android справился с этим за вас.

3. Как увеличивается версия БД?

4. @jaffa в sqlite для каждой базы данных есть PRAGMA user_version. android сравнивает это со значением, которое вы предоставляете конструктору SQLiteOpenHelper. если он находит вашу версию> user_version, он вызывает onUpgrade() и присваивает ПРАГМЕ user_version новое значение, иначе он вызывает onCreate() . итак, все, что вам нужно сделать, это увеличить версию, которую вы предоставляете конструктору SQLiteOpenHelper, и реализовать метод onUpgrade() для изменения базы данных до требуемого нового состояния.

Ответ №1:

Это фрагмент кода из источника SQLiteOpenHelper.getWritableDatabase() :

 int version = db.getVersion();
if (version != mNewVersion) {
    db.beginTransaction();
    try {
        if (version == 0) {
            onCreate(db);
        } else {
            if (version > mNewVersion) {
                onDowngrade(db, version, mNewVersion);
            } else {
                onUpgrade(db, version, mNewVersion);
            }
        }
        db.setVersion(mNewVersion);
        db.setTransactionSuccessful();
    } finally {
        db.endTransaction();
    }
}

onOpen(db);
  

Как вы можете видеть, onCreate() or onUpgrade() вызываются в вызове to getWritableDatabase() .
Вы должны использовать эти вызовы всякий раз, когда вам нужен экземпляр SQLiteDatabase . Вы не должны использовать свои собственные методы, за исключением случаев, когда они являются оболочками вокруг метода getWritableDatabase or getReadableDatabase .

Комментарии:

1. Означает ли это onUpgrade() , что вызывается каждый раз, когда пользователь вызывает getWritableDatabase() ?

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

3. Экономия времени! Когда мне нужно было запустить обновление базы данных, работает простое получение базы данных WRITEABLEDATABASE с помощью описанного выше метода.

4. Версия, на которую я смотрю прямо сейчас, вызывает этот код независимо от параметра «доступный для записи». Итак, методы onCreate, onUpgrade и onDowngrade выполняются всякий раз, когда вы извлекаете экземпляр базы данных, в зависимости от текущей версии базы данных. Чтобы вызвать это, вам не нужно вызывать getWritableDatabase(), но вы также можете вызвать getReadableDatabase() .

5. @AlenSiljak прав в этом. Я просто хотел дать простой, не вводящий в заблуждение ответ. Я обновлю ответ.

Ответ №2:

В документации говорится:

База данных фактически не создается и не открывается до тех пор, пока не будет вызван один из getWritableDatabase() или getReadableDatabase() .

Я вижу, что вы внедрили свои собственные методы для получения базы данных: openDataBaseForRead() и openDataBaseForWrite() . Это плохо 😉

Комментарии:

1. Почему это плохо? Я думаю, что использовал их для тестирования.

2. Это было здорово. Мое приложение использовалось для обновления, когда я его запускал, но, по-видимому, в моем лаунчере больше нет вызовов БД, поэтому я рвал на себе волосы, пытаясь выяснить, почему оно не обновляется — пришлось перейти к действию, которое вызывало БД!

Ответ №3:

Я разобрался с проблемой. Эта проблема возникает только при копировании базы данных из ресурсов. БД в папке assets имеет версию по умолчанию 0. Поэтому, когда вы вызываете помощник по открытию базы данных с номером версии (скажем, 1), а затем перезаписываете его тем, что находится в активах, номер версии сбрасывается на 0.

И когда вы вызываете db.getReadableDatabase() или db.getWriteableDatabase, который должен вызывать метод onUpgrade, он завершается с ошибкой, потому что номер версии 0, который должен быть новой базой данных. вы можете посмотреть исходный код SQLiteOpenHelper. Он запускает метод onUpgrade только тогда, когда версия больше нуля, а текущая версия больше старой.

`

 db.beginTransaction();
try {
  //Skips updating in your case
  if (version == 0) {
  onCreate(db);
  } else {
  if (version > mNewVersion) {
    onDowngrade(db, version, mNewVersion);
  } else {
   onUpgrade(db, version, mNewVersion);
  }
}
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
} finally {
   db.endTransaction();
}
  

`

Итак, это то, что я сделал, чтобы преодолеть эту проблему. Это не идеальное решение, но оно работает. По сути, вам нужно обновить версию БД перед вызовом метода super класса SQLiteOpenHelper (конструктора вашего менеджера базы данных).

`

 try
 {
   String myPath = MyApplication.context.getDatabasePath(DATABASE_NAME).toString();
   //open a database directly without Sqliteopenhelper 
   SQLiteDatabase myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.NO_LOCALIZED_COLLATORS);
   //if the version is default 0 the update the version
   if (myDataBase.getVersion() == 0)
    {
     //update the database version to the previous one
     myDataBase.execSQL("PRAGMA user_version = "   1);
    }
    //Close DataBase 
    myDataBase.close();
  }
   catch (Exception e)
   {
     //Do Nothing a fresh install happened
   }
  

`

P.S: Я знаю, что ответ запоздал, но это может помочь кому-то вроде меня, кто искал это конкретное решение

Комментарии:

1. Умная идея. Я нахожусь в той же ситуации, что и это, и это мне очень помогает. Приветствия

2. вы могли бы заархивировать это проще, вызвав onUpgrad из onCreate и обработав все в onUpgrade также вы можете вызвать pragma в OnConfigure согласно документации, этот метод должен вызывать только методы, которые настраивают параметры … или выполняют операторы PRAGMA

Ответ №4:

В моем случае версия базы данных в папке ресурсов и текущая версия базы данных в коде одинаковы, позже я увеличил текущую версию базы данных в коде, затем вызывается метод onUpgrade.