Is this bad database coding? - android

In my DatabaseHelper class that extends SQLiteOpenHelper I've set various methods to return Cursors to my other Activities so that I don't perform any queries within any other class except DatabaseHelper. In those methods I don't close the Cursor or database afterwards, and I return it like:
public Cursor getCoursesLeft()
{
// Open a readable database.
SQLiteDatabase database = this.getReadableDatabase();
// Query the database and return the cursor.
return database.query(DEGREE_PLAN_TABLE, null, DEGREE_COLUMN_TAKEN + " = ?",
new String[] { "0" }, null, null, DEGREE_COLUMN_CLASS_NAME + " COLLATE NOCASE");
}
From whichever Activity I call the method from, I do ensure to close the Cursor that's returned after I use it.
Since Cursor is an Object, it should pass by reference, correct? So closing it from the other Activity should close the original object, and if I understand it correctly closing the Cursor also closes the database.
Is this a bad coding practice?
It seems like randomly I'll get a LogCat error saying close was never called on the database and the only thing I can find in my code that might be the reason is how I return the Cursors in those methods.

and if I understand it correctly closing the Cursor also closes the
database.
That does not sound quite right. You have to explicitly close the database after you've closed all cursors. The logcat errors are due to you not closing the databse and probably attempting to open another instance of it.
The order is important, cursors first, then the DB instance.
<bad joke in 3.. 2.. 1...>
The rest does not sound like any bad practice, when you gotta db it you just gotta db it. :D
[EDIT]: You said you've done this:
public Cursor getCoursesLeft()
{
// Open a readable database.
SQLiteDatabase database = this.getReadableDatabase();
^^^ here you're creating a new instance of the db
which means the db is opened for reading, and the scope of this variable
is lost outside this function. This means you can not close this instance explicitly
// Query the database and return the cursor.
return database.query(DEGREE_PLAN_TABLE, null, DEGREE_COLUMN_TAKEN + " = ?",
new String[] { "0" }, null, null, DEGREE_COLUMN_CLASS_NAME + " COLLATE NOCASE");
}
Instead have a database variable that you can access outside this method and close it once you're done working with the Cursor (and you've closed the cursor)
SQLiteDatabase database;
public Cursor getCoursesLeft()
{
// Open a readable database.
database = this.getReadableDatabase();
// Query the database and return the cursor.
return database.query(DEGREE_PLAN_TABLE, null, DEGREE_COLUMN_TAKEN + " = ?",
new String[] { "0" }, null, null, DEGREE_COLUMN_CLASS_NAME + " COLLATE NOCASE");
}
public void someOtherFunction() {
Cursor blah = getCoursesLeft();
// do something with blah
blah.close();
database.close();
}

Not closing a cursor just causes memory leaks. Closing a database is different.
Closing a cursor is like closing a particular connection to certain .file files generated when the cursor is created.
Hence you should ALWAYS close your cursor.
Is this bad coding?
No, and yea. Don't let your Activity mess around with those temp files. While nothing will happen it just doesn't seem nice

Related

Android - Efficient way using the Database Cursor

