Disallow duplicate records and check if database is available - android

I am creating a database and I'm inserting a row like so:
/* Add two DataSets to the Table. */
myDB.execSQL("INSERT INTO "
+ MY_DATABASE_TABLE
+ " (LastName, FirstName, Country, Age)"
+ " VALUES ('Gramlich', 'Nicolas', 'Germany', 20);");
myDB.execSQL("INSERT INTO "
+ MY_DATABASE_TABLE
+ " (LastName, FirstName, Country, Age)"
+ " VALUES ('Doe', 'John', 'US', 34);");
I have two questions regarding the above code:
How do I disallow duplicate records? I want to check incoming data at the insert time. How can I achieve that? Should I use IF NOT EXISTS?
How can I check whether that database is available or not? So far, without any success, I've tried:
private static String DB_PATH = "/data/data/YOUR_PACKAGE/databases/myDBName";
private static String DB_NAME = "myDBName";
private boolean checkDataBase() {
SQLiteDatabase checkDB = null;
try {
checkDB = SQLiteDatabase.openDatabase(DB_PATH, null,
SQLiteDatabase.OPEN_READONLY);
checkDB.close();
} catch (SQLiteException e) {
// database doesn't exist yet.
}
return checkDB != null ? true : false;
}
Any suggestions on how to achieve this?

to prevent duplicates create a UNIQUE INDEX on that table.
At runtime, you have at least two options:
let the index give you an exception if you attempt a duplicate, then catch that exception and do something else, or
Query the table pro-actively to see if the new record exists, and if you find it you know there is a duplicate, so do something else.

Here are a few suggestions:
When creating your tables, define the columns that you want to prevent duplicates as UNIQUE.
Your checkDataBase() method is a bit strange. Have you considered using the SQLiteOpenHelper class in your implementation to simplify things?
http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html

Related

Android SQLite check if table exist [duplicate]

