Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does my exception handler not trap Android SQLite insert error?

I'm using SQLite for the first time, and am trying to learn its exception handling, so I'm forcing an insert error in my test app. The exception occurs and I see it written to the Eclipse LogCat output window. However it doesn't get caught in the code. I've seen other questions here about being sure to use the correct exception type, and think I've got that right. Any idea what I'm missing?

In the following statement, which is in my main activity, myTable is a class which extends my own AbstractDbAdapter (which has a class DatabaseHelper that extends SQLiteOpenHelper).

try {
    myTable.create("dupkey");
}
catch (android.database.sqlite.SQLiteConstraintException e) {
    Log.e(TAG, "SQLiteConstraintException:" + e.getMessage());
}
catch (android.database.sqlite.SQLiteException e) {
    Log.e(TAG, "SQLiteException:" + e.getMessage());
} 
catch (Exception e) {
    Log.e(TAG, "Exception:" + e.getMessage());
}

Sample stack trace:

Error inserting id="dupkey" last_seen_ts=1360624732 first_seen_ts=1360624732 android.database.sqlite.SQLiteConstraintException: error code 19: constraint failed
  at android.database.sqlite.SQLiteStatement.native_execute(Native Method)
  at android.database.sqlite.SQLiteStatement.execute(SQLiteStatement.java:61)
  at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1582)
  at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1426)
  at com.myCompany.testApp.myTable_DbAdapter.create(myTable_DbAdapter.java:51)

The myTable and AbstractDbAdapter classes:

public class myTable_DbAdapter extends AbstractDbAdapter {

    private static final String DATABASE_TABLE = "myTable";

    // column names -- keys for ContentValues()
    public static final String KEY_ID = "id";
    public static final String KEY_FIRST_SEEN = "first_seen_ts";
    public static final String KEY_LAST_SEEN = "last_seen_ts";

    public myTable_DbAdapter(Context ctx) {
        super(ctx);
    }

    public long create(String id) {
        long firstSeen = System.currentTimeMillis() / 1000;  // SQLite timestamps are in seconds

        ContentValues args = new ContentValues();
        args.put(KEY_ID, id);
        args.put(KEY_FIRST_SEEN, firstSeen);
        args.put(KEY_LAST_SEEN, firstSeen);  // defaults to firstSeen for a new entry

        return mDb.insert(DATABASE_TABLE, null, args);
    }
}

public abstract class AbstractDbAdapter {

    protected static final String TAG = "AbstractDbAdapter";

    protected DatabaseHelper mDbHelper = null;
    protected SQLiteDatabase mDb = null;

    protected static final String TABLE_CREATE_MYTABLE =
        "create table myTable (" +
        "  id text primary key not null" +
        ", first_seen_ts integer not null" +
        ", last_seen_ts integer not null" +
        ");";

    protected static final String DATABASE_NAME = "myDB";
    protected static final int DATABASE_VERSION = 1;

    protected final Context mCtx;

    protected static class DatabaseHelper extends SQLiteOpenHelper {

        DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            // Note: SQLite requires one execSQL per table
            db.execSQL(TABLE_CREATE_MYTABLE);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which destroys existing data.");
            db.execSQL("DROP TABLE IF EXISTS myTable");
            onCreate(db);
        }
    }

    public AbstractDbAdapter(Context ctx) {
        this.mCtx = ctx;
    }

    public AbstractDbAdapter open() throws SQLException {
        mDbHelper = new DatabaseHelper(mCtx);
        mDb = mDbHelper.getWritableDatabase();
        return this;
    }

    public void close() {
        if (mDb != null) {
            mDb.close();
            mDb = null;
        }
        if (mDbHelper != null) {
            mDbHelper.close();
            mDbHelper = null;
        }
    }

}
like image 829
Alan Avatar asked Feb 12 '13 00:02

Alan


People also ask

What is SQLite exception?

An exception that indicates that the SQLite database file is corrupt. SQLiteDatabaseLockedException. Thrown if the database engine was unable to acquire the database locks it needs to do its job. SQLiteDatatypeMismatchException. SQLiteDiskIOException.

Do I need to install SQLite for Android studio?

SQLite is part of the standard Android library; its classes can be found in android. database. sqlite. You don't need to install it.

What is android database SQLite SQLiteDatabase?

android.database.sqlite.SQLiteDatabase. Exposes methods to manage a SQLite database. SQLiteDatabase has methods to create, delete, execute SQL commands, and perform other common database management tasks.


2 Answers

I found the answer here: SQLiteConstraintException not caught

The SQLiteDatabase.insert() method doesn't throw an exception. d'oh!

For other SQLite newbies like me, if you want to catch exceptions when inserting into the database, use the SQLite.insertOrThrow() method. It will throw an exception which you can then catch and handle.

like image 102
Alan Avatar answered Oct 22 '22 12:10

Alan


While not 100% related to the question, I ran into a similar problem using Room and Google returned this question for my search. In the case of Room, there doesn’t appear to be a catchable exception thrown and insertOrThrow() does not exist. In reviewing https://developer.android.com/reference/kotlin/androidx/room/OnConflictStrategy#ABORT:kotlin.Int, many of the options are depreciated, but I went with OnConflictStrategy.IGNORE here since this will return -1 if there is a problem.

Cheers.

like image 44
Bink Avatar answered Oct 22 '22 13:10

Bink