Database onUpgrade from Assts Folder - android

When my app is created, my database is copied from the assets folder to my app.
Now if i want to do an onUpgrade, i just want to overwrite all Tables EXCEPT of one.
But how can i do this?
I just can write the whole database or nothing...
Please help me with this.
This doesnt work of course, because it doesnt overwrite the existing tables, it just backups the one i do not replace..
public void createDataBase() throws IOException{
boolean dbExist = checkDataBase();
if(dbExist){
myDataBase = this.getWritableDatabase();
} else {
myDataBase = this.getReadableDatabase();
try {
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
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[1024];
int length;
while ((length = myInput.read(buffer))>0){
myOutput.write(buffer, 0, length);
}
//Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.i("onUpgrade", "newVersion: "+newVersion+", oldVersion: "+oldVersion);
try{
if(newVersion > oldVersion){
db.execSQL("ALTER TABLE Results RENAME TO temp_Results");
db.execSQL("CREATE TABLE Results (lektionenId INTEGER PRIMARY KEY, testPassed Integer, lektionPassed Integer)");
db.execSQL("INSERT INTO Results (testPassed, lektionPassed) SELECT testPassed, lektionPassed FROM temp_Results");
db.execSQL("DROP TABLE IF EXISTS temp_Results");
}
} catch(Exception e){
Log.i("exc", ""+e);
}
}
EDIT:
Where i call the Database:
myDbHelper = new LernAppOpenHelper(this, "LernApp", DbConfig.DB_VERSION_LERNAPP);
try {
String[] columns = {"_id","description"};
myDbHelper.createDataBase();
myDbHelper.openDataBase();
cursor = myDbHelper.getQuery("Uebersicht", columns, null, null, null, null, null);
And my onUpgrade Method:
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if(newVersion > oldVersion){
if("LernApp".equals(DATABASE_NAME)){
myContext.deleteDatabase(DATABASE_NAME);
try {
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
}

Step #1: Copy your table data from the old database into memory.
Step #2: Restore your database from your copy in assets.
Step #3: Update your new database from the table data in memory.
If the table might be too big for that, copy the data to a file and then back from a file.
EDIT
Or:
Step #1: Copy your old database (the one being replaced) to a new database using standard Java file I/O.
Step #2: Restore your database from your copy in assets.
Step #3: Open both databases.
Step #4: Load the table you are keeping from the old database and pour its contents into the new database. Be sure to watch your transactions: the default of one-transaction-per-statement can make this sort of work very slow.
Step #5: When done, close the old database and delete it.

Related

Android Attach database onupgrade locks database

On upgrade method i'm renaming my database and copying new database from assets folder.
But on Attach statements it throws an exception on "database locked (code) 5"
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if(oldVersion<newVersion){
db.close();
/**
* Renaming Database from Databse path
*
*/
new File(Constants.DATABASE_PATH+Constants.DATABSE_NAME).renameTo(new File(Constants.DATABASE_PATH+Constants.DATABASE_NAME_RENAME));
boolean mm = checkDataBase(Constants.DATABSE_NAME);
boolean up = checkDataBase(Constants.DATABASE_NAME_RENAME);
try {
copyDataBase();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
boolean dbmm = checkDataBase(Constants.DATABSE_NAME);
boolean dbup = checkDataBase(Constants.DATABASE_NAME_RENAME);
try{
String path = Constants.DATABASE_PATH+Constants.DATABSE_NAME;
db = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READWRITE);
File dbFile=myContext.getDatabasePath(Constants.DATABSE_NAME);
At the line below it throws database lock (code 5) expection.
db.execSQL(String.format("ATTACH DATABASE '%s' AS BACKUP;",
dbFile,null));
db.execSQL("INSERT INTO "+Constants.COMPANY_TABLE_NAME+" SELECT * FROM BACKUP."+Constants.COMPANY_TABLE_NAME);
db.execSQL("INSERT INTO "+Constants.PAGE_TABLE_NAME+" SELECT * FROM BACKUP."+Constants.PAGE_TABLE_NAME);
db.execSQL("INSERT INTO "+Constants.BOOKMARK_TABLE_NAME+" SELECT * FROM BACKUP."+Constants.BOOKMARK_TABLE_NAME);
db.execSQL("INSERT INTO "+Constants.CATALOG_TABLE_NAME+" SELECT * FROM BACKUP."+Constants.CATALOG_TABLE_NAME);
db.execSQL("INSERT INTO "+Constants.Order_Items_TABLE_NAME+" SELECT * FROM BACKUP."+Constants.Order_Items_TABLE_NAME);
db.execSQL("INSERT INTO "+Constants.Order_TABLE_NAME+" SELECT * FROM BACKUP."+Constants.Order_TABLE_NAME);
db.execSQL(String.format("DETACH DATABASE '%s' ;",
"BACKUP",null));
db.close();
}catch(Exception e){
Log.e("Db", e+"");
}
}
}
onUpgrade() (and onCreate()) are run inside a transaction; these methods must not do anything besides executing SQL statements inside that database.
If you are not interested in the contents of the old database, don't bother updating it. Just use a different file name for the new database, and delete the old file whenever you want.
If you are interested in the old data, you can attach and copy it after copying the new file.
(And consider using SQLiteAssetHelper.)
db.close();
doesn't need to close db manually.

Updating SQLite database when releasing new version of the android app on the store

I'm using sqlite in my android app, my database file is called data.db. Let's suppose that I have two table in it one is having static values(read only) and second is saves dynamic values and pushed the app to the playStore.
In the next version I updated the data.db and I updated the values in first table, added new columns in second table and added third new table and push the app to the PlayStore. So how I can check if user is updating the app and what is best possible way to save existing data and how I can update data.db programmatically when it is a update not a fresh install?
You can use SQLiteOpenHelper's onUpgrade method. In the onUpgrade method, you get the oldVersion as one of the parameters.
In the onUpgrade use Switch case and in each of the cases use the version number to keep track of the current version of Database that was sent for each new Version of Database.
Its best that you loop over from oldVersion to newVersion, incrementing version by 1 at a time and then upgrade the database stepbystep. This is very helpful when someone with Database version 1 upgrades the app after a long time, to a version using database version 7 and the app starts crashing because of certain incompatible changes.
Then the updates in database will be done stepwise, covering all possible cases i.e incorporating the changes in the database done for each new version and thereby preventing your application from Crashing.
There are two ways
1] Changed database version so that when user updates an app SQLiteOpenHelper's onUpgrade() method gets executed in this method you can write a code to drop tables n create new tables according to new schema and send network calls to fetch data.
2] Push app with pre-installed db.
Create database with db version and save it in assets folder.
Whenever app runs compare versions of assets's db & app's db.
if asset's db version is higher than app's db then this means you need to update database.
Directly copy Assete's db into folder structor and sends delta call to fetch data.
In this example you have add a new column so the changes in this case should be like this (here you gonna not lose your data).
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
String sql = "ALTER TABLE " + TABLE_SECRET + " ADD COLUMN " +
"name_of_column_to_be_added" + " INTEGER";
db.execSQL(sql);
}
I advice you to use http://www.activeandroid.com/ library. It supports migrations. You just need to write [n].sql files in assets/migrations folder for n version of you database model. Active android carries about existing version and applies required migration.
private static String DB_PATH = Environment.getDataDirectory()+"/data/package-name/databases/";
private static String DB_NAME = "name_of_the_database"; // student.db
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 d inb
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[1024];
int length;
while ((length = myInput.read(buffer)) > 0)
{
myOutput.write(buffer, 0, length);
}
// Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
//Copy successful
outFileName = DB_PATH + DB_SUCCESS;
myOutput = new FileOutputStream(outFileName);
myOutput.write(1);
myOutput.flush();
myOutput.close();
}
package com.example.sqllite_db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
public class DatabseAdapter extends SQLiteOpenHelper
{
public static String DatabaseName="NewDatabase";
public static int DatabaseVersion=1;
public DatabseAdapter(Context context)
{
super(context,DatabaseName,null,DatabaseVersion);
// TODO Auto-generated constructor stub
}
/*public static String TableUser="tb1";
public static String keyId="id";
public static String keyName="name";
public static String keyPass="pass";*/
#Override
public void onCreate(SQLiteDatabase db)
{
// TODO Auto-generated method stub
String createQuery="create table tb1(name text,pass text);";
db.execSQL(createQuery);
//String createQuery1="create table tb2(pass text);";//("+keyName+"text,"+keyPass+"text);";
//db.execSQL(createQuery1);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
// TODO Auto-generated method stub
db.execSQL("drop table tb1");
onCreate(db);
//db.execSQL("drop table tb2");
//onCreate(db);
}
}
drop the database in on Upgrade method then again call on Create method in it and your database will update when you upload new database.

