I am trying to make an application that comes with some pre inserted rows in the database for the user to use. I have looked at lots of different questions here relating to the same topic but I am still slightly confused.
At the moment I have a DB class that contains all the table creates and functions that interact with the different tables. From my understanding of reading around the best way to have pre populated data is creating a file in the assets folder and calling that to insert the data.
As per the help of the answers below I have made some progress, I am no facing the error of not being able to access the sql file in the res folder or even in the raw folder. I have added below the snippet from the class were I am creating my tables and then attempting to call sql file using the runScript method. I have also added the directory layout of both res and raw.
Methods
public void runScript(SQLiteDatabase db, int rawResourceId, boolean okToFail)
{
Log.i("DB", "Running SQL script");
InputStream in = context.getResources().openRawResource(rawResourceId);
Scanner s = new Scanner(in);
String sql = "";
while (s.hasNext())
{
sql += " " + s.nextLine();
if (sql.endsWith(";"))
{
try
{
db.execSQL(sql);
}
catch (SQLException e)
{
if (okToFail)
Log.w(getClass().getSimpleName(), e.getMessage());
else
throw e;
}
sql = "";
}
}
s.close();
}
private static class DatabaseHelper extends SQLiteOpenHelper
{
DatabaseHelper(Context context)
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db)
{
try {
db.execSQL(CREATE_TABLE_RECIPES);
db.execSQL(CREATE_TABLE_INGREDIENTS);
db.execSQL(CREATE_TABLE_CONTENTS);
db.execSQL(CREATE_TABLE_SHOPPING);
db.execSQL(CREATE_TABLE_DIRECTIONS);
db.execSQL(CREATE_TABLE_FOOD_CATEGORY);
db.execSQL(CREATE_TABLE_FOOD_LIST);
db.execSQL(CREATE_TABLE_BARCODE);
db.execSQL(CREATE_TABLE_FAVOURITES);
runScript(db, R.raw.pocket_chef_db.sql); ------Cannot resolve symbol raw
//runScript(db, R.res.database.pocket_chef_db.sql) ------Cannot resolve symbol res
} catch (SQLException e) {
e.printStackTrace();
}
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS contacts");
onCreate(db);
}
}
Res - Path to sql
Res->database->pocket_chef_db.sql
Raw - Path to sql
Raw->pocket_chef_db.sql
The way I did this is create a .sql file and put it in the res/raw folder.
This script contains DROP IF EXISTS statements to drop all existing tables and then does all the CREATE TABLE statements. If course you can add INSERT statements as well.
To run this script I wrote the following method in my SQLOpenLiteHelper extension class. Your rawResourceId will be R.raw.db_create, if your file is called db_create.sql.
/**
* Runs the provided raw resource as a script that doesn't return anything.
*
* Warning: this is a NOT a foolproof SQL script interpreter.
*
* Please note:
* All terminators (;) must be at the end of a line.
*
*
* #param db
* #param rawResourceId
*/
public void runScript(SQLiteDatabase db, int rawResourceId, boolean okToFail)
{
Log.i(getClass().getSimpleName(), "Running SQL script");
InputStream in = context.getResources().openRawResource(rawResourceId);
Scanner s = new Scanner(in);
String sql = "";
while (s.hasNext())
{
sql += " " + s.nextLine();
if (sql.endsWith(";"))
{
try
{
db.execSQL(sql);
}
catch (SQLException e)
{
if (okToFail)
Log.w(getClass().getSimpleName(), e.getMessage());
else
throw e;
}
sql = "";
}
}
s.close();
}
Try the SqliteAssetHelper from this link https://github.com/jgilfelt/android-sqlite-asset-helper to implement your requirement.
Related
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.
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.
I've been racking my brain on this for days and I just can't wrap my head around using SQLite databases in Android/Java. I'm trying to select two rows from a SQLite database into a ListArray (or two, one for each row. Not sure if that would be better or worse) and I just don't understand how to do it. I've tried various database manager classes that I've found but none of them do what I need and it seems that I should be able to do this simple task without the extra features I've seen in other database managers. Is there any simple way to JUST query some data from an existing SQLite database and get it into a ListArray so that I can work with it? I realize I need to copy the database from assets into the Android database path and I can handle that part. I also need to be able to modify one of the columns per row. I don't need to create databases or tables or rows. I implore someone to help me with this as I consider any code I've written (copied from the internet) to be completely useless.
You can create a method like this :
private List<MyItem> getAllItems()
List<MyItem> itemsList = new ArrayList<MyItem>();
Cursor cursor = null;
try {
//get all rows
cursor = mDatabase.query(MY_TABLE, null, null, null, null,
null, null);
if (cursor.moveToFirst()) {
do {
MyItem c = new MyItem();
c.setId(cursor.getInt(ID_COLUMN));
c.setName(cursor.getString(NAME_COLUMN));
itemsList.add(c);
} while (cursor.moveToNext());
}
} catch (SQLiteException e) {
e.printStackTrace();
} finally {
cursor.close();
}
return itemsList;
}
This will be inside your class let say MyDatabaseHelper where you will also have to declare a :
private static class DatabaseHelper extends SQLiteOpenHelper{
private final static String DATABASE_CREATE="create table " + MY_TABLE + " (id integer primary key, country string);";
public DatabaseHelper(Context context,String name, CursorFactory factory, int version){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db){
db.execSQL(DATABASE_CREATE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion,
int newVersion){
Log.w(TAG, "Upgrading database from version " + oldVersion
+ " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS "+ MY_TABLE);
onCreate(db);
}
}
used to open() and close() the database.
I need to update my android application in the market to next version.next version i need to update the sqlite DB without losing the exsiting data.
In version one i did not create the tables in runtime instead get the database file from the "assets" folder and copies into the system database path of my application.
Refer to this link
http://www.reigndesign.com/blog/using-your-own-sqlite-database-in-android-applications/
next version of my app i have modify the exsting table columns and add extra records and there are few new tables as well.I have updated the data base and copied to asset folder.
This will work for users who buy the app for first time,but not for existing users,my question is how can i update the version one users data base without losing existing data
Sam.
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion,
int newVersion)
{
Log.w(TAG, "Upgrading database from version " + oldVersion
+ " to "
+ newVersion + ", which will destroy all old data");
if(oldVersion == 2 && newVersion == 3)
{
db.execSQL("ALTER TABLE xyz ADD bobby int default 0");
}
else
{
db.execSQL("DROP TABLE IF EXISTS xyz");
onCreate(db);
}
}
}
Prepare sql query to upgrade database.
If database exists then perform updating else copy database from assets.
In tutorial that you provided is such code:
if(dbExist){
//do nothing - database already exist
}else{
In place where is //do nothing - database already exist put your upgrading code.
You could try a patch-like solution I describe in this blog post. It will aid with incremental upgrading and will continue to scale as you build more and more versions.
http://www.greenmoonsoftware.com/2012/02/sqlite-schema-migration-in-android/
In your SQLiteHelper class the DATABASE_VERSION variable should be the latest version. Suppose earlier DATABASE_VERSION was 1 and as you are upgrading so it must be 2.
DatabaseHelper(Context context)
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
Now Upgrade the old database version to new Version. If you don't set the latest version number in the database then onUpgrade(..., ...) will get called repeatedly.
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
logger.info("DBManager - :::: Upgrading database from version " + oldVersion+ " to "+ newVersion + ", which will modify some old table");
String sqlFileName = "update_version_1.sql";
int totalSQLQueries = executeSQLScript(db, sqlFileName);
logger.info("DBManager - :::: Upgrading database from version - " +"Total " + totalSQLQueries +" queries executed succesfully from " +sqlFileName);
if(db.getVersion() == oldVersion) {
db.setVersion(newVersion);
logger.info("DBManager - :::: DB Version upgraded from " +oldVersion +" to " +newVersion);
}
}
Your database modification code must be written within a transaction. See the code bellow as an example of using transaction for database upgrade :-
private int executeSQLScript(SQLiteDatabase db, String sqlFileName)
{
int count = 0;
InputStream inputStream = null;
BufferedReader bufferReader = null;
try
{
db.beginTransaction();
inputStream = localContext.getApplicationContext().getAssets().open(sqlFileName);
bufferReader = new BufferedReader(new InputStreamReader(inputStream));
String inputLine;
while((inputLine = bufferReader.readLine()) != null)
{
if(inputLine.trim().equalsIgnoreCase("BEGIN;") || inputLine.trim().equalsIgnoreCase("END;"))
{
continue;
}
else
{
db.execSQL(inputLine);
count = count + 1;
}
}
db.setTransactionSuccessful();
}
catch(Exception ex) {
ex.printStackTrace();
}
finally
{
if(bufferReader != null)
{
try {
bufferReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(inputStream != null)
{
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
db.endTransaction();
}
return count;
}
I am doing my first app with a database and I am having a little trouble understanding the onUpgrade function. My database has a table with an items and a favorite column so that the user can favorite an item. Most implementations I see simply drop the table and reconstruct it but I don't want to do this. I want to be able to add more items to the table.
When the app is upgraded through the android marketplace does the database know its version number? So could I increment the version number in the code and then export it to the marketplace and when the user boots up the upgraded version for the first time then onUpgrade will be called?
If this is the case my onUpgrade would simply pull from a file and add the database items in. Is this a standard way of doing things or is there a better way of handling this in Android. I am trying to stay as standard as possible.
Thanks
Ok, before you run into bigger problems you should know that SQLite is limited on the ALTER TABLE command, it allows add and rename only no remove/drop which is done with recreation of the table.
You should always have the new table creation query at hand, and use that for upgrade and transfer any existing data. Note: that the onUpgrade methods runs one for your sqlite helper object and you need to handle all the tables in it.
So what is recommended onUpgrade:
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();
}
Next to Pentium10's excellent answer, here are some good examples from living code:
Android AOSP: com.android.providers.calendar.CalendarDatabaseHelper.java
Android AOSP: com.android.browser.BrowserProvider.java
OpenIntents Notepad: org.openintents.notepad.NotePadProvider.java
Thank you for clarifying that onUpgrade() will not support Remove/Drop statements #Pentium 10
For those of you who would like to know the exact moment when onUpgrade() gets called, it is during a call to either getReadableDatabase() or getWriteableDatabase().
To those who are not clear how it ensure it gets triggered...the answer is: It is triggered when the database version provided to the constructor of SqLiteOpenHelper is updated. Here is a example
public class dbSchemaHelper extends SQLiteOpenHelper {
private String sql;
private final String D_TAG = "FundExpense";
//update this to get onUpgrade() method of sqliteopenhelper class called
static final int DB_VERSION = 2;
static final String DB_NAME = "fundExpenseManager";
public dbSchemaHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
// TODO Auto-generated constructor stub
}
now to...onUpgrade()
#Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
sql = "ALTER TABLE " + fundExpenseSchema.Expense.TABLE_NAME + " ADD COLUMN " + fundExpenseSchema.Expense.FUNDID + " INTEGER";
arg0.execSQL(sql);
}
I've been using the solution proposed by #Pentium10 for a long time but today i had a problem, after doing alter table, getColumns from the original table still returns the same columns (in the new version of the db the table suffer mayor structure changes, some columns added some others), really i don't know why select statement does not reflect the structure changes, more over before creating my table again, select statement still returns the columns! When the table is not re-created yet!
So i manage solving this issue updating getColumns method using pragma table_info, like this:
/**
* Get a list of column base_dictionary for the selected table
*
* #param db
* Database that contains the table
* #param tableName
* Table name to be used
* #return A List of column name
*/
public static List<String> getColumns(SQLiteDatabase db, String tableName) {
List<String> ar = null;
Cursor c = null;
try {
c = db.rawQuery("pragma table_info(" + tableName + ")", null);
ar = new ArrayList<String>();
if (c != null && c.moveToFirst()) {
do {
ar.add(c.getString(c.getColumnIndexOrThrow("name")));
} while (c.moveToNext());
c.close();
}
} catch (Exception e) {
Log.v(tableName, e.getMessage(), e);
e.printStackTrace();
} finally {
if (c != null) c.close();
}
return ar;
}