I've implemented a sqlite database in my application and I'm using the Android Cursor. I've written a database class with e.g. the database name and the table and column names. Here I also have various methods, like the following:
public Cursor getCorrectQuestions(int topic) {
SQLiteDatabase db = getReadableDatabase();
Cursor questionCursor = db.rawQuery(
"Select * FROM Result, Question WHERE Result.qid = Question._id AND correct = 1 AND topic = " + topic,
null);
questionCursor.moveToFirst();
return questionCursor;
}
public Cursor getExamQuestions() {
SQLiteDatabase db = getReadableDatabase();
Cursor questionCursor = db.rawQuery("Select * FROM Question WHERE topic = 7", null);
questionCursor.moveToFirst();
return questionCursor;
}
public Cursor getAnswerItems(String id) {
SQLiteDatabase db = getReadableDatabase();
Cursor answerCursor = db.rawQuery(
"Select * FROM Answer, Question WHERE Question._id = " + id + " AND Question._id = Answer.qid", null);
answerCursor.moveToFirst();
return answerCursor;
}
public Cursor getUserResults(String qid) {
SQLiteDatabase db = getReadableDatabase();
Cursor userResultsCursor = db.rawQuery("SELECT result FROM Result, Answer WHERE Result.qid = " + qid, null);
userResultsCursor.moveToFirst();
return userResultsCursor;
}
In the QuizActivity which has 3 cursors (answerCursor, questionCursor, userResultCursor) I call these methods.
My question is: is it necessary to create a SQLiteDatabase Object in every method or is it possible to define this once in my database constructor? And do I need 3 different cursors in my activity or is there a better way to handle this?
Assuming the methods you have written are part of a SQLiteOpenHelper, you are not really creating 3 database objects. Only the first call to getReadableDatabase() actually creates a database object, and subsequent calls reuse the same object over again.
You also need to make a new Cursor for each query you perform, as they cannot be edited after creation. In this sense, there is no way to simplify what you have already done.
As far as improvements to your code, there are a few things you can look at:
Consider putting your database in a ContentProvider and accessing it via URI's. This will require more upfront work, but will make it much easier if you want to share your database with other apps or sync your data to a server in the future.
Leave the cursor in its default position (don't call moveToFirst()). That way when the caller receives the cursor, it can use the following code to start iterating cursor rows without performing any further checks:
while (cursor.moveToNext()) {
// extract data
}
This is because the cursor returned from a query is initially positioned before the first row of data, so if the cursor is empty then the code inside the while loop simply never executes at all.

Android Close dataBase Failed

with simple below code i get this error for close database or cursor:
Database﹕ close() was never explicitly called on database '/data/data/ir.tsms/databases/tsms'
android.database.sqlite.DatabaseObjectNotClosedException:
My Function:
public Boolean searchLastID( Long lastID){
SQLiteDatabase db = this.getReadableDatabase();
String selectQuery = "SELECT * FROM " + this.RECEIVE_FIELDS_TABLE + " WHERE lastId = ?" ;
String[] args = {String.valueOf(lastID)};
Cursor cursor = db.rawQuery(selectQuery, args);
//db.close();
return cursor.moveToFirst();
}
after uncommenting db.close();
Cursor﹕ Finalizing a Cursor that has not been deactivated or closed. database = /data/data/ir.tsms/databases/tsms, table = null, query = SELECT * FROM ReceiveFields WHERE lastId = ?
whats problem and how to resolve that? i can't find any document about this problem. Thanks
make your cursor a global variable
and inside your onDestory Method close your cursor and your database
#Override
protected void onDestroy() {
super.onDestroy();
cursor.close();
db.close();
}
As soon as you're done retrieving the data of your query from your database you should close the Cursor or at least deactivate it. (Keyword: Freeing resources)
Following two quotes from the Docs on the close() and deactivate() method:
#close()
Closes the Cursor, releasing all of its resources and making it
completely invalid. Unlike deactivate() a call to requery() will not
make the Cursor valid again.
#deactivate()
Deactivates the Cursor, making all calls on it fail until requery() is
called. Inactive Cursors use fewer resources than active Cursors.
Calling requery() will make the cursor active again.

Quering SqlLite database where clause Android

Hi I am developing an android app.I am trying to query from the database. I need to fetch everything from the table TASK where dbDate = AlarmDate and dbdTime = AlarmTime.
c = db.rawQuery("SELECT * FROM TASK WHERE dbDate = '"+AlarmDate+"' AND dbTime= '"+Alarmtime+"'", null);
The problem is ,the cursor c is null.
I am not sure where I am going wrong in the query. Please Help.
Thanks!
Android has binding method to avoid sql inject. You can use the second parameter to provide the variables of SQL.
Cursor cur = db.rawQuery("SELECT * FROM TASK WHERE dbDate = ? AND dbTime = ? ", new String[]{AlarmDate, AlarmDate});
Going by your comment 'I have used db = openOrCreateDatabase("Globus", 0, null); where Globus is the db name', you are not using SQLite properly with android.
What you should be doing is creating class which extends SQLiteOpenHelper, then make sure you override the onCreate and onUpgrade methods, these are the methods where you create tables and make changes, it has been said a hundred times on here so I will provide a link to a tutorial: http://www.codeproject.com/Articles/119293/Using-SQLite-Database-with-Android
When you do database operations, on the class call getWritableDatabase (http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html#getWritableDatabase())
I say call getWritableDatabase because that way you don't need to worry if you can write to it, a writable database is also readable. Just FYI. Ask away for more details.
This should be the process of reading (writing is the same, just use what method you want instead of query):
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.beginTransaction();//this should lock the tables you are reading
Cursor c = db.rawQuery("select 1 where 1=?", new String[]{"1"});
if(c.moveToFirst()){
do{
//Do what you want with the row
}while(c.moveToNext());
}
c.close();
db.setTransactionSuccessful();
db.endTransaction();
db.close();
Here is the source code of a database helper I wrote, maybe it will help, read through it, understand how it works. https://bitbucket.org/FabianCCook/dbhelper/src/af7a8eba8d1a3f139e4170bbef9f1a2d3fdf1b47/src/nz/smartlemon/DatabaseHelper/ApplicationDataDbHelper.java?at=master
And if you want to know the reason the open methods exist read through this code
(This class was made from the help of someone elses code)
https://bitbucket.org/FabianCCook/dbhelper/src/af7a8eba8d1a3f139e4170bbef9f1a2d3fdf1b47/src/nz/smartlemon/DatabaseHelper/SDCardSQLiteOpenHelper.java?at=master
SQLiteDatabase db = getReadableDatabase();
Cursor cur = db.rawQuery("SELECT * FROM TASK WHERE dbDate = '"+AlarmDate+"' AND dbTime = '"+AlarmTime+"'",new String [] {});
Make sure you have gotten a readable database for 'db' or it will return null everytime.
Also change the end of your raw query to new String [] {}
Hope this helps, this is what I use in my applications.

Pointing second cursor to same database in sqlite on android, this a no no?

I have this function that is filling out a class based on data from several tables. I got the first cursor:
String query="SELECT * FROM SESSION where _id =" + mSessionID + ";";
Cursor c = dbAdapter.selectRecordsFromDB(query, null);
Session session=null;
c.moveToFirst();
This works great. Then a little lower I do this:
long galleryId = c.getInt(4);
long packageId = c.getInt(5);
long contractId = c.getInt(6);
String query2="SELECT * FROM PHOTOPACKAGES WHERE _id =" + packageId + ";";
Cursor p = dbAdapter.selectRecordsFromDB(query2, null);
and the p cursor always returns -1 for its count. I can go right into the sqlite in the adb and run the same query where packageId = 1 and it works great...so not sure why this is not working, i don't see any other errors...can you just not use two cursors on the same database? p.s. selectRecordsFromDB is a helper function:
public Cursor selectRecordsFromDB(String query, String[] selectionArgs) {
Cursor c = myDataBase.rawQuery(query, selectionArgs);
return myDataBase.rawQuery(query, selectionArgs);
}
To answer your actual question: Yes you can target the same DB with multiple cursors. I believe there is something else wrong with your code.
Also as Philip pointed out, creating Cursors is very costly and you do not want to make extras just because, and always close them when finished with them.
Your selectRecordsFromDB function looks pretty darned weird, but it will probably work after a fashion, because the first cursor that you create goes out of focus straight away. Leaking open cursors like that is not a good idea though.

SQLite table query

I query the table by using this function below
public Cursor getTableInfo() throws SQLException
{
return db.query(TableName, null,
null,
null,
null,
null,
null);
}
I got the error "View Root.handleMessage(Message)line:1704". I could insert the data but can't query the data. I called this function below
Cursor c = db.getTableInfo();
int cRow = c.getCount();
if (cRow == 0)
{
Toast.makeText(NewContact.this,
"No Record",
Toast.LENGTH_LONG).show();
}
In SQLite, is there any case-sensitive in the name of database, table, column?
Please help me.
Your db request looks ok and it should return all records from your table.
So maybe there are just no records in the table?
Also it's unclear whether you have problem with db related stuff or with smth else, because the code provided looks ok.
I would rather evaluate the outcome of c.moveToFirst() instead of c.getCount(). The latter means the cursor iterates over the whole dataset which is a more costly operation.

Categories

Resources