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

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

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

Я фактически копирую базу данных из папки с ресурсами в папку данных, а не заново создавая схему.

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

Это фрагмент кода из источника 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);

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

Ответ 2

Документация говорит:

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

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

Ответ 3

Я выяснил эту проблему. Эта проблема возникает только при копировании db из активов. У db в папке с активами есть версия по умолчанию 0. Поэтому, когда вы вызываете вспомогательный помощник базы данных с номером версии (скажем 1), а затем перезаписываете ее с именем в активах, номер версии reset равен 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();
}

`

Итак, это то, что я сделал, чтобы преодолеть эту проблему. Его нет, где идеальное решение, но оно работает. В основном вам нужно обновить версию db, прежде чем вы вызовете супер метод класса 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: Я знаю, что ответ задерживается, но может помочь кому-то вроде меня, который искал это конкретное решение.