Best practices for creating a SQLite database on Android - android

I have read through a lot of the posts on copying the database file over from the assets or raw folders to the /data/data/APP/databases folder, but that would leave two copies of the DB on the device taking up valuable space. I am thinking of building a solution with a slightly smaller footprint and allowing for more flexible schema management by storing the database SQL creation text file in the raw folder and using the standard on_create / on_update process in the DBhelper class. However I am a little confused because the examples that copy the database over bypass the on_create and on_update methods.
Is this the recommended way if you are not building the db from strings in the code?
My solution would simulate the running scripts from code method by having the scripts all in one file. The reason I am looking at building the db this way is that my DB will have close to 100 tables when the application is complete, so I need the schema to be manageable.
Any guidance is welcome as I am still learning the best practices and patterns for Android.
Here is an example of my code:
public class DatabaseHelper extends SQLiteOpenHelper {
private final String DATABASE_NAME = "mydb";
private final int DATABASE_VERSION = 1;
private final Context myCtx;
private String DATABASE_CREATE_SCRIPT = null;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
DATABASE_CREATE_SCRIPT = getLoadFile();
// Create all tables and populate the lookup tables
db.execSQL(DATABASE_CREATE_SCRIPT);
db.execSQL(VIEW_CREATE_V_PERSON);
}
private String getLoadFile(){
InputStream inputStream = myCtx.getResources().openRawResource(resIdofmyfile);
InputStreamReader inputreader = new InputStreamReader(inputStream);
BufferedReader buffreader = new BufferedReader(inputreader);
String line;
StringBuilder text = new StringBuilder();
try {
while (( line = buffreader.readLine()) != null) {
text.append(line);
text.append('\n');
}
} catch (IOException e) {
// We have an error to look up
return null;
}
return text.toString();
}
/**
* onUpgrade will check the version of the database and update the database
* if a newer version is available.
*/
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Will set conditional upgrade checks
//Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which may destroy all old data");
db.execSQL("DROP TABLE IF EXISTS CONTEXT_LOAD");
db.execSQL("DROP TABLE IF EXISTS ISSUES");
db.execSQL("DROP TABLE IF EXISTS GOALS");
db.execSQL("DROP TABLE IF EXISTS PERSON");
db.execSQL("DROP VIEW IF EXISTS V_PERSON");
onCreate(db);
}
}

I guess it looks like a good practice. It's not very bad even if you create your database with code completely. Check out google's app for IO schedules it has a fairly big database creation part. It may lead you a way.
Here is the project home page, here is the database part
I use this application as a reference all the time it's like a work of art =)
Edit: Google updated the app so the old database link doesn't work.

I'm not really sure what your question is.
If you are asking if it's OK to create your DB from scratch using a text file containing a load of SQL CREATE statements then the answer is "yes".
Not only that but it's more flexible than distributing a pre-built DB within your APK as you could even download the 'schema' file or files from a network location. This allows you to update the schema dynamically at a central point.

Related

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.

Manage DB before uploading to playstore in android

I have uploaded an application to play store couple of weeks back. This application involves sqlite database that stores information on username, password, other details that given are by user while using the application locally.
Now I have couple of more tables and fields added to database and wanna upload the application to playstore as an update?
My worry is if the user updates the application from playstore - After update - all the data stored in database will be saved or will the user has to recreate everything from scratch?
Let me know!
Thanks!
You have to override the onUpgade method of SQLiteOpenHelper. In the OnUpgrade method you can either erase the data(drop sqlite command) or maintain the data with the additional columns(alter sqlite command) or create new table (create sqlite command).
Refer the following snippet.
I assume your version would be 1.(Plz check the constructor of your SqliteOpenHelper class)
Increment the version by 1.
class DatabaseHelper extends SqliteOplenHelper{
private static final int DATABASE_VERSION = 2; //new version of the database
private static final int Database_name = "MyDatabase";
private static final String alterUserName = "alter table users add name text";
private static final String table_users = "create table if not exists "
+ users + "(" + "_id integer primary key autoincrement,"
+ "email text" + ")";
public DatabaseHelper(Context context) {
super(context, Database_name, null, DATABASE_VERSION);
cntxt = context;
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(table_users);
db.execSQL(alterUserName);
}
}
Now everytime when you roll the next update with database changes be sure to increment the database version by 1 else let it remain the same.
This isn't done for you automatically. In your SQLiteOpenHelper, you need to increment the Schema integer. This will trigger the on upgrade method for your existing users.
Adding a table is not a problem, just do this in onUpgrade, nothing breaks.
However to add fields, you should use the 'ALTER TABLE' SQL command
If you add new columns you can use ALTER TABLE to insert them into a live table. If you rename or remove columns you can use ALTER TABLE to rename the old table, then create the new table and then populate the new table with the contents of the old tab
See the official reference here

