I have a problem with android database and cursors. Time to time (very rarely) happens, that I get crash report from customers. It's hard to find out why it crashes, so it's really some minor bug.
Here is exception:
java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed.
at android.database.sqlite.SQLiteConnectionPool.throwIfClosedLocked(SQLiteConnectionPool.java:962)
at android.database.sqlite.SQLiteConnectionPool.waitForConnection(SQLiteConnectionPool.java:599)
at android.database.sqlite.SQLiteConnectionPool.acquireConnection(SQLiteConnectionPool.java:348)
at android.database.sqlite.SQLiteSession.acquireConnection(SQLiteSession.java:894)
Then I have methods like this one (that it crashed in the line c.moveToNext() with that error). It almost never crashes but sometimes it does and i can't reproduce it.
public List<AlarmModel> getAlarms() {
SQLiteDatabase db = this.getReadableDatabase();
String select = "SELECT * FROM " + Alarm.TABLE_NAME +" WHERE "+ Alarm.COLUMN_NAME_ALARM_FLAVOR_ID+" = -1 ";
Cursor c = db.rawQuery(select, null);
List<AlarmModel> alarmList = new ArrayList<AlarmModel>();
while (c.moveToNext()) {
alarmList.add(populateModel(c));
}
if (!alarmList.isEmpty()) {
if (db != null) {
db.close();
}
if (c != null) {
c.close();
}
return alarmList;
}
return null;
}
Multiple things might cause this issue:
a. You are calling db.close() before accessing the data.
b. Or db.close() is being called by some other method and you are still trying to access data from database from other method with same database reference.
Remove db.close()
This may be a multi thread issue, the db is closed by another thread. You can check all places of db.close().
I usually open database in Application.onStart(), and only close it in Application.onDestroy(). Thus no worry about multi-thread database open/close.
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
.....
The issue is purely with the contents inside the tables. If the table is not empty (with one or more records), application works perfectly. I am deleting the contents of table and immediately after that reading the same table, it throws exception and app force closes.
I tried searching for it but couldn't conclude. The key point is : index out of bound exception which is thrown at movetofirst() method of cursor when i am going to read the table, i suppose... Please help.
public List<TableData> readForPaymentDetais()
{
List<TableData> paymentDetails = new ArrayList<TableData>();
try
{
String selectQuery = "select * from PaymentDetails";
SQLiteDatabase database = this.getWritableDatabase();
Cursor cursor = database.rawQuery(selectQuery, null);
if(cursor.getCount() !=0)
{
if(cursor.moveToFirst())
{
do
{
TableData data = new TableData();
data.setPaymentMade(Float.valueOf(cursor.getString(0).toString()));
data.setDateOfPayment(cursor.getString(1));
paymentDetails.add(data);
}
while(cursor.moveToNext());
}
}
return paymentDetails;
}
catch(Exception exc)
{
return null;
}
}
Before executing moveToFirst method of cursor please check whether cursor is empty. For that you can use code like:
if (mCursor.getCount() == 0) {
// cursor is empty
}
If cursor is not empty put your stuff in else part.
In my database there are many methods, each one is created to retrieve a specific row by passing the ID as a parameter. And, In my App, I need to call these methods to perform certain action on the retrieved data. example of that is, the code posted below. Do I need to check the SqliteDB.isOpen before I call these methods.
Java_Code:
if (! sqliteDB.isOpen()) {
Log.d(TAG, "#fetchItemRelativeToFullList(): Sqlite DataBase Was Closed, and it Will Be Opened");
sqliteDB = mplOpenHelperDB.getWritableDatabase();
}
Log.d(TAG, "#fetchItemRelativeToFullList(): Sqlite DataBase Is Opened");
String name = mplOpenHelperDB.getLocationName(itemClickedPos+1);
Toast.makeText(getApplicationContext(), "name of location"+name, Toast.LENGTH_SHORT).show();
DataBaseRow dataBaseRow = new DataBaseRow();
if (dataBaseRow != null) {
Log.d(TAG, "#fetchItemRelativeToFullList(): Object of DataBaseRow Class Is Created, Not NULL");
if ( (mplOpenHelperDB.getLocationName(itemClickedPos+1)).equals("") ) {
Log.i(TAG, "#fetchItemRelativeToFullList: getLocationName Is Empty");
dataBaseRow.setLocName("NULL");
}
else {
String targetName = mplOpenHelperDB.getLocationName(itemClickedPos+1);
dataBaseRow.setLocName(targetName);
}
Sample Of DataBase Methods:
public String getLocationName(long id) {
SQLiteDatabase db = this.getReadableDatabase();
SQLiteCursor c = (SQLiteCursor) db.rawQuery("SELECT locationName FROM MPLData WHERE "+
BaseColumns._ID+" = "+
Long.toString(id) +" AND locationName IS NOT NULL ", null);
String r;
c.moveToFirst();
if (c.getCount() == 0) {
return "";
} else {
r = c.getString(0);
}
c.close();
db.close();
return r;
}
The answer to your immediate question is "No". When you getReadableDatabase(), the db is open. You are good.
FWIW, I'd guess that you have experience doing db work in some other context (Ruby, PHP, etc). As David points out you are doing way more work than you need to do. Have a look at Loaders, ContentProviders and ContentResolvers, FTW.
No, getReadableDatabase() and getWritableDatabase() Create and/or open a database that will be used for reading and writing. The first time this is called, the database will be opened and onCreate(SQLiteDatabase), onUpgrade(SQLiteDatabase, int, int) and/or onOpen(SQLiteDatabase) will be called.
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