I'm creating a forum application and I currently if I delete a thread I'm deleting all threads.
Is there a good method or query to check if the UserId == ThreadId?
My current code:
public void deleteThread() {
SQLiteDatabase db = this.getWritableDatabase();
// Delete All Rows
db.delete(TABLE_THREAD, null, null);
db.close();
Log.d(TAG, "Deleted all Thread info from sqlite");
}
You need to pass correct value to the well-documented delete method to narrow down the scope of deletion to a subset of all entries in the DB table.
public void deleteThreadById(String threadId) {
SQLiteDatabase db = this.getWritableDatabase();
String whereClause = "threadId = " + threadId;
db.delete(TABLE_THREAD, whereClause, null);
db.close();
}
Deleting all threads of a given user via their userId would be similar but probably doesn't make sense in a forum software.
This is how SQL works in general and it's a bit scary you started development without familiarising yourself with the very basics.
Something like this;
public void deleteThread(String threadName) {
SQLiteDatabase db = this.getWritableDatabase();
try {
db.delete(MYDATABASE_TABLE, "name = ?", new String[]{threadName});
} catch (Exception e) {
e.printStackTrace();
} finally {
db.close();
}
}
Something long these lines, querying database to find the specific row that has column which matches the parameter.
For example to delete a row which the name column is "Hello World";
deleteThread("Hello World");
I have a small function for checking to see if a records already exists in my sqlite database. There is data in the database that should match the query, i have verified this by opening up the database.But i get an empty result.
Below is the function, it takes in a parameter and uses that as the search parameter. i have also verified that the parameter is correct.
public boolean checkParent(String email)
{
SQLiteDatabase db = this.getReadableDatabase();
Cursor res = null;
try
{
res = db.rawQuery("SELECT * FROM parents WHERE email = ' " + email + " ' ",null);
res.close();
}
catch (Exception ex)
{
Log.e("Error checking parent", ex.toString());
}
if(res == null)
{
return true;
}
else
{
return false;
}
}
Right way to pass argument in rawQuery method.
db.rawQuery("SELECT * FROM parents WHERE email = ?",new String[]{email});
You are checking whether the cursor object res is null. This will never happen; rawQuery() always returns a cursor object.
You have to check whether the cursor is empty, i.e., whether the cursor actually contains any rows. To do this, call a method like moveToFirst() and check if it succeeds.
Or even better, use a helper function that does handle the cursor for you:
public boolean checkParent(String email)
{
SQLiteDatabase db = this.getReadableDatabase();
long count = DatabaseUtils.queryNumEntries(db,
"parents", "email = ?", new String[]{ email });
return count > 0;
}
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
.....
does the Android system automatically close an open SQLite Database? I've implemented a singleton class for accessing data and sometimes the methods there needs to be run on a worker thread. I have a synch functionality with queries that run on the background, here's the code:
public ArrayList<ActualCall> getUnsentCalls(boolean forUnsentSignature) {
ArrayList<ActualCall> calls = new ArrayList<ActualCall>();
Cursor c;
open();
if (forUnsentSignature)
c = db.rawQuery("SELECT * FROM " + TXN_ACTUAL_CALL_COMPONENT
+ " WHERE sentFlag=1 and sigSentFlag=?", new String[] {"0"});
else
c = db.rawQuery("SELECT * FROM " + TXN_ACTUAL_CALL_COMPONENT
+ " WHERE sentFlag=? AND signature != ''", new String[] {"0"});
if (c.moveToFirst()) {
do {
if (!db.isOpen())
open();
ActualCall call = new ActualCall();
call.setAcpComponentId(c.getString(c.getColumnIndex("acpComponentId")));
call.setAcpDate(c.getString(c.getColumnIndex("acpDate")));
call.setAcpId(c.getString(c.getColumnIndex("acpId")));
call.setCallEndTime(c.getString(c.getColumnIndex("callEndTime")));
call.setCallStartTime(c.getString(c.getColumnIndex("callStartTime")));
call.setCoordinates(c.getString(c.getColumnIndex("coordinates")));
call.setDeviceTimestamp(c.getString(c.getColumnIndex("deviceTimestamp")));
call.setDoctorId(c.getString(c.getColumnIndex("doctorId")));
call.setSignature(c.getString(c.getColumnIndex("signature")));
call.setSigSentFlag(c.getString(c.getColumnIndex("sigSentFlag")));
call.setTimeSpent(c.getString(c.getColumnIndex("timeSpent")));
call.setSentFlag(c.getString(c.getColumnIndex("sentFlag")));
call.setCoordinates(c.getString(c.getColumnIndex("coordinates")));
calls.add(call);
} while (c.moveToNext());
}
c.close();
db.close();
return calls;
}
As you can see, I've already called open() on the initial lines yet I'm getting an error that tells that my database is already closed. Here's the stack trace:
java.lang.IllegalStateException: database
/data/data/com.***.****/databases/****.db (conn# 0) already closed
at android.database.sqlite.SQLiteDatabase.verifyDbIsOpen(SQLiteDatabase.java:2123)
at android.database.sqlite.SQLiteDatabase.lock(SQLiteDatabase.java:398)
at android.database.sqlite.SQLiteDatabase.lock(SQLiteDatabase.java:390)
at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:74)
at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:311)
at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:292)
at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:156)
at android.database.AbstractCursor.moveToFirst(AbstractCursor.java:233)
at com.***.****.datalayer.ESCAndroidDataLayer.getUnsentCalls(ESCAndroidDataLayer.java:5340)
at com.***.****.synchall.SynchAll$UnsentSignatureSender.run(SynchAll.java:1750)
Again, the method above is called on a Thread class, if that helps. I've found a link regarding SQLite being closed automatically but I don't understand his solution : http://darutk-oboegaki.blogspot.com/2011/03/sqlitedatabase-is-closed-automatically.html
Try to call db.acquireReference() before c.moveToFirst()
db.acquireReference();
if (c.moveToFirst()) {
or it may be sufficient simply to enclose db.close() with "if (db.isOpen())".
if (db.isOpen())
{
db.close();
}
I'm not so sure but these will change something.
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.