#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.