Android not paying attention to file changes?

I'm running into a very frustrating bug.
I have a java class that reads in data from a file, and enters it into the database.
package edu.uci.ics.android;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DbAdapter extends SQLiteOpenHelper{
private static final String DATABASE_NAME = "mydb";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = "fruits";
private static final String FRUIT_ID = "_id";
private static final String FRUIT_NAME = "name";
private static final String FILE_NAME = "fruits.txt";
private static final String CREATE_TABLE = "CREATE TABLE "+ TABLE_NAME + "("+FRUIT_ID+" integer primary key autoincrement, "+FRUIT_NAME+" text not null);";
private SQLiteDatabase mDb;
private Context mContext;
public DbAdapter(Context ctx){
super(ctx, DATABASE_NAME, null, DATABASE_VERSION);
mContext = ctx;
this.mDb = getWritableDatabase();
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE);
// populate database
try {
BufferedReader in = new BufferedReader(new InputStreamReader(mContext.getAssets().open(FILE_NAME)));
String line;
while((line=in.readLine())!=null) {
ContentValues values = new ContentValues();
values.put(FRUIT_NAME, line);
db.insert(TABLE_NAME, null, values);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS "+TABLE_NAME);
onCreate(db);
}
public Cursor fetchAll() {
return mDb.query(TABLE_NAME, new String[] {FRUIT_NAME}, null, null, null, null, null);
}
}
EDIT:
To be more clear, this is what fails:
When I change the database name variable, the table name variable, the program force closes, indicating that something went wrong. Why can't I change the name of the table I create?
When I make changes in the fruits.txt file, I don't see anything reflecting those changes at run-time. Why does this happen?
SQLiteOpenHelper.onCreate() will only get called automatically if the database does not exist, which will only happen once. After that, the database file exists on the device's internal storage so it is simply going to load up the version it has.
If you want to create a new database when the external file is changed, you need to either delete the current database file (manual process) or also change the current database version the helper is created with. When Android sees that the version SQLiteOpenHelper is created with varies from the current file in internal storage, it will call onUpgrade() to allow the existing database to be modified to match the new "version".
EDIT:
To clarify, when you create a database, a db file is created (and persisted) on the device's internal storage, separate from your application's assets. When you re-run your application, persisted data storage does not clear out (or else it wouldn't be "persisted" anymore) so that database file from the last run of your application still exists...with all the settings from when it was created.
When you make changes to the variables in this class, it doesn't somehow magically modify the database file that already exists on the device, so now you are looking for tables and databases that don't exist (probably where your crashes are coming from).
If you simply need to clear out the database so it will reflect changes you've made during development, just clear the database manually on the device by going into Settings -> Manage Applications -> {Application Name} -> Clear Data. This deletes persisted files so they can be re-created by your application the next time you launch it.
If, however, you need this to somehow be a feature where your application automatically recognizes changes you've made to a file in /assets and modifies or re-creates the database as a result, then look at my previous suggestions about using the upgrade mechanism built into SQLiteOpenHelper
HTH
When you change the database name or table name in your code, they no longer reflect the names in the database on the device, so you get a force close. During development, the easy thing is to just uninstall your application and then reinstall whenever you make incompatible changes like that. When changing your database schema from one released version to another, you need to increase the database version number and do the right thing in onUpgrade().
For example, right now, you have
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS "+TABLE_NAME);
onCreate(db);
}
So when you change the table name in code from, say "fruits" to "veggies", onUpgrade() gets run, but table veggies doesn't exist, so it isn't dropped, and then you call onCreate(db) whith a conflicting shchema on top of the existing database. So you need to check oldVeresion and newVersion and do something more like
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (newVersion == 2 && oldVersion == 1) {
db.execSQL("DROP TABLE IF EXISTS" + TABLE_NAME_V1);
db.execSQL("CREATE TABLE "+ TABLE_NAME ...);
}
}
If you're trying to change fruits.txt on the device, it won't work. That's how Android is designed: your assets never change, they're a read-only part of your APK. You need to write the fruit.txt file to the SD card if you want it to be able to change it.

Android: duplicate values are being inserted in to database