no such table exception SQLite

My code is now as follows:
Main app java:
String TAG = "DATABASES";
try {
String destPath = "/data/data/" + getPackageName()
+ "/databases/data.db";
File f = new File(destPath);
if(!f.exists()){
Log.v(TAG,"Dest DB doesn't exist");
InputStream in = getAssets().open("airports.db");
OutputStream out = new FileOutputStream(destPath);
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
in.close();
out.close();
} else {
Log.v(TAG,"File exists: " + destPath);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
Log.v(TAG,"ioexeption");
e.printStackTrace();
}
DBManager dbManager = new DBManager(this);
Log.v(TAG,"Database is there with version: "+dbManager.getReadableDatabase().getVersion());
//String sql = "select * from airports where IATA='GRO' ";
String sql = "select count(*) from airports";
SQLiteDatabase db = dbManager.getReadableDatabase();
Cursor cursor = db.rawQuery(sql, null);
Log.v(TAG,"Query Result:"+cursor);
cursor.close();
db.close();
dbManager.close();
My DBManager.java:
package com.jammo.mywidget4;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class DBManager extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
private static final String TAG = "DATABASES";
public DBManager(Context context) {
super(context, "data.db", null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
Log.v(TAG,"On create Called:"+db.getPath());
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
Now the execution runs ok in the main.java on initiation of "db", however fails on the next line where it tries to execute rawQuery()
FYI "select count(*) from airports" run on my Sqlite DB Manager GUI returns 1650 rows.
Error log is saying :
03-04 21:54:24.200: E/AndroidRuntime(11513): FATAL EXCEPTION: main
03-04 21:54:24.200: E/AndroidRuntime(11513): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.jammo.mywidget4/com.jammo.mywidget4.SectorInfo}: android.database.sqlite.SQLiteException: no such table: airports: , while compiling: select count(*) from airports
Local /assets/airports.db "seems" to be being detected and copied to /data/data/mypackage/databases/data.db as "file is found"
Many thanks
J
Try it without using your DBManager class.
Comment out this line...
DBManager dbManager = new DBManager(this);
...then open the database explicitly by replacing this line...
SQLiteDatabase db = dbManager.getReadableDatabase();
...with...
SQLiteDatabase db = SQLiteDatabase.openDatabase(destPath, null, SQLiteDatabase.OPEN_READONLY);
Then clear the app data so the database is re-copied from your assets directory.
EDIT: Even though my suggestion worked, it's actually just a workaround and the root of your problem was this...
String destPath = "/data/data/" + getPackageName() + "/databases/data.db";
You should avoid using hard-coded paths with Android - they may work fine for many devices but there is no guarantee they'll work with all devices or even with all versions of Android.
The problem was you were creating / copying your database to that path but the DBManager class was presumably creating an empty database located somewhere on a different path in the file-system.
To fix this for all devices and Android versions, before copying the database, I would use...
SQLiteDatabase.openOrCreateDatabase("data.db", null)
...to create a 'dummy' (empty) database. Then I'd call...
getDatabasePath("data.db")
...to get the absolute path to data.db. You can then overwrite that database during the copy operation and from then on you can use a class which extends SQLiteOpenHelper and it will find the correct database.

Replace sql database but keep old records

After a lot of work I managed to build my first app, but I stick with one question. For my app I am using a sql database.. Suppose I want to add 30 records to a certain table. How is it possible that when I put a new version in the android market with a new sql table to use this one for the future, but to keep the records of the previous database?
Does it has to do something with:
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
EDIT
my databasehelper code:
public class DataBaseHelper extends SQLiteOpenHelper {
private static String DB_PATH = "/data/data/com.test.com/databases/";
private static String DB_NAME = "quizDb";
private SQLiteDatabase myDataBase;
private final Context myContext;
private Cursor c;
static int numberOfLevels = 10;
private final static int DB_VERSION = 2; // = until level 10
/**
* Constructor Takes and keeps a reference of the passed context in order to
* access to the application assets and resources.
*
* #param context
*/
public DataBaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
this.myContext = context;
}
/**
* Creates a empty database on the system and rewrites it with your own
* database.
* */
public void createDataBase() throws IOException {
boolean dbExist = checkDataBase();
if (!dbExist) {
// By calling this method and empty database will be created into
// the default system path
// of your application so we are gonna be able to overwrite that
// database with our database.
this.getReadableDatabase();
try {
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
/**
* Check if the database already exist to avoid re-copying the file each
* time you open the application.
*
* #return true if it exists, false if it doesn't
*/
private boolean checkDataBase() {
File dbFile = new File(DB_PATH + DB_NAME);
return dbFile.exists();
}
/**
* Copies your database from your local assets-folder to the just created
* empty database in the system folder, from where it can be accessed and
* handled. This is done by transfering bytestream.
* */
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[1024];
int length;
while ((length = myInput.read(buffer)) > 0) {
myOutput.write(buffer, 0, length);
}
// Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
}
public void openDataBase() throws SQLException {
// Open the database
String myPath = DB_PATH + DB_NAME;
myDataBase = SQLiteDatabase.openDatabase(myPath, null,
SQLiteDatabase.OPEN_READONLY);
}
#Override
public synchronized void close() {
if (c != null)
c.close();
if (myDataBase != null)
myDataBase.close();
super.close();
}
#Override
public void onCreate(SQLiteDatabase db) {
}
public File getDatabasePath(String name) {
File file = myContext.getDatabasePath(name);
return file;
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("ATTACH DATABASE ? as AttachedDB",
new String[] { getDatabasePath("quizDbNew").getPath() });
db.execSQL("INSERT OR IGNORE INTO questions (_id, file, answer, level) SELECT _id, file, answer, level FROM AttachedDB.questions");
db.execSQL("DETACH AttachedDB");
}
The concept of using "DROP TABLE" in onUpgrade() is as primitive as database management gets, however more useful techniques require more SQL savvy. A smarter way to upgrade your databases by using "ALTER TABLE" to add new columns or otherwise finagle the old data into your new schema.
Addition
Below in the comments you stated (more or less):
I want to copy the content from my backup file of Db v1 into my current Db v2
So let's set up a couple hypothetical tables:
Database Version One (DBv1):
CREATE TABLE Foo(_id INTEGER PRIMARY KEY, bar TEXT, bar2 TEXT, bar3 TEXT);
Database Version Two (DBv2):
CREATE TABLE Foo(_id INTEGER PRIMARY KEY, bar2 TEXT, bar4 INTEGER);
First let's see a regular upgrade from DBv1 to DBv2. SQLite only supports ADD COLUMN and RENAME TO, not REMOVE COLUMN or anythings else. So we have to re-create the entire table:
#Override // DBv1 => DBv2
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("ALTER TABLE Foo RENAME TO OldFoo");
db.execSQL("CREATE TABLE Foo(_id INTEGER PRIMARY KEY, bar2 TEXT, bar4 INTEGER)");
db.execSQL("INSERT INTO Foo (_id, bar) SELECT _id, bar2 FROM OldFoo");
db.execSQL("DROP TABLE OldFoo");
}
Again this created a table with DBv2's schema and kept all of the valid, existing data from DBv1 by inserting the appropriate columns into DBv2. (Then it removed the old data by dropping the old table.)
You have wisely chosen to backup your database over time in a separate file, but now you want to bring the old data into the new table schema. To start make sure that your backup SQLite file is in the same directory as your current SQLite file (data/data/<reverse.package.name>/databases/). It will obviously need a unique name, let's call it DBBackup. Now let's attach DBBackup to your current database and perform a similar action from above:
// DBBackupv1 => DBv2
public void restore(SQLiteDatabase db) {
db.execSQL("ATTACH DATABASE ? as AttachedDB", new String[] {getDatabasePath("DBBackup").getPath()});
db.execSQL("INSERT OR IGNORE INTO Foo (_id, bar2) SELECT _id, bar2 FROM AttachedDB.Foo");
db.execSQL("DETACH AttachedDB");
}
I used INSERT OR IGNORE to restore any rows that were deleted but left the current existing rows untouched. You can use INSERT OR REPLACE to revert to the backed up version. There are many more options to suit your needs.

How do i upgrade my pre-populated sqlite database on my device without re-creating tables?

I have my code below. It correctly reads my sqlite database file(that i have already created using the SQLite Database Browser) in my assets folder - moves it to the /data/data/packagename/databases/ path on my device then i am able to use a query and cursor to get my information and it works great. Code here:
public class DatabaseHelper extends SQLiteOpenHelper {
private Context myDbContext;
private static String dbName = "restaurant.db";
private static String outfilePath = "/data/data/dev.mypackage.com/databases/";
private static String path = outfilePath + dbName;
private static SQLiteDatabase db;
public DatabaseHelper(Context context){
super(context, dbName, null, 2);
this.myDbContext = context;
db = openDb();
String s = "select * from menu";
Cursor c = db.rawQuery(s, null);
Log.e("DB Constructor Row count", String.valueOf(c.getCount()).toString());
while(c.moveToNext()){
Log.e("DB Constructor", c.getString(c.getColumnIndex("category")));
Log.e("DB Constructor", c.getString(c.getColumnIndex("menuItem_id")));
Log.e("DB Constructor", c.getString(c.getColumnIndex("title")));
Log.e("DB Constructor", c.getString(c.getColumnIndex("desc")));
Log.e("DB Constructor", c.getString(c.getColumnIndex("price")));
Log.e("DB Constructor", c.getString(c.getColumnIndex("icon")));
}
c.deactivate();
c.close();
}
private void copyDataBase(File dbFile) throws IOException {
try{
InputStream dbStream = myDbContext.getAssets().open(dbName);
OutputStream newDbFile = new FileOutputStream(outfilePath + dbName);
byte[] buffer = new byte[1024];
int length;
while((length = dbStream.read(buffer)) > 0){
newDbFile.write(buffer);
}
newDbFile.flush();
newDbFile.close();
dbStream.close();
}
catch(IOException e){
throw new IOException("trying to copy the database - ERROR: " + e.getMessage());
}
}
private SQLiteDatabase openDb() throws SQLiteException{
File dbFile = myDbContext.getDatabasePath(dbName);
if(!dbFile.exists()){
Log.e("openDb", "file does not exist");
try {
copyDataBase(dbFile);
} catch (IOException e) {
throw new RuntimeException("Error creating source database", e);
}
}
return SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
}
public void loadRestaurantInfo(){
}
#Override
public void onCreate(SQLiteDatabase db){
// TODO Auto-generated method stub
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
Now i had to go back and add a row of information to one of the tables(in the database in the assets folder), using the SQLite Brower, - but that is not being reflected in my cursor output - I understand why this is happening - because if(!dbFile.exists()) fails so there is no copying to be done. So my question is - is what code do i need to add to make this work? I never created the tables with code so i dont see how useful the onUpgrade method is going to be for me.
You have three options:
Use onUpgrade() (preferred)
Delete the existing database and copy the new one (not a good idea)
Copy the data in the existing database, delete the existing database, copy the new database, insert data from old database into new database (too much work when the new database schema can be upgraded in onUpgrade).
So, to answer your question, you upgrade your database in onUpgrade() without having to recreate any tables.
On the other hand, if you just added a new row to a particular table, the database schema has not changed and you can just insert the new row at runtime... of course, not knowing what your application's purpose is this may not be a good idea as you can easily lose track of changes to your "static" database.
The "right" way to do things is quite different from how you've set out. Rather than go there, I'll assume you want to keep your current method of creating your database and I'll offer a suggestion to work with it. Add a table to your database which has a single row of meta data, which will include the database version (as well as anything else you like). If the database file already exists, open it and check the version. If the version is old, close it and replace it.

Categories

Resources