I am learning to use Sqlite on Android by using this tutorial. I am having trouble understanding some of the code.
public DatabaseHandler(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
// Creating Tables
#Override
public void onCreate(SQLiteDatabase db) {
String CREATE_CONTACTS_TABLE = "CREATE TABLE " + TABLE_CONTACTS + "("
+ KEY_ID + " INTEGER PRIMARY KEY," + KEY_NAME + " TEXT,"
+ KEY_PH_NO + " TEXT" + ")";
db.execSQL(CREATE_CONTACTS_TABLE);
}
I make a new object of DatabaseHandler in my activity. The super in the constructor is SQLiteOpenHelper constructor. The code works great, it creates a new database, if there is none and it uses the old one if it exists. I would like to make some changes to this code(I want to add different tables to one db), but I dont understand how exactly does this work, how does the constructor know if it should create a new db or use the existing one?
The trick is that your class extends SQLiteOpenHelper, the call to super in your constructor triggers a lot of behind-the-scenes code.
If you read through the SQLiteOpenHelper source code you'll see that getWritableDatabase() and getReadableDatabase() call the same method: SQLiteOpenHelper#getDatabaseLocked(). This method does most of the work. It is the one that determines whether a database needs to be created, opened, upgraded, or anything else, from the information that you supplied in one simple command: super(context, DATABASE_NAME, null, DATABASE_VERSION);.
When you want to open your db, you call SQLiteOpenHelper.getReadableDatabase() or SQLiteOpenHelper.getWriteableDatabase(). If the database exists, these return a handle to it. If it doesn't exist, the system calls onCreate(), where you do the db.execSQL.
It would be better to call the class DatabaseOpenHelper instead of DatabaseHandler. What it does is defer creating the database until its needed, instead of creating it at the beginning of some other class. This is particularly useful for content providers backed by an SQLite database. You should use ContentProvider.onCreate() to do very quick initialization, and then implement SQLiteOpenHelper.onCreate() to create the database. That way, the system can load your ContentProvider when your app starts, but it doesn't have to do anything with your database until something tries to access it.
Related
I was reading this tutorial: http://developer.android.com/training/basics/data-storage/databases.html and it raised a lot of questions.
This is my code I made while following the tutorial:
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
public final class SongDbHelper extends SQLiteOpenHelper {
// If you change the database schema, you must increment the database version.
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "Song.db";
private static final String TEXT_TYPE = " TEXT";
private static final String PRIMARY_KEY_TYPE = " INTEGER PRIMARY KEY";
private static final String COMMA_SEP = ",";
private static final String SQL_CREATE_ENTRIES =
"CREATE TABLE " + SongEntry.TABLE_NAME + " (" +
SongEntry._ID + PRIMARY_KEY_TYPE + COMMA_SEP +
SongEntry.COLUMN_NAME_SONG_TITLE + TEXT_TYPE + COMMA_SEP +
SongEntry.COLUMN_NAME_ENGLISH_LYRICS + TEXT_TYPE + COMMA_SEP +
SongEntry.COLUMN_NAME_ROMAJI_LYRICS + TEXT_TYPE + COMMA_SEP +
SongEntry.COLUMN_NAME_KANJI_LYRICS + TEXT_TYPE + COMMA_SEP +
SongEntry.COLUMN_NAME_INFO + TEXT_TYPE +
" )";
private static final String SQL_DELETE_ENTRIES =
"DROP TABLE IF EXISTS " + SongEntry.TABLE_NAME;
public SongDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE_ENTRIES);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// This database is only a cache for online data, so its upgrade policy is
// to simply to discard the data and start over
db.execSQL(SQL_DELETE_ENTRIES);
onCreate(db);
}
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
onUpgrade(db, oldVersion, newVersion);
}
public static abstract class SongEntry implements BaseColumns {
public static final String TABLE_NAME = "Song";
public static final String COLUMN_NAME_SONG_TITLE = "song_title";
public static final String COLUMN_NAME_ENGLISH_LYRICS = "english_lyrics";
public static final String COLUMN_NAME_ROMAJI_LYRICS = "romaji_lyrics";
public static final String COLUMN_NAME_KANJI_LYRICS = "kanji_lyrics";
public static final String COLUMN_NAME_INFO = "info";
}
}
Are we supposed to call SongDbHelper.onCreate() every time the app opens up? (I'm imagining putting it in my main activity's onCreate() method.
If you call SongDbHelper.onCreate() more than once, will it recreate the table, or will SongDbHelper.onCreate() just fail silently and gracefully?
Why does the app need to call SongDbHelper.onCreate() anyways? If I put my app on the app store, won't I just let people download the database that I already filled with data?
When/where in my app do I call SongDbHelper.onUpgrade()? I assume this happens when the user notices that "There is an update available for this app. Click here to download it" or something like that. I'm just confused, because I'd imagine the user simply downloads a .db file with the database intact; and therefore won't need to call SongDbHelper.onCreate().
If the app has to call SongDbHelper.onCreate(), how does it fill the database with values? I think it would defeat the purpose of a database if I also have code that fills in the values, because then the values would be duplicated (in the code and the database).
What is the best way to fill a database with values? With the terminal, or programmatically in a separate program from the app, or only in the Android app?
Answer for :
Point 1,2,3 : As simply mentioned in tutorial you are following at this link, we are not supposed to call SongDbHelper.onCreate(). Instead of it, when we want to have reference of database from Helper class, we use the CONSTUCTOR like :
SongDbHelper mDbHelper = new SongDbHelper(getContext());
// this will call super method internally and
// this will create table in database
Point 4 : onUpgrade() is also not responsibility of ours to call explicitely. When we change database schema, we update DATABASE_VERSION and android framework will internally call onUpgrade() for us
Point 5 : You can store song lyrics one by one from your xml to database. This is right way as far as i know
Update :
The best way would be to store your database on a web server which is made prior, download it from the web server and have the app then read/write into database. This will not defeat purpose of database, in addition it will not duplicate database entries and code entries (xml). In addition to that, your app size will also be smaller because application will download database in runtime, rather than storing in device memory initially
Your SongDbHelper class should be implemented as Singleton instance.
It could be a good practice instantiate it in your Application class and exposing the SongDbHelper object for all your App
public class YourApp extends Application {
public SongDbHelper songDBHelper;
#Override
public void onCreate() {
super.onCreate();
songDBHelper = new SongDbHelper(.....);
}
public SongDBHelper getSongDB() {
return songDBHelper;
}
}
Firstly, I would recommend you to read the tutorial again to understand it. Now moving on to your questions,
1) You are not going to call onCreate() ever. As you can see, it comes from the super class SQLiteOpenHelper and it is called by the system and not the you when the app installs for the first time.
2) So from the previous question you can understand that there is not question of calling the onCreate() more than once. It will be called by the system only once to create the database when the user installs the app for the first time.
3) It doesn't work in this way as you are saying. When the app installs the onCreate() will be called to create the database. After that you can use your logic to fill data into it. No pre-defined data will be downloaded from anywhere as you said.
4) No, its also not like that "whenever the users get an update". onUpgrade() is only called when the database version changes. Like if you want to change the schema of the database on an app already existing in the Play Store, you can change the version number and perform the upgrade appropriately in the onUpgrade().
5) Your question is not clear.
I hope your conception will be changed after reading this answer. Do read the tutorials again to get a proper concept or post a new question if you have further questions.
UPDATE
5) You are thinking to create a database with song lyrics from before and put it inside your app. This is why you are having a misconception with onCreate().
See, when the user installs the app for the first time, the onCreate() will be called and a database with the given schema will be created. There would be no content inside it.
To put something inside it, you need to insert rows into that table programmatically. Hope it doesn't defeats the purpose now.
6) Now as for your use-case.
According to me, the best solution would be to put all your song lyrics in your web server and fetch those lyrics inside your app and put it in the database.
When the user install the app and runs for the first time, you should query the web server to check for the available lyrics and then fetch them one by one and put them inside your database using insert() method.
NOTE - the onCreate() just creates a blank table and doesn't put any values into it. But you are programmatically putting content inside your database, like the song lyrics using the insert() method.
Now when ever some new songs come up, just put the lyrics in your server and the app will automatically query the server to check if any new song lyrics is available and download it and insert in into the database accordingly.
Hope it is now clear to you.
I have created DatabaseHelper to make my development easier. If my Database version is upgrade from 1 to 2, onUpgrade will called and table will be dropped. I wonder will this cause my data stored in table dropped too? I means, will I lost my data stored in table?
Here's the code.
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
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 notes");
onCreate(db);
}
}
By looking at your code, Yes you will lose the current table of the database and a new empty table notes will be created.
So the best way to upgrade the database is before this:
db.execSQL("DROP TABLE IF EXISTS notes");
statement, you should made a backup of your current database (easiest way is to rename the table) and then drop the current table to create the new one.
It is always a good programming practice to keep a backup of information before editing/upgrading it.
When you drop a table all it's data is deleted too. If you want to keep it, rename the table first, create the new one and then select the data from the old into the new one.
You can alter table in onUpgrade method if there is change in coloumn. If you want to make fresh table then only call Drop inside onUpgrade, if there is no change in schema dont do anything inside onUpgrade method.
Is it better to have a single big SQLiteOpenHelper subclass that defines onCreate and onUpgrade methods for every table in the database, or is better to have many SQLiteOpenHelper subclasses, one for each table?
Is there a best practice? Or are both acceptable, but with different good and bad side effects?
You should have a single SQLiteOpenHelper class for all the tables. Check this link.
Just for the sake of a different approach:
You can always overried on the onOpen(..) method have it called your onCreate(..) . Be sure to use the "CREATE TABLE IF NOT EXISTS..." statement rather than "CREATE TABLE"
#Override
public void onOpen(SQLiteDatabase db) {
onCreate(db);
}
#Override
public void onCreate(SQLiteDatabase db) {
String CREATE_FRIENDS_TABLE = "CREATE TABLE IF NOT EXISTS ...";
db.execSQL(CREATE_FRIENDS_TABLE);
}
You do that with every class that extends from SQLiteOpenHelper
#TheReader is right. I prefer a single SQLiteOpenHelper for all tables, here is what i do: pass a List of "table creation" sqls to the Constructor of the SQLiteOpenHelper subClass, then in the onCreate function iterate the list to create each table.
so my SQLiteOpenHelper subclass looks sth like this:
public ModelReaderDbHelper(Context context, List<String> createSQLs, List<String> deleteSQLs){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
this.TABLE_CREATION_SQLS = createSQLs;
this.TABLE_DELETE_SQLS = deleteSQLs;
}
#Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
for(String oneCreation : TABLE_CREATION_SQLS){
sqLiteDatabase.execSQL(oneCreation);
}
}
But that comes another problem: after adding a new table, and install the new version of the app with an existing old one installed, the new table won't be created, because the existence of the old database will prevent the onCreate function from being called. So user has to uninstall the app first, and install the app completely. The DATABASE_VERSION helps, it seem android will not execute the onCreate function if and only if the a existin database with the same name and the same DATABASE_VERSION
I'm using SQLite to create a database table for my app. I've searched online but haven't found the answer yet: Do I need to create a database first or is there a default database for each app?
I've written the following DBHelper class. It's in a separate file. How Do I call it when the app starts?
public class DataBaseHelper extends SQLiteOpenHelper{
final String CREAT_TABLE = "CREATE TABLE IF NOT EXIST `employee` ("+
"`id` int(11) NOT NULL AUTO_INCREMENT,"+
"`firstName` varchar(30) NOT NULL,"+
"`lastName` varchar(30) NOT NULL,"+
"PRIMARY KEY (`id`)"+
") ;";
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREAT_TABLE);
}
}
You don't have to create the database yourself.
You specify the database name (the sqlite file name) when you call the superconstructor of your open helper.
This will create or update the database with that name when needed (depending on which version number you send in vs. the current version number meta data).
I don't know what your constructor looks like but let's say it looks like
public DatabaseHelper(final Context context) {
super(context, "mydatabase", null, 0);
}
Then the SQLiteOpenHelper will create a database named "mydatabase" when you call getReadableDatabase() or getWritableDatabase(). It's all in the docs.
You declare your database name in the constructor
public DataBaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
I am writing an app that displays fun-facts (and the source they are from). The user can browse through the facts one by one.
Here is the design I thought of :
Create a table in SQLiteDatabase with a text column that stores the fun-fact and a second column that stores it's source. (not sure if this is the best way to go about doing it, but I want the app available even without the network)
My question is, when database is initially created on the device, should I manually populate the database from within the code, something like this pseodo-code:-
#Override
public void onCreate(SQLiteDatabase db) {
//Create the table
//Populate the table
//Insert statement 1
//Insert statement 2
//Insert statement 3
...
//Insert statement 500
}
Surely there must be a better method to create the initial database when the app is installed?
Are you certain that you really need a databse? Doesn't it just add unnecessary overhead to something so trivial?
Can't you just declare the array in your code, or am I missing something? Whether it's in the db or your code, it is taking up space. The db will add some overhead to that and vious?will take some time to load, plus your code has to handle errors, etc.
Woudl you not be better off with a simple array declared in your code? Or am I misisng something obvious? (maybe users can d/l a new db? But is that so much more overhead than d/ling a new program?)
If I'm way off, please explain (rather than downvoting). I am trying to help
Edit: presumably you already have your facts soemwhere? Maybe in a text file? You could just write code to parse that and initialze and array (or populate a db). It should bascially be a short for loop.
use a class derived from SQLiteOpenHelper
i already wrote sth obout this on my blog www.xenonite.net
public class myDatabase extends SQLiteOpenHelper
{
private static final String DB_NAME = "database.db";
private static final int DB_VERSION = 1;
public MyDatabase(Context context)
{
super(context, DB_NAME, null, DB_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db)
{
db.execSQL("CREATE TABLE tbl_test ( id INTEGER PRIMARY KEY AUTOINCREMENT, test TEXT NOT NULL )");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
db.execSQL("DROP TABLE IF EXISTS tbl_test");
onCreate(db);
}
}
you can use this like
myDatabase db = new myDatabase(getApplicationContext());
sql = "INSERT INTO tbl_test (test) VALUES ('xyz')";
db.getWritableDatabase().execSQL(sql);
String sql = "SELECT id FROM tbl_test";
Cursor result = db.getWritableDatabase().rawQuery(sql, null);
int value;
while(result.moveToNext())
{
value = result.getInt(0);
}
on every call to db, myDatabase.onCreate() is called, so your database will be created on the first run. when changing the table structure, implement onUpgrade() and increment DB_VERSION