I have an android app that needs to check if there's already a record in the database, and if not, process some things and eventually insert it, and simply read the data from the database if the data does exist. I'm using a subclass of SQLiteOpenHelper to create and get a rewritable instance of SQLiteDatabase, which I thought automatically took care of creating the table if it didn't already exist (since the code to do that is in the onCreate(...) method).
However, when the table does NOT yet exist, and the first method ran upon the SQLiteDatabase object I have is a call to query(...), my logcat shows an error of "I/Database(26434): sqlite returned: error code = 1, msg = no such table: appdata", and sure enough, the appdata table isn't being created.
Any ideas on why?
I'm looking for either a method to test if the table exists (because if it doesn't, the data's certainly not in it, and I don't need to read it until I write to it, which seems to create the table properly), or a way to make sure that it gets created, and is just empty, in time for that first call to query(...)
EDIT
This was posted after the two answers below:
I think I may have found the problem. I for some reason decided that a different SQLiteOpenHelper was supposed to be created for each table, even though both access the same database file. I think refactoring that code to only use one OpenHelper, and creating both tables inside it's onCreate may work better...
Try this one:
public boolean isTableExists(String tableName, boolean openDb) {
if(openDb) {
if(mDatabase == null || !mDatabase.isOpen()) {
mDatabase = getReadableDatabase();
}
if(!mDatabase.isReadOnly()) {
mDatabase.close();
mDatabase = getReadableDatabase();
}
}
String query = "select DISTINCT tbl_name from sqlite_master where tbl_name = '"+tableName+"'";
try (Cursor cursor = mDatabase.rawQuery(query, null)) {
if(cursor!=null) {
if(cursor.getCount()>0) {
return true;
}
}
return false;
}
}
I know nothing about the Android SQLite API, but if you're able to talk to it in SQL directly, you can do this:
create table if not exists mytable (col1 type, col2 type);
Which will ensure that the table is always created and not throw any errors if it already existed.
Although there are already a lot of good answers to this question, I came up with another solution that I think is more simple. Surround your query with a try block and the following catch:
catch (SQLiteException e){
if (e.getMessage().contains("no such table")){
Log.e(TAG, "Creating table " + TABLE_NAME + "because it doesn't exist!" );
// create table
// re-run query, etc.
}
}
It worked for me!
This is what I did:
/* open database, if doesn't exist, create it */
SQLiteDatabase mDatabase = openOrCreateDatabase("exampleDb.db", SQLiteDatabase.CREATE_IF_NECESSARY,null);
Cursor c = null;
boolean tableExists = false;
/* get cursor on it */
try
{
c = mDatabase.query("tbl_example", null,
null, null, null, null, null);
tableExists = true;
}
catch (Exception e) {
/* fail */
Log.d(TAG, tblNameIn+" doesn't exist :(((");
}
return tableExists;
Yep, turns out the theory in my edit was right: the problem that was causing the onCreate method not to run, was the fact that SQLiteOpenHelper objects should refer to databases, and not have a separate one for each table. Packing both tables into one SQLiteOpenHelper solved the problem.
// #param db, readable database from SQLiteOpenHelper
public boolean doesTableExist(SQLiteDatabase db, String tableName) {
Cursor cursor = db.rawQuery("select DISTINCT tbl_name from sqlite_master where tbl_name = '" + tableName + "'", null);
if (cursor != null) {
if (cursor.getCount() > 0) {
cursor.close();
return true;
}
cursor.close();
}
return false;
}
sqlite maintains sqlite_master table containing information of all tables and indexes in database.
So here we are simply running SELECT command on it, we'll get cursor having count 1 if table exists.
You mentioned that you've created an class that extends SQLiteOpenHelper and implemented the onCreate method. Are you making sure that you're performing all your database acquire calls with that class? You should only be getting SQLiteDatabase objects via the SQLiteOpenHelper#getWritableDatabase and getReadableDatabase otherwise the onCreate method will not be called when necessary. If you are doing that already check and see if th SQLiteOpenHelper#onUpgrade method is being called instead. If so, then the database version number was changed at some point in time but the table was never created properly when that happened.
As an aside, you can force the recreation of the database by making sure all connections to it are closed and calling Context#deleteDatabase and then using the SQLiteOpenHelper to give you a new db object.
Kotlin solution, based on what others wrote here:
fun isTableExists(database: SQLiteDatabase, tableName: String): Boolean {
database.rawQuery("select DISTINCT tbl_name from sqlite_master where tbl_name = '$tableName'", null)?.use {
return it.count > 0
} ?: return false
}
public boolean isTableExists(String tableName) {
boolean isExist = false;
Cursor cursor = db.rawQuery("select DISTINCT tbl_name from sqlite_master where tbl_name = '" + tableName + "'", null);
if (cursor != null) {
if (cursor.getCount() > 0) {
isExist = true;
}
cursor.close();
}
return isExist;
}
no such table exists: error is coming because once you create database with one table after that whenever you create table in same database it gives this error.
To solve this error you must have to create new database and inside the onCreate() method you can create multiple table in same database.
Important condition is IF NOT EXISTS to check table is already exist or not in database
like...
String query = "CREATE TABLE IF NOT EXISTS " + TABLE_PLAYER_PHOTO + "("
+ KEY_PLAYER_ID + " TEXT,"
+ KEY_PLAYER_IMAGE + " TEXT)";
db.execSQL(query);
i faced that and deal with it by try catch as simple as that i do what i want in table if it not exist will cause error so catch it by exceptions and create it :)
SQLiteDatabase db=this.getWritableDatabase();
try{
db.execSQL("INSERT INTO o_vacations SELECT * FROM vacations");
db.execSQL("DELETE FROM vacations");
}catch (SQLiteException e){
db.execSQL("create table o_vacations (id integer primary key ,name text ,vacation text,date text,MONTH text)");
db.execSQL("INSERT INTO o_vacations SELECT * FROM vacations");
db.execSQL("DELETE FROM vacations");
}
.....
Toast t = Toast.makeText(context, "try... " , Toast.LENGTH_SHORT);
t.show();
Cursor callInitCheck = db.rawQuery("select count(*) from call", null);
Toast t2a = Toast.makeText(context, "count rows " + callInitCheck.getCount() , Toast.LENGTH_SHORT);
t2a.show();
callInitCheck.moveToNext();
if( Integer.parseInt( callInitCheck.getString(0)) == 0) // if no rows then do
{
// if empty then insert into call
.....

android content provider - first run Activity Splash screen while filling the database in the content provider from raw data

In my application I have a content provider which uses a database for the content provided. When the database is created the first time it needs to be filled from the raw content of a Json file. My idea was that i trigger this filling of the database at onCreate of my SQLiteOpenHelper subclass. This works fine yet I am not sure how to handle the the communication between application and content provider when the app is running the first time. Basically i would like to show some sort of a splash screen while the database is filled. Yet how does the application get informed that
the content provider is busy filling the database when running the first time
the content provider is ready to go
Surely I could fill the database from the application by calling the content provider with each dataset yet I would prefer doing it within the sphere of the content provider so that the application does not have to handle the reading of the json file etc. Besides design preferences it would also enable the content provider to fill the database more efficiently because it would have the whole dataset at once. I have a feeling this is not possible yet I hope I miss some simple point.
Any suggestions how to achieve this would be highly appreciated.
Thanks
martin
When using a content Provider i would presume that your using a DBHelper class to manage the creation of the database. Below is the code from the android notes example project.
This shows how the DBHelper constructor is intelligent enough to determine if the database has been created before. In the createDatabase method i would subsequently call a method to pre-populate the database, from as you say a json file.
The problem is that this doesn't really allow you to communicate to the Activity that your database hasn't been initialised.
One thought could be that you use SharedPreferences to store the fact you've populated the database. You could then check the sharedPreference in the activity on startup, Call the content provider to populate the database and then store in the shared preference that you've done this task already.
Just be aware that i'm not sure if the sharedPreferences maintain the same state as the database if you for example erase the data from the android settings menu. You'd need to check that.
http://code.google.com/p/android-notes/source/browse/trunk/src/com/bitsetters/android/notes/DBHelper.java?r=10
public class DBHelper {
private static final String DATABASE_NAME = "notes";
private static final String TABLE_DBVERSION = "dbversion";
private static final String TABLE_NOTES = "notes";
private static final int DATABASE_VERSION = 1;
private static String TAG = "DBHelper";
Context myCtx;
private static final String DBVERSION_CREATE =
"create table " + TABLE_DBVERSION + " ("
+ "version integer not null);";
private static final String NOTES_CREATE =
"create table " + TABLE_NOTES + " ("
+ "id integer primary key autoincrement, "
+ "note text, "
+ "lastedit text);";
private static final String NOTES_DROP =
"drop table " + TABLE_NOTES + ";";
private SQLiteDatabase db;
/**
*
* #param ctx
*/
public DBHelper(Context ctx) {
myCtx = ctx;
try {
db = myCtx.openOrCreateDatabase(DATABASE_NAME, 0,null);
// Check for the existence of the DBVERSION table
// If it doesn't exist than create the overall data,
// otherwise double check the version
Cursor c =
db.query("sqlite_master", new String[] { "name" },
"type='table' and name='"+TABLE_DBVERSION+"'", null, null, null, null);
int numRows = c.getCount();
if (numRows < 1) {
CreateDatabase(db);
} else {
int version=0;
Cursor vc = db.query(true, TABLE_DBVERSION, new String[] {"version"},
null, null, null, null, null,null);
if(vc.getCount() > 0) {
vc.moveToFirst();
version=vc.getInt(0);
}
vc.close();
if (version!=DATABASE_VERSION) {
Log.e(TAG,"database version mismatch");
}
}
c.close();
} catch (SQLException e) {
Log.d(TAG,"SQLite exception: " + e.getLocalizedMessage());
} finally {
db.close();
}
}
private void CreateDatabase(SQLiteDatabase db)
{
try {
db.execSQL(DBVERSION_CREATE);
ContentValues args = new ContentValues();
args.put("version", DATABASE_VERSION);
db.insert(TABLE_DBVERSION, null, args);
db.execSQL(NOTES_CREATE);
// Populate with data
populateDataBaseFromFile();// There are probably better ways to do this.
setSharedPreferenceYouPopulatedDB();
} catch (SQLException e) {
Log.d(TAG,"SQLite exception: " + e.getLocalizedMessage());
}
}
Personally I wouldn't bother with the splash screen, unless you really needed to.
Another thought might be to:
Write in the db helper a method to determin if your tables exist. Return false if not.
In startup activity call ContentProvider with a request that calls the DBHelper test method.
If false then display splash screen and then call Content Provider to populate DB.
If true, then carry on as normal.

Most efficient way to access/write data to SQLite on Android

Currently I'm using ContentProvider in my application. Because of "layers" and no actual need for provider - I'm working on optimizing data access as much as possible. Here is my attempt to do this:
public static String getPreferenceString(Context context, String key)
{
DatabaseHelper helper = new DatabaseHelper(context);
SQLiteDatabase database = helper.getReadableDatabase();
SQLiteStatement statement = database.compileStatement("SELECT Value FROM Preferences WHERE Key='" + key + "' LIMIT 1");
try
{
return statement.simpleQueryForString();
}
catch (Exception ex)
{
return "";
}
finally
{
statement.close();
database.close();
helper.close();
}
}
public static void setPreferenceString(Context context, String key, String value)
{
DatabaseHelper helper = new DatabaseHelper(context);
SQLiteDatabase database = helper.getReadableDatabase();
SQLiteStatement statement = database.compileStatement("INSERT OR REPLACE INTO Preferences (Key, UpdatedOn, Value) VALUES ('" +
key + "', '" +
Utility.getDateConvertedToUTCDBString(new Date()) + "', '" +
value + "'); ");
try
{
statement.execute();
}
finally
{
statement.close();
database.close();
helper.close();
}
}
Is that about as close as I can get to direct calls to SQLite?
Should I have all this .close() statements in my code?
In setPreferenceString I did copy/paste and called getReadableDatabase even though I write data and it works. Why?
Is that about as close as I can get to direct calls to SQLite?
AFAIK SQL queries are closest you can go against RDBs
Should I have all this .close() statements in my code?
Personally, I would not create a DatabaseHelper, an SQLiteDatabase, and an SQLiteStatement each time I call that method. I would create all this just before you need them, and close them when no needed anymore. Also centralizing this is a good idea IMHO (using a singleton, for example).
Also your SQL statement could be written like
SELECT Value FROM Preferences WHERE Key= ? LIMIT 1
This way you only have to prepare it once and bind parameters as you need the statement. Same goes for any SQL query.

Problem with SQLite onCreate() in Android

I'm extending the SQLiteOpenHelper class to help me connect and do my database work. According to the documentation, the OnCreate method should only be called if the database has not been created. Yet, my problem is that I am getting this error when I try to execute a query to insert a record.
ERROR/Database(214): Failure 1 (table Sample already exists) on 0x218688 when preparing
'CREATE TABLE Sample (RecId INT, SampleDesc TEXT);'.
The only place this Create query is used in code is the OnCreate method which looks like this.
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(SAMPLE_TABLE_CREATE);
}
Note: I'm following a tutorial - the only thing I've done different is make the SQLiteDatabase object public instead of private so that I could extend this class for each entity, letting the public SQLiteDatabase object from the inherited DataHelper class do all the work
Here is the method that makes the call that fails.
//This method is in the class that extends DataHelper (See note on tutorial)
public void createSample(Sample sample)//next action form
{
String id = sample.getId();
String name = sample.getSummary();
String query = "INSERT INTO " + SAMPLE_TABLE_NAME + "( " + SAMPLE_Id + "," +
SAMPLE_NAME + ") " + " VALUES (" + id + "," + name + ")";
try{
data.rawQuery(query, null);
}
catch(SQLException e){
Log.i("Sample", "Errors: Sample LN60: " + e.getMessage());
}
}
Can someone tell me what I'm doing wrong? Or maybe a hack (i.e. check if table exists before executing create statement)
Please let me know what other code I can post to solve this...
Is it due to you've execute it your activity once and never destroy the DB after that?
And 2nd run you'd hit this error.
Database is stored in /data/data/YOUR_PACKAGE/databases/, so a workaround would be to check if the DB exists here before creating it.
//The Android's default system path of your application database.
private static String DB_PATH = "/data/data/YOUR_PACKAGE/databases/";
private static String DB_NAME = "myDBName";
SQLiteDatabase checkDB = null;
String myPath = DB_PATH + DB_NAME;
checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
if(checkDB){
//do nothing
}else{
//create DB
}
Code source here
The first error is quite simply because you are creating a table that already exists, so yes adding a check if the table exists prior to creating it would be good. Once an SQLite dB is created or made it will stay until someone or something deletes it, unlike the default onCreate() call which resembles re-creating or drawing your screen.
every time you call getWritableDatabase() onCreate() method is called.

SQLiteOpenHelper onUpgrade() Confusion Android

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;
}

Categories

Resources