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