Each time i run the project same values are also being inserted in the android database. Even i have given the drop if exists query.What i need is that the data in the database gets updated only if there are some changes in the response from the server side instead of cresting database every time but what is happening with me is that same values got insertes again in the tables. How do I solve this? Following is my code:
public void onCreate(SQLiteDatabase database) {
try{
// onUpgrade(database, oldVersion, newVersion)
database.execSQL(DATABASE_CREATE);
}
catch (Exception e) {
System.out.println("in on create db"+e);
}}
public void onUpgrade(SQLiteDatabase database, int oldVersion,
int newVersion) {
database.execSQL("DROP TABLE IF EXISTS" +DATABASE_CREATE);
onCreate(database);
}
private static final String INSERT = "insert into "
+ DATABASE_TABLE + "(KEY_TYPE,KEY_ENCODING,KEY_WIDTH,KEY_HEIGHT,KEY_DATA,KeyIId)"+" values (?,?,?,?,?,?)";
public WineDatabaseAdapter(Context context) {
try{
this.context = context;
openHelper = new WineDatabaseHelper(context);
this.db=openHelper.getWritableDatabase();
this.insertStmt=this.db.compileStatement(INSERT);
}
catch(Exception e)
{
System.out.println(e);
}
}
Can anyone help me how to solve this problem.
Thanks
DROP TABLE seems a pretty extreme way of trying to stop duplicate values. It's a bit hard to follow the code you've posted, but the normal way of stopping duplicates is to add a unique index on the appropriate column(s). Have you tried that yet ? E.g. something like
CREATE UNIQUE INDEX idx_keytype ON tableName (key_type)
What does your schema look like? If you don't want duplicate rows and you know a certain column will be unique use the "UNIQUE" specifier on it. If what you really want is for the row to be replaced you have to use the databaseHelper command "replace" ie. dbHelper.replace(...);

Upgrading Android application data with multiple tables (without destroying unaffected tables)

In my Android application; I have a single database with multiple tables.
Each table is more-or-less separate from each other, but figured (for best practice?) to just have one DB file.
When it comes to Upgrades, it's currently an all or nothing affair. On upgrade, it "DROP"'s all the tables and re-creates them. However, this is rather harsh if only one of the tables has changed as all the other tables' data is also lost.
Is there a built-in way to auto-upgrade just the tables that have changed?
(e.g. using a version number per/table?)
If not, I guess I can see two options:
Use separate databases/files for each table, to use built-in version upgrade functionality.
Use the database version number to know when the "schema" has changed, but have a separate table to store the current TABLE_VERSIONS and manage my own upgrade by checking the version number of each table against the current build and DROP/CREATE Tables where needed.
(I'd rather not re-invent the wheel here, so I'm hoping I'm missing something simple...)
You need an abstract class that implements the upgrade process described here. Then you extend this abstract class for each of your tables. In your abstract class you must store you tables in a way(list, hardcoded) so when the onUpgrade fires you iterate over the table items and for each table item you do the described steps. They will be self upgraded, keeping all their existing details. Please note that the onUpgrade event fires only once per database, that's why you need to iterate over all your tables to do the upgrade of all of them. You maintain only 1 version number over all the database.
beginTransaction
run a table creation with if not exists (we are doing an upgrade, so the table might not exists yet, it will fail alter and drop)
put in a list the existing columns List<String> columns = DBUtils.GetColumns(db, TableName);
backup table (ALTER table " + TableName + " RENAME TO 'temp_" + TableName)
create new table (the newest table creation schema)
get the intersection with the new columns, this time columns taken from the upgraded table (columns.retainAll(DBUtils.GetColumns(db, TableName));)
restore data (String cols = StringUtils.join(columns, ",");
db.execSQL(String.format(
"INSERT INTO %s (%s) SELECT %s from temp_%s",
TableName, cols, cols, TableName));
)
remove backup table (DROP table 'temp_" + TableName)
setTransactionSuccessful
(This doesn't handle table downgrade, if you rename a column, you don't get the existing data transfered as the column names do not match).
.
public static List<String> GetColumns(SQLiteDatabase db, String tableName) {
List<String> ar = null;
Cursor c = null;
try {
c = db.rawQuery("select * from " + tableName + " limit 1", null);
if (c != null) {
ar = new ArrayList<String>(Arrays.asList(c.getColumnNames()));
}
} catch (Exception e) {
Log.v(tableName, e.getMessage(), e);
e.printStackTrace();
} finally {
if (c != null)
c.close();
}
return ar;
}
public static String join(List<String> list, String delim) {
StringBuilder buf = new StringBuilder();
int num = list.size();
for (int i = 0; i < num; i++) {
if (i != 0)
buf.append(delim);
buf.append((String) list.get(i));
}
return buf.toString();
}
If you're using the Android SQLite helper classes (i.e. SQLiteOpenHelper) then you only have one version number representing the database schema. Personally, I put all the schema creation code in my instance of SQLiteOpenHelper and keep the upgrade logic simple:
#Override
public void onUpgrade (SQLiteDatabase db, int oldVersion, int newVersion) {
// Alter all the tables so the schema is brought up-to-date.
if (oldVersion < newVersion) {
db.execSQL("ALTER TABLE foo ADD COLUMN new_column INTEGER NOT NULL");
}
}

Categories

Resources