SQLiteDatabaseLockedException: database is locked retrycount exceeded - android

I am doing Stock management application and it required frequent access to database to view and edit data. While repeating this process many times ,database get locked.
Getting 2 error :
"A SQLiteConnection object for database was leaked! Please fix your application to end transactions in progress properly and to close the database when it is no longer needed."
"SQLiteDatabaseLockedException: database is locked (code 5): retrycount exceeded"
After get this error it take long time to complete database insert operation.
How can I fix this issue.
public synchronized boolean insert(BeanOrderHeader orderHdrItem) throws Exception {
boolean status=false;
try {
//Create Sqlite object to insert order details
db = getSQLiteDatabase();
//Before insert delete records if already exists
deleteOrderData(db,orderHdrItem);
db.beginTransactionNonExclusive();
// db.beginTransaction();
getPaymentPreparedStatement(db,orderHdrItem);
status= true;
db.setTransactionSuccessful();
db.endTransaction();
if(orderHdrItem.isNewOrder())
mCounterProvider.updateOrderBillNumber();
} catch (Exception e) {
e.printStackTrace();
status= false;
throw new Exception("Failed to save the order. Please check the log for details");
}finally{
db.setTransactionSuccessful();
db.endTransaction();
}
return status;
}
protected SQLiteDatabase getSQLiteDatabase() {
if(myDataBase==null)
open();
if(!myDataBase.isOpen())
open();
return myDataBase;
}
public SQLiteStatement getPaymentPreparedStatement(SQLiteDatabase db,BeanOrderHeader
orderHeader) throws Exception{
ArrayList<BeanOrderPayment> orderPaymentlList=orderHeader.getOrderPaymentItems();
SQLiteStatement prep;
String insert_sql="insert into "+"order_payments "+" (" +
"order_id, "+
"payment_mode, "+
"paid_amount, "+
"card_name, "+
"card_type, "+
"card_no, "+
"name_on_card, "+
"card_expiry_month, "+
"card_expiry_year, "+
"card_approval_code, "+
"card_account_type, "+
"company_id, "+
"voucher_id, "+
"voucher_value, "+
"voucher_count, "+
"cashier_id, "+
"payment_date, " +
"payment_time "+
",id " +
",discount_id" +
",discount_code" +
",discount_name" +
",discount_description" +
",discount_price" +
",discount_is_percentage" +
",discount_is_overridable" +
",discount_amount" +
",is_repayment" +
",is_voucher_balance_returned" +
",partial_balance" +
") "+
" values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
prep=db.compileStatement(insert_sql);
int counter=0;
if(mPosOrderUtil==null)
mPosOrderUtil=new PosOrderUtil(getContext());
for (BeanOrderPayment payItem: orderPaymentlList){
prep.bindString(1, payItem.getOrderId());
prep.bindLong(2, payItem.getPaymentMode().getValue());
prep.bindDouble(3, payItem.getPaidAmount());
prep.bindString(4, payItem.getCardName());
prep.bindString(5, payItem.getCardType());
prep.bindString(6, payItem.getCardNo());
prep.bindString(7, payItem.getNameOnCard());
prep.bindLong(8, payItem.getCardExpiryMonth());
prep.bindLong(9, payItem.getCardExpiryYear());
prep.bindString(10, payItem.getCardApprovalCode());
prep.bindString(11, payItem.getAccount());
prep.bindLong(12, payItem.getCompanyId());
prep.bindLong(13, payItem.getVoucherId());
prep.bindDouble(14, payItem.getVoucherValue());
prep.bindLong(15, payItem.getVoucherCount());
prep.bindLong(16, payItem.getCashierID());
prep.bindString(17, payItem.getPaymentDate());
prep.bindString(18, payItem.getPaymentTime());
prep.bindString(19, mPosOrderUtil.appendToId(orderHeader.getOrderId(), counter++)); //Id generated from order id
BeanDiscount disc=payItem.getDiscount();
if(disc!=null){
prep.bindLong(20, disc.getId());
prep.bindString(21, disc.getCode());
prep.bindString(22, disc.getName());
prep.bindString(23, disc.getDescription());
prep.bindDouble(24, disc.getPrice());
prep.bindLong(25, getIntFromBoolean(disc.isPercentage()));
prep.bindLong(26, getIntFromBoolean(disc.isOverridable()));
prep.bindDouble(27, payItem.getPaidAmount());
}
prep.bindLong(28, getIntFromBoolean(payItem.isRepayment()));
prep.bindLong(29, getIntFromBoolean(payItem.isVoucherBalanceReturned()));
prep.bindDouble(30, payItem.getPartialBalance());
prep.executeInsert();
prep.clearBindings();
}
return prep;
}

