I am not very much familiar with android sqlite database. I only have rough idea of populating sqlite database. I have followed some tutorials but they tells different things.
I have an android project and one of its' packeges is a .db
This package consists of 5 different classes. They are:
DataAdapter.java
DataBaseHelper.java
DBAdapter.java
LoadDBActivity.java
SelectDBAdapter.java
I know SelectDBAdapter class is used to select data from the database. My database is in asset folder which is in .jpeg format.I can open it from sqliteBrowser.
Actually, what I want to know is why should we use these different classes ? and what's the purpose of each and every class ?
I am really sorry, I cannot post codes since this projects belongs to another person (my friend).
I would be much obliged if anyone could be so kind enough to explain the meaning of using these different classes and why should we use such a senario ?
From my development experience , I always prefer to add a prepared sqlite database file in the /res/raw folder.You create/manage sqlite database using Sqlite Manager addon of Firefox , it's a great tool. This method is really great because
firstly I don't need to write a bunch of codes for creating/managing database.
Most importantly , some applications needs to read from a pre-populated database. I don't need to care about what the app requires and whether database is empty or filled already. It serves all purpose. I just need to write some methods that runs the required simple sqls.
Here is my own customised DatabaseHelper class. To use this class you'll need to follow some instructions.
If sqlite database size is more than 1MB then split the file into chunks , I prefer 512KB chunks and place them into /res/raw directory.
Edit the package name and your db file names in the following class.
package your.packagee.name;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import android.widget.Toast;
public class DataBaseHelper extends SQLiteOpenHelper {
private static final String pkg = "your package name";
private static String DB_PATH = "/data/data/" + pkg + "/databases/";
private static String DB_NAME = "yourDBFile.sqlite";
int[] dbfiles = { R.raw.chunk1 , R.raw.chunk2 ..... };
private SQLiteDatabase myDataBase;
private final Context myContext;
public DataBaseHelper(Context context) {
super(context, DB_NAME, null, 1);
this.myContext = context;
}
public void createDataBase() {
boolean dbExist = checkDataBase();
if (dbExist) {
// do nothing - database already exist
} else {
this.getReadableDatabase();
try {
CopyDataBase();
} catch (IOException e) {
Toast.makeText(myContext, e.getMessage(), Toast.LENGTH_SHORT)
.show();
Log.d("Create DB", e.getMessage());
}
}
}
private boolean checkDataBase() {
SQLiteDatabase checkDB = null;
try {
String myPath = DB_PATH + DB_NAME;
checkDB = SQLiteDatabase.openDatabase(myPath, null,
SQLiteDatabase.NO_LOCALIZED_COLLATORS);
} catch (SQLiteException e) {
Toast.makeText(myContext, e.getMessage(), Toast.LENGTH_SHORT)
.show();
Log.d("Check DB", e.getMessage());
}
if (checkDB != null) {
checkDB.close();
}
return checkDB != null ? true : false;
}
private void CopyDataBase() throws IOException {
InputStream databaseInput = null;
Resources resources = myContext.getResources();
String outFileName = DB_PATH + DB_NAME;
OutputStream databaseOutput = new FileOutputStream(outFileName);
byte[] buffer = new byte[512];
int length;
for (int i = 0; i < dbfiles.length; i++) {
databaseInput = resources.openRawResource(dbfiles[i]);
while ((length = databaseInput.read(buffer)) > 0) {
databaseOutput.write(buffer, 0, length);
databaseOutput.flush();
}
databaseInput.close();
}
databaseOutput.flush();
databaseOutput.close();
}
public void openDataBase() throws SQLException {
String myPath = DB_PATH + DB_NAME;
myDataBase = SQLiteDatabase.openDatabase(myPath, null,
SQLiteDatabase.NO_LOCALIZED_COLLATORS);
}
#Override
public synchronized void close() {
if (myDataBase != null)
myDataBase.close();
super.close();
}
#Override
public void onCreate(SQLiteDatabase db) {
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
public boolean deleteItem (String ID){
String query = "delete from item where id='" + ID + "'" ;
Log.d("Query : ", query);
try{
myDataBase.execSQL(query);
return true ;
} catch (Exception e){
Log.d("Exception", e.toString());
return false ;
}
}
public Cursor getSearchFromID(String id) {
return myDataBase.rawQuery("select * from item where id = \"" + id + "\"", null);
}
public boolean addSave(String type, String data , String time) {
String query = "insert into item (type, data , timestamp) values ('" + type
+ "', '" + data + "', '" + time + "')";
try {
myDataBase.execSQL(query);
return true ;
} catch (Exception e) {
return false ;
}
}
}
Here's some methods written as a sample , how to use it.
Usage is simple. When your application starts , that means in your Launcher activity use this code to initialize your database
DataBaseHelper helper = new DataBaseHelper(this);
helper.createDataBase();
helper.openDataBase();
helper.close();
Then just use the methods written in DatabaseHelper class. A sample will be like this
String id = "1";
DataBaseHelper helper = new DataBaseHelper(this);
helper.openDataBase();
Cursor c = helper.getSearchFromID(id);
if(c.getCount() > 0){
c.moveToFirst();
while(!c.isAfterLast()){
// extract your data from cursor
c.MoveToNext();
}
}
Hope it will solve your all problems about sqlite database in Android. At least it solved for me. Thank you.
There are various way in populating a database. What I do is I create an insert(ObjectType objectName) in the DBAdapter Class. That being said, I create an object class and for this example, I'm going to use Authorized Personnel
public class AuthorizedPersonnelClass {
private String _id;
private String Last_Name;
private String Middle_Name;
private String First_Name;
private String Store_ID;
private String Status;
private String New_Personnel;
//of course insert your 2 constructors and getter setter methods here
}
In my DBAdapter, I'll create the insert(AuthorizedPersonnelClass authorizedPersonnel) method to handle the data insertions:
public long addPersonnel(AuthorizedPersonnelClass authorizedPersonnel){
ContentValues values = new ContentValues();
values.put(AUTHORIZEDPERSONNEL_ID, authorizedPersonnel.get_id());
values.put(L_NAME_AUTHORIZED_PERSONNEL, authorizedPersonnel.getLast_Name());
values.put(M_NAME_AUTHORIZED_PERSONNEL, authorizedPersonnel.getMiddle_Name());
values.put(F_NAME_AUTHORIZED_PERSONNEL, authorizedPersonnel.getFirst_Name());
values.put(STATUS, authorizedPersonnel.getStatus());
values.put(STORE_ID, authorizedPersonnel.getStore_ID());
values.put(NEW, authorizedPersonnel.getNew_Personnel());
return this.mDB.insert(TABLE_AUTHORIZED_PERSONNEL, null, values);
}
And from there, let's say I want to populate entries in my onCreate() function or in a button call, I'll just do as such:
//instantiate a global variable for the DBAdapter
DBAdapter db = new DBAdapter(this);
//then if you want to insert
db.insert(new AuthorizedPersonnelClass( /*insert variables here*/ ));
Of course these values may be hard coded or user input (just use EditTexts and extract the Strings and use them there).
Here, I used the ContentValues example because it's easier for beginners to use as opposed to doing a rawQuery Insert statement which may get confusing.
Related
My app is a quiz that has 33 stages and 25 categories, quiz data are in a LOCAL sqlite database that has three tables (Stages + Categories + Questions), users scores are saved to the Stage/Category tables in the database upon successful completion of each level.
I need from time to time to revise the data OR add new stages, categories and questions; I do so in the sqlite database itself, then I copy the database and paste it the Asset folder thus replacing old one, and the incitement version number.
These changes appear to new users after first installation BUT DO NOT appear to existing users after update.
I would like to reflect all changes and STILL PRESERVE user data (scores) that are already stored in the database.
I have tried the following:
1- copyDataBase onUpgrade - RESULT: changes and not reflected and user data is preserved.
2- Create a new_sqlite_database, copyDataBase onUpgrade using the new_sqlite_database - RESULT: changes are reflected, app crashes onclicklistner of a stage only once and works thereafter, user data is not preserved.
3- Alter Table with temp names, Drop old tables, create new tables with same old names, copyDataBase, update new tables records using temp tables - RESULT: Blank screens.
QuizDbHelper
class QuizDbHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "qa_sqlite.db";
private static String DATABASE_LOCATION = "/data/data/packagename/databases/";
private static final int DATABSE_VERSION = 12;
private final Context mContext;
private SQLiteDatabase mDatabase;
private final Handler handler = new Handler();
public QuizDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABSE_VERSION);
if (android.os.Build.VERSION.SDK_INT >= 17)
DATABASE_LOCATION = context.getApplicationInfo().dataDir + "/databases/";
else
DATABASE_LOCATION = "/data/data/" + context.getPackageName() + "/databases/";
this.mContext = context;
copyDataBase();
this.getReadableDatabase();
}
#Override
public void onCreate(SQLiteDatabase db) {
copyDataBase();
Log.w("POSITIVE ... ","onCreate Called - Database Copied");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (newVersion > oldVersion) {
copyDataBase();
Log.w("POSITIVE ... ", "onUpgrade Called - Database Copied");
}
}
private boolean checkDataBase() {
File dbFile = new File(DATABASE_LOCATION + DATABASE_NAME);
return dbFile.exists();
}
private void copyDataBase() {
if (!checkDataBase()) {
this.getReadableDatabase();
this.close();
try {
copyDBFile();
} catch (IOException mIOException) {
throw new Error("ُCopy Database Failed");
}
}
}
private void copyDBFile() throws IOException {
InputStream mInput = mContext.getAssets().open(DATABASE_NAME);
OutputStream mOutput = new FileOutputStream(DATABASE_LOCATION + DATABASE_NAME);
byte[] mBuffer = new byte[1024];
int mLength;
while ((mLength = mInput.read(mBuffer)) > 0)
mOutput.write(mBuffer, 0, mLength);
mOutput.flush();
mOutput.close();
mInput.close();
}
private void openDatabase() {
String dbPath = mContext.getDatabasePath(DATABASE_NAME).getPath();
if (mDatabase != null && mDatabase.isOpen()) {
return;
}
mDatabase = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READWRITE);
}
private void closeDatabase() {
if (mDatabase != null) {
mDatabase.close();
}
}
}
Database should be created or updated using onCreate or onUpgrade in QuizDbHelper after calling mDBHelper.getReadableDatabase(); onCreate in Mainavtivity.java ... example:
Mainavtivity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
QuizDbHelper mDBHelper = new QuizDbHelper(this);
mDBHelper.getReadableDatabase();
SOME OTHER CODE
}
I NEED Two thing::
1. Reflect all changes after users update the app
2. PRESERVE user's data (scores) that are already stored in the database.
I haven't ever had to do this for one of my apps, but if I did, I would have a method to merge the new version of the database with the old one. The basic flow would be to see if the local database were present, if not, copy the new database to the local folder. If it does exist, check the db version. If the db version is less than the new database version, then copy the new database to the local folder with a temp name, then call doMerge().
doMerge would open the new database and select all rows from one of the tables into a cursor. Then, it would iterate through the cursor and do a select query on the old database to see if that record already exists in the old database, if so, skip it. Say, the new database has a cursor of all of the Stages records, you take each record's Name or Description value and "SELECT * FROM old.stages WHERE Name = '" + newName + "'"
If not found, insert the values from the new database into the old database. Repeat this for all three tables.
The trick will be managing the primary keys in each table. What if the user has inserted 50 records in one of the tables, and those keys match the primary keys in the new rows in the new database? If you have primary/foreign keys in your other tables, you would have to sync them, which would get hairy.
I managed to achieve the first objective
reflecting all changes after app upgrade ... here is what I have done:
**// 1. Check database version:**
private Boolean checkDataBaseVersion() {
Boolean Upgrade = false;
SQLiteDatabase db = mContext.openOrCreateDatabase(DATABASE_NAME, Context.MODE_PRIVATE, null);
Integer CurrentdbVersion = db.getVersion();
if (CurrentdbVersion < DATABSE_VERSION) {
Upgrade = true;
} else {
Upgrade = false;
}
Log.w("POSITIVE ...", "checkDataBase - Database Upgrade = " + Upgrade + " ... CurrentdbVersion: "
+ CurrentdbVersion + " DATABSE_VERSION: " + DATABSE_VERSION);
db.close();
return Upgrade;
}
**// 2. change copy database code:**
public void copyDataBase() {
if (!checkDataBase()) {
this.getReadableDatabase();
this.close();
try {
copyDBFile();
Log.w("POSITIVE ...", "copyDataBase - Database does not exists ... db copied");
} catch (IOException mIOException) {
throw new Error("حدث خطأ في نسخ قاعدة البانات من الملف");
}
} else if (checkDataBaseVersion()) {
//this.getReadableDatabase();
//this.close();
try {
copyDBFile();
Log.w("POSITIVE ...", "copyDataBase - Database exists - new version exists ... db copied ");
} catch (IOException mIOException) {
throw new Error("حدث خطأ في نسخ قاعدة البانات من الملف");
}
} else {
Log.w("POSITIVE ...", "copyDataBase - Database exists - no new version ... nothing done ");
}
}
private boolean checkDataBase() {
File dbFile = new File(DATABASE_LOCATION + DATABASE_NAME);
Log.w("POSITIVE ...", "checkDataBase - Database exists = " + dbFile.exists());
close(); /// NEW
return dbFile.exists();
}
**// 3. remove copy database code from public QuizDbHelper(Context context) and place it where it is needed:**
public QuizDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABSE_VERSION);
if (android.os.Build.VERSION.SDK_INT >= 17)
DATABASE_LOCATION = context.getApplicationInfo().dataDir + "/databases/";
else
DATABASE_LOCATION = "/data/data/" + context.getPackageName() + "/databases/";
this.mContext = context;
}
What I'm trying to achieve is that my application will ship with an existing sqlite database exported from my web panel with some records(news, products, categories etc.) and later on it will insert some records of its own into it(lets say it'll insert notifications it receives) and it will be copied to the databases folder, up to here there is no problem but my concern is when a user upgrades their application through market I want to replace the new database with the old one but keep those application generated records(notifications it has received) and insert them into the new one. Here's my code so far: (please enhance if necessary)
public class Helper_Db extends SQLiteOpenHelper{
public static final String DB_NAME = "Test.sqlite";
public static final int DB_VERSION = 3;
private static String DB_PATH = "";
private SQLiteDatabase _db;
private final Context _ctx;
public Helper_Db(Context context) {
super(context, null, null, 1);
DB_PATH = "/data/data/" + context.getPackageName() + "/databases/";
_ctx = context;
}
#Override
public void onCreate(SQLiteDatabase db) {
try
{
copyDatabase();
Log.e("DATABASE", "Database created");
}
catch(IOException io)
{
Log.e("DATABASE", io.toString());
}
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//take out the notifications from old db
//insert them into the new db
//delete the old db
//copy the new db
}
private void copyDatabase() throws IOException
{
InputStream input = _ctx.getAssets().open(DB_NAME);
String outFileName = DB_PATH + DB_NAME;
OutputStream output = new FileOutputStream(outFileName);
byte [] buffer = new byte [1024];
int length;
while((length = input.read(buffer)) > 0)
{
output.write(buffer, 0, length);
}
output.flush();
output.close();
input.close();
}
public boolean openDatabase() throws SQLException
{
String path = DB_PATH + DB_NAME;
_db = SQLiteDatabase.openDatabase(path, null,
SQLiteDatabase.CREATE_IF_NECESSARY);
return _db != null;
}
#Override
public void close()
{
if(_db != null)
{
_db.close();
}
super.close();
}
}
Thanks in advance.
I do something similar. I have default content in a database that I ship with the app that changes sometimes. However, I have a routine for synchronizing databases for syncing between users and with backups, and each time I upgrade the DB, I just take the included DB and sync it with the existing user's DB.
That may be too much for your needs, but this brings me to how I know whether data is user data or not which seems like is the real problem here. You need to determine what data is user data. Then all you need to do is copy that data.
So, my recommendation is to create an integer column in each database table called something like "IncludedContent" and set that to 1 on all data that you include in your shipped database and set the default value to 0 which is what all user content will have. Then all you have to so is attach the databases using the Attach command something like this:
db.execSQL("ATTACH DATABASE ? AS Old_DB", new String[]{fullPathToOldDB});
and then do an insert like this to copy only user content:
db.execSQL("INSERT INTO New_DB.TABLE SELECT * FROM Old_DB.TABLE WHERE IncludedContent = 0");
For my internship I'm working on an Android application, and trying to get a local database running to set up for offline referal as well as preventing new BasicAuth requests and a required database pull every time the user returns to a certain activity. To achieve this, I'm trying to set up a SQLite database, but things are not really working out.
I'm currently using this tutorial to try and set up the database. For my application specifically I edited the MySQLiteHelper and TicketsDataSource classes.
MySQLiteHelper:
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class MySQLiteHelper extends SQLiteOpenHelper {
public static final String TABLE_TICKETS = "tickets";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_TICKET_ID = "ticketId";
public static final String COLUMN_TICKET_TITLE = "ticketTitle";
public static final String COLUMN_PROJECT_NAME = "projectName";
public static final String COLUMN_CLIENT_NAME = "clientName";
public static final String COLUMN_TICKET_DESCRIPTION = "ticketDescription";
private static final String DATABASE_NAME = "commments.db";
private static final int DATABASE_VERSION = 1;
// Database creation SQL statement
private static final String DATABASE_CREATE = "create table "
+ TABLE_TICKETS
+ "("
+ COLUMN_ID + " integer primary key autoincrement, "
+ COLUMN_TICKET_ID + " text not null, "
+ COLUMN_TICKET_TITLE+ " text not null, "
+ COLUMN_PROJECT_NAME+ " text not null, "
+ COLUMN_CLIENT_NAME+ " text not null, "
+ COLUMN_TICKET_DESCRIPTION+ " text not null);";
public MySQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase database) {
database.execSQL(DATABASE_CREATE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(MySQLiteHelper.class.getName(),
"Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS " + TABLE_TICKETS);
onCreate(db);
}
}
TicketsDataSource:
import java.util.ArrayList;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
public class TicketsDataSource {
// Database fields
private SQLiteDatabase database;
private MySQLiteHelper dbHelper;
private String[] allColumns = { MySQLiteHelper.COLUMN_ID,
MySQLiteHelper.COLUMN_TICKET_ID, MySQLiteHelper.COLUMN_TICKET_TITLE, MySQLiteHelper.COLUMN_PROJECT_NAME,
MySQLiteHelper.COLUMN_CLIENT_NAME, MySQLiteHelper.COLUMN_TICKET_DESCRIPTION};
public TicketsDataSource(Context context) {
dbHelper = new MySQLiteHelper(context);
}
public void open() throws SQLException {
database = dbHelper.getWritableDatabase();
}
public void close() {
dbHelper.close();
}
public Ticket createTicket(String ticket) {
ContentValues values = new ContentValues();
values.put(MySQLiteHelper.COLUMN_TICKET_DESCRIPTION, ticket);
long insertId = database.insert(MySQLiteHelper.TABLE_TICKETS, null,
values);
Cursor cursor = database.query(MySQLiteHelper.TABLE_TICKETS,
allColumns, MySQLiteHelper.COLUMN_ID + " = " + insertId, null,
null, null, null);
cursor.moveToFirst();
Ticket newTicket = cursorToTicket(cursor);
cursor.close();
return newTicket;
}
public void deleteTicket(Ticket ticket) {
long id = ticket.getId();
System.out.println("Ticket deleted with id: " + id);
database.delete(MySQLiteHelper.TABLE_TICKETS, MySQLiteHelper.COLUMN_ID
+ " = " + id, null);
}
public List<Ticket> getAllTickets() {
List<Ticket> tickets = new ArrayList<Ticket>();
Cursor cursor = database.query(MySQLiteHelper.TABLE_TICKETS,
allColumns, null, null, null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Ticket ticket = cursorToTicket(cursor);
tickets.add(ticket);
cursor.moveToNext();
}
// make sure to close the cursor
cursor.close();
return tickets;
}
private Ticket cursorToTicket(Cursor cursor) {
Ticket ticket = new Ticket();
ticket.setId(cursor.getLong(0));
ticket.setTicketId(cursor.getLong(1));
ticket.setTicketTitle(cursor.getString(2));
ticket.setClientName(cursor.getString(3));
ticket.setProjectName(cursor.getString(4));
ticket.setTicketDescription(cursor.getString(5));
return ticket;
}
}
In my activity, I then call the TicketsDataSource class by first defining it:
private TicketsDataSource datasource;
And then calling it from an if-statement that checks if the server has been called succesfully already:
else {
datasource = new TicketsDataSource(this);
datasource.open();
}
Unfortunately, when I switch to the DDMS view, I can't see any database being created after I run the application multiple times. In fact, I can't even see any other folders within the main "data" folder in the DDMS view. I'm not getting any error messages (except for an 'eglSurfaceAttrib not implemented', which seems to be irrelevant after some searching on google) at this moment, but when I place a system print in the onCreate function of the MySQLiteHelper class, it does not print anything in the log (it does appear in the else-statement). I'm not sure if my Java-code is faulty, or if there's something wrong with my general setup.
Thanks in advance for your help,
Dennis
You'll only be able to see files/folders in the /data folder if you're
a: using the emulator, or
b: using a rooted phone. Those are protected folders and can't be viewed by mere mortals.
The real test of what you've written is this - can you write to and read from the database?
The best android db troubleshooting solution for me was to add this method to my SQliteHelper class which will copy your sqlite db to the sd card on the device your testing on. You can then open it up using any number of sqlite manager desktop apps and verify your tables and data. You don't need to root the phone this way. While developing, I stick calls to this method where i need to get a snapshot of the db:
public static void backup() {
try {
File sd = Environment.getExternalStorageDirectory();
File data = Environment.getDataDirectory();
if (sd.canWrite()) {
String currentDBPath = "//data//com.example//databases//" + DATABASE_NAME;
String backupDBPath = DATABASE_VERSION + DATABASE_NAME;
File currentDB = new File(data, currentDBPath);
File backupDB = new File(sd, "//Download//" + backupDBPath);
if (currentDB.exists()) {
FileChannel src = new FileInputStream(currentDB).getChannel();
FileChannel dst = new FileOutputStream(backupDB).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
}
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
you need to add external storage permissions to your manifest.
You can see my database helper class below. I use prepopulated sqlite database imported in assets folder. Whenever I add a table to my existing database, I get no such table error if my app is already installed on my phone. I guess my onUpgrade() method is now so good. It works, don't get me wrong, when I change some data to existing tables, I increase db version and it gets updated. But if I add a table I get error.
public class DataBaseHelper extends SQLiteOpenHelper
{
private static String TAG = "DataBaseHelper"; // Tag just for the LogCat window
//destination path (location) of our database on device
private static String DB_PATH = "/data/data/rs.androidaplikacije.themostcompleteiqtest/databases/";
private static String DB_NAME ="pitanja.sqlite";// Database name
private static SQLiteDatabase mDataBase;
private final Context mContext;
private static final int DATABASE_VERSION = 3;
public DataBaseHelper(Context mojContext)
{
super(mojContext, DB_NAME, null, 3);// 1 it's Database Version
DB_PATH = mojContext.getApplicationInfo().dataDir + "/databases/";
this.mContext = mojContext;
}
public void createDataBase() throws IOException
{
//If database not exists copy it from the assets
this.getReadableDatabase();
this.close();
try
{
//Copy the database from assests
copyDataBase();
Log.e(TAG, "createDatabase database created");
}
catch (IOException mIOException)
{
throw new Error("ErrorCopyingDataBase");
}
}
/**
* 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
*/
public boolean checkDataBase(){
SQLiteDatabase checkDB = null;
try{
String myPath = DB_PATH + DB_NAME;
checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
}catch(SQLiteException e){
//database does't exist yet.
}
if(checkDB != null){
checkDB.close();
}
return checkDB != null ? true : false;
}
/*Check that the database exists here: /data/data/your package/databases/Da Name
private boolean checkDataBase()
{
File dbFile = new File(DB_PATH + DB_NAME);
//Log.v("dbFile", dbFile + " "+ dbFile.exists());
return dbFile.exists();
}
*/
//Copy the database from assets
private void copyDataBase() throws IOException
{
InputStream mInput = mContext.getAssets().open(DB_NAME);
String outFileName = DB_PATH + DB_NAME;
OutputStream mOutput = new FileOutputStream(outFileName);
byte[] mBuffer = new byte[1024];
int mLength;
while ((mLength = mInput.read(mBuffer))>0)
{
mOutput.write(mBuffer, 0, mLength);
}
mOutput.flush();
mOutput.close();
mInput.close();
}
//Open the database, so we can query it
public boolean openDataBase() throws SQLException
{
String mPath = DB_PATH + DB_NAME;
//Log.v("mPath", mPath);
mDataBase = SQLiteDatabase.openDatabase(mPath, null, SQLiteDatabase.CREATE_IF_NECESSARY);
//mDataBase = SQLiteDatabase.openDatabase(mPath, null, SQLiteDatabase.NO_LOCALIZED_COLLATORS);
return mDataBase != null;
}
#Override
public void close()
{
if(mDataBase != null)
mDataBase.close();
super.close();
}
#Override
public void onCreate(SQLiteDatabase arg0) {
}
#Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
try {
// delete existing?
// Copy the db from assests
copyDataBase();
Log.e(TAG, "database updated");
} catch (IOException mIOException) {
Log.e(TAG, mIOException.toString());
try {
throw mIOException;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
There's 2 ways to do onUpgrade, depending on your app.
1)Drop all your old tables, then run onCreate. THis basically wipes out all your old data and starts over. Its a good technique if you can regenerate the old data somehow, or just don't care about it.
2)Carefully maintain a diff of your schema between each released version, and write SQL statements to make the proper changes between them- add new tables, alter existing tables to add/remove columns, etc. This is time consuming and fragile, so use this only if you need to keep data between those versions.
On the Emulator the app runs fine. On the device the moment im trying to copy my database over the application's database and execute a query the app force closes. Any idea why this could be happening? Do i have to request any kind of permissions or something in manifest for it to run?
Database.Java
public class Database extends SQLiteOpenHelper{
//The Android's default system path of your application database.
private static String DB_PATH = "/data/data/gr.BHC.www/databases/";
//Name of the Database to be created.
private static String DB_NAME = "BHCLibrary3.sqlite";
private SQLiteDatabase myDataBase;
private final Context myContext;
* Constructor
* Takes and keeps a reference of the passed context in order to access to the application assets and resources.
* #param context
*/
public Database(Context context) {
super(context, DB_NAME, null, 1);
this.myContext = context;
}
/**
* Creates a empty database on the system and rewrites it with your own database.
* */
public void createDataBase() throws IOException{
//First we check if the database already exists, Method declared later
boolean dbExist = checkDataBase();
if(dbExist){
//do nothing - database already exists
}else{
//By calling this method an empty database will be created into the default system path
//of your application so we are going to be able to overwrite that database with our database.
this.getReadableDatabase();
try {
copyDataBase(); //Method declared later
} 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() {
//SQLiteDatabase checkdb = null;
boolean checkdb = false;
try{
String myPath = DB_PATH + DB_NAME;
File dbfile = new File(myPath);
//checkdb = SQLiteDatabase.openDatabase(myPath,null,SQLiteDatabase.OPEN_READWRITE);
checkdb = dbfile.exists();
}
catch(SQLiteException e){
System.out.println("Database doesn't exist");
}
return checkdb;
}
/**
* 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 transferring byte stream.
* */
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();
}
//Opening the Database
public void openDataBase() throws SQLException{
//Open the database
String myPath = DB_PATH + DB_NAME;
myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READWRITE);
}
//Finally overriding a few methods as required
#Override
public synchronized void close() {
if(myDataBase != null)
myDataBase.close();
super.close();
}
#Override
public void onCreate(SQLiteDatabase db) {
//First we check if the database already exists, Method declared later
boolean dbExist = checkDataBase();
if(dbExist){
//do nothing - database already exists
}else{
//By calling this method an empty database will be created into the default system path
//of your application so we are going to be able to overwrite that database with our database.
this.getReadableDatabase();
try {
copyDataBase(); //Method declared later
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
And Results.java (The activity i run my query)
public class SearchResults extends ListActivity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.searchresults);
Database myDbHelper = new Database(null);
myDbHelper = new Database(this);
}
// Get the intent, verify the action and get the query
Intent intent = getIntent();
String query = intent.getStringExtra(SearchManager.QUERY);
SQLiteDatabase myDb = myDbHelper.getReadableDatabase();
//Executing our query against the server using rawQuery and getting the cursor
String select="SELECT DISTINCT b._ISBN as _id, b.BookTitle, b.Edition, b.Year, b.Pages, b.Rating, c.Category, p.Publisher, w.LastName" +
" FROM" +
" Books b" +
" JOIN Categories_Books cb ON cb._Books_ISBN = _id" +
" JOIN Categories c ON c._CategoryID = cb._Categories_CategoryID" +
" JOIN Publishers p ON p._PublisherID = b.PublisherID" +
" JOIN Writers_Books wb ON wb._Books_ISBN = _id" +
" JOIN Writers w ON w._WriterID = wb._Writers_WriterID" +
" WHERE b.BookTitle LIKE '%" + query +"%'" +
" OR c.Category LIKE '%" + query +"%'" +
" OR p.Publisher LIKE '%" + query +"%'" +
" OR w.LastName LIKE '%" + query +"%'" +
" OR _id LIKE '%" + query +"%'" +
" GROUP BY b.BookTitle";
Cursor c = myDb.rawQuery(select, null);
startManagingCursor(c);
// the desired columns to be bound
String[] columns = new String[] { "Books.BookTitle", "Publishers.Publisher" };
// the XML defined views which the data will be bound to
int[] to = new int[] { R.id.ISBN_entry, R.id.Title_entry };
//Getting results into our listview
try
{
SimpleCursorAdapter mAdapter = new SimpleCursorAdapter(this, R.layout.listlayout, c, columns, to);
this.setListAdapter(mAdapter);
}
catch( Exception e)
{
}
}
}
Help would be appreciated.
EDIT: The error im getting is : java.lang.runtimeexception: Unable to start activity Componentinfo(gr.BHC.www/gr.BHC.www.SearchResults} and then various exceptions saying table books etc dont exist.
EDIT2: I saw the exception im getting usually related with content providers but i still cant figure out why i'd get that.
I think I solved the problem. I made some changes on your codes and now it is working. Here are the codes:
SearchResults.java
public class SearchResults extends ListActivity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.searchresults);
DbManager myDbHelper = new DbManager(null);
myDbHelper = new DbManager(this);
// Get the intent, verify the action and get the query
myDbHelper.createNewDatabase();
try {
myDbHelper.open();
Log.d("Search Results", "database opened");
} catch (SQLException sqle) {
throw sqle;
}
Intent intent = getIntent();
String query = intent.getStringExtra(SearchManager.QUERY);
// Executing our query against the server using rawQuery and getting the
// cursor
String select = "SELECT DISTINCT b._ISBN as _id, b.BookTitle, b.Edition, b.Year, b.Pages, b.Rating, c.Category, p.Publisher, w.LastName"
+ " FROM"
+ " Books b"
+ " JOIN Categories_Books cb ON cb._Books_ISBN = _id"
+ " JOIN Categories c ON c._CategoryID = cb._Categories_CategoryID"
+ " JOIN Publishers p ON p._PublisherID = b.PublisherID"
+ " JOIN Writers_Books wb ON wb._Books_ISBN = _id"
+ " JOIN Writers w ON w._WriterID = wb._Writers_WriterID"
+ " WHERE b.BookTitle LIKE '%"
+ query
+ "%'"
+ " OR c.Category LIKE '%"
+ query
+ "%'"
+ " OR p.Publisher LIKE '%"
+ query
+ "%'"
+ " OR w.LastName LIKE '%"
+ query
+ "%'"
+ " OR _id LIKE '%"
+ query
+ "%'"
+ " GROUP BY b.BookTitle";
Cursor c = myDbHelper.rawQ(select);
startManagingCursor(c);
// the desired columns to be bound
String[] columns = new String[] { "Books.BookTitle",
"Publishers.Publisher" };
// the XML defined views which the data will be bound to
int[] to = new int[] { R.id.ISBN_entry, R.id.Title_entry };
// Getting results into our listview
try {
SimpleCursorAdapter mAdapter = new SimpleCursorAdapter(this,
R.layout.listlayout, c, columns, to);
this.setListAdapter(mAdapter);
} catch (Exception e) {
}
}
}
And your new database helper, DbManager:
DbManager.java
public class DbManager extends SQLiteOpenHelper {
private static final String DB_NAME = "BHCLibrary3.sqlite";
private static final String DB_PATH = "/data/data/gr.BHC.www/databases/";
private static final Integer DB_VERSION = 1;
private static final String TAG = "DbManager";
private final Context context;
private SQLiteDatabase db;
private DbManager dbManager;
public DbManager(Context context) {
super(context, DB_NAME, null, DB_VERSION);
this.context = context;
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE 'notes' (_id integer primary key autoincrement, title text not null);");
}
#Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
public DbManager open() {
dbManager = new DbManager(context);
db = dbManager.getWritableDatabase();
return this;
}
public void createNewDatabase() {
InputStream assetsDB = null;
try {
assetsDB = context.getAssets().open(DB_NAME);
OutputStream dbOut = new FileOutputStream(DB_PATH + DB_NAME);
byte[] buffer = new byte[1024];
int length;
while ((length = assetsDB.read(buffer)) > 0) {
dbOut.write(buffer, 0, length);
}
dbOut.flush();
dbOut.close();
assetsDB.close();
Log.i(TAG, "New database created...");
} catch (IOException e) {
Log.e(TAG, "Could not create new database...");
e.printStackTrace();
}
}
public Cursor rawQ(String select) {
return db.rawQuery(select, null);
}
}
Looks like the size of your database exceeds one MB. In that case you need to store it in the assets folder as .jpg and then copy it over. This is because Android places a restriction on the size of the text assets.