You need to close your SQLiteHelper class instance after db operations
dbHelper.close()

At last I resolve issue by keeping a single object of SQLiteDatabase instance. If database structure is complex, use single object to access data from all table so we can manage sq-lite database easily.
After db operations close database object to resolve database leak
mDatabase.close()

The other reason for this SQLiteDatabaseLockedException is starting a transaction and not closing it in finally block and later trying to do something with the DB.
Bad:
SQLiteDatabase db = this.getReadableDatabase();
db.beginTransaction();
//DO some db writing
db.setTransactionSuccessful();
db.endTransaction();
Good:
SQLiteDatabase db = this.getReadableDatabase();
db.beginTransaction();
try {
//DO some db writing
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}

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 - SQLiteException while deleting record

Currently We have one application in which we are receiving many crash reports while deleting record from database .
Here is method in which app is crashing.
public int deleteGroupMap(String nickName) {
SQLiteDatabase database = this.getWritableDatabase();
try {
return database.delete(TABLE_NAME_GROUP_MAP, COLUMN_GMAP_NICK_NAME + " = '" + nickName + "'", null);
} catch (Exception e) {
e.printStackTrace();
} finally {
database.close();
}
return 0;
}
but we am getting following exception:
android.database.sqlite.SQLiteException: near "adz": syntax error
(code 1): , while compiling: DELETE FROM groups_map WHERE
gmap_nick_name = ''adz.'
Any help will be appreciated.
Look at delete signature:
int delete (String table, String whereClause, String[] whereArgs)
Third argument is where args:
You may include ?s in the where clause, which will be replaced by the
values from whereArgs. The values will be bound as Strings.
It's automatically escaped, so there is no need to put quotes (', ") manually.
Use where args instead of strings concating:
database.delete(TABLE_NAME_GROUP_MAP, COLUMN_GMAP_NICK_NAME + " = ?", new String[] { nickName });
Try Like This
public int deleteGroupMap(String nickName) {
SQLiteDatabase database = this.getWritableDatabase();
try {
database .execSQL("DELETE FROM "+ TABLE_NAME_GROUP_MAP + " WHERE " + COLUMN_GMAP_NICK_NAME + " = "+nickName+"");
database .close();
} catch (Exception e) {
e.printStackTrace();
} finally {
database.close();
}
return 0;
}
Try this
return database.delete(TABLE_NAME_GROUP_MAP, COLUMN_GMAP_NICK_NAME + "= ?" , new String[]{Long.toString(nickName)});
You should also use parameter markers because appending values directly is error prone when the source contains special characters.
try following because it will also prevent SQL injections to your app
database.delete(TABLE_NAME_GROUP_MAP, COLUMN_GMAP_NICK_NAME + "=?", new String[]{String.valueOf(nickName));

Update doesn't work sqlite android

Having problem updating a column in a table. I tried both of these solutions:
this.openDataBase();
String SQLStatement = "update " + TABLE_POSES;
SQLStatement += " set " + COLUMN_SKIP + "=" + SKIP + " Where ";
SQLStatement += COLUMN_ID + "=" + String.valueOf(skipPoseId);
myDataBase.rawQuery(SQLStatement, null);
this.close();
and this:
this.openDataBase();
ContentValues args = new ContentValues();
args.put(COLUMN_SKIP,SKIP);
myDataBase.update(TABLE_POSES, args, COLUMN_ID + "=" + String.valueOf(skipPoseId),null);
this.close();
Neither of these code snippets work and I am not getting any exceptions thrown. What am I doing wrong?
You should use the second method using update() and you should check the return value. If the value is zero, then the state of the database isn't what you expect and no rows were updated. If the row is not zero then the updating is succeeding.
If anything is wrong with your accessing the database an exception will be thrown before the update() call.
I would take advantage of the args parameter of update() like so:
myDataBase.update(TABLE_POSES, args, COLUMN_ID + " = ?", new String[]{ Long.toString(skipPoseId) });
Use update like this,
String query="UPDATE tablename SET columnname="+var+ "where columnid="+var2;
sqlitedb.execSQL(query);
Just write your update query in String query and execute.
if you use db.begintransaction() in your code you must call db.setTransactionSuccessful() before db.endtransaction() such as:
try {
SQLHelper dbHelper = new SQLHelper(this);
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.beginTransaction();
...................
db.setTransactionSuccessful();
db.endTransaction();
db.close();
}
catch (Exception ex){
}
I had the exactly same issue. After much thought and debugging I saw that the WHERE condition wasn't addressing any rows of the table.
The odd thing is that the myDatabase.update command giving 1 as the return and I was understanding it as 1 row affected by the update.

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.

SQLiteDatabase.insertOrThrow doesn't throw but not data is inserted

I'm trying to get familiar with Android and its database API.
I've created a class that inherits from SQLiteOpenHelper and this
is how I create the table in the database
#Override
public void onCreate(SQLiteDatabase db) {
try {
db.execSQL("CREATE TABLE " + FUELS_TABLE_NAME + " ("
+ "_ID INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "DATE_OF_FUELS DATE DEFAULT CURRENT_TIME,"
+ "SELLER_POSITION TEXT DEFAULT 'unknown',"
+ "AMOUNT REAL"
+ ");"
);
} catch (SQLException e) {
Log.e(DATABASE_NAME, e.toString());
}
}
The function used to add data to the DB is the following implemeneted within
the same class is
public void addNewFuel(float amount) {
// Create the content to insert into the database
ContentValues newEntry = new ContentValues();
newEntry.put("amount", amount);
// Get database handler
try {
db = getWritableDatabase();
} catch (SQLException e) {
Log.e(DATABASE_NAME, e.toString());
return;
}
// Begin transaction and insert data
long returnedValue;
db.beginTransaction();
try {
returnedValue = db.insertOrThrow(FUELS_TABLE_NAME, null, newEntry);
Log.v(DATABASE_NAME, "return value " + returnedValue);
} catch (SQLException e) {
Log.e(DATABASE_NAME, e.toString());
} finally {
db.endTransaction();
}
db.close();
}
but apparently no data is added. The returnValue is always 1. The method doesn't throw,
and when I pull out the DB with adb and look at it's content is totally empty.
I just can't understand what I'm missing.
Any suggestion would be appreciated.
Thanks,
S
McStretch's answer is incorrect. getWritableDatabase() does not create a transaction for your code, the quoted line from the docs is referring to transactions being used for the onCreate and onUpgrade methods meaning that you don't need to add transaction code in those methods. You still need add transaction code for any other method that requires transactions.
emitrax's code is not working correctly as db.setTransactionSuccessful() is not being called which means the transaction will be rollbacked by db.endTransaction().
See benritz's answer for the correct solution. This answer is incorrect, but I'm unfortunately not able to delete it since it's an accepted post.
/******* NOT CORRECT!!
Since you're inheriting from SQLiteOpenHelper, your call to getWritableDatabase() already starts a DB transaction. From the SQLiteOpenHelper API:
Transactions are used to make sure the
database is always in a sensible
state.
Thus you don't need to call db.beginTransaction() and db.endTransaction(). Those extraneous transaction calls are messing up your inserts. I plugged the same scenario into my project and found that the same index (6 in my case), was returned when using those transaction methods. When I remove the calls I get my desired results (multiple records inserted).
NOT CORRECT!! *******/

Categories

Resources