I declared the _ID auto increment column in the ContentProvider running on SQLite, and yet I get the following "no such column: _id" error message in the stacktrace, how do I fix this error?
FATAL EXCEPTION: AsyncTask
java.lang.RuntimeException: An error occured while executing doInBackground()
Caused by: android.database.sqlite.SQLiteException: no such column: _id:,
while compiling: SELECT _id, title, text FROM notes
another strange thing is that I did not call the query method of the contentProvider class anywhere in my code and yet I got the location of the error as a line of code in this query method of the ContentProvider class, if the ContentProvider's query method is not being called then it should not be in the stacktrace, however it is, here is where it tells me the location of the error in the stacktrace
com.example.contentproviderexample.ProviderExample.query(ProviderExample.java:132)
ContentProvider class ProviderExample line 132
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
EDIT: after it was suggested that I uninstall or delete data from the app and run again, after I did that I got a different error from the stacktrace, here it is:
Couldn't open notes.db for writing (will try read-only):
android.database.sqlite.SQLiteException:
AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY:
, while compiling: CREATE TABLE IF NOT EXISTS
notes (_id ID INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, text TEXT);
and here is the complete query method of the ContentProvider class
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(TABLE_NAME);
qb.setProjectionMap(notesProjectionMap);
switch(sUriMatcher.match(uri)){
case NOTES:
break;
case NOTES_ID:
selection = selection + "_id=" + uri.getLastPathSegment();
break;
default:
throw new IllegalArgumentException("Unknown URI" + uri);
}
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder); // <-- no colunn _ID error here
c.setNotificationUri(getContext().getContentResolver(), uri);
return c
here is more of the code from the ProviderExample class that extends ContentProvider
public class ProviderExample extends ContentProvider {
public static final String DATABASE_NAME = "notes.db";
public static final int DATABASE_VERSION = 1;
public static final String TABLE_NAME = "notes";
public static final String AUTHORITY = "com.example.contentproviderexample.providerexample";
public static final UriMatcher sUriMatcher;
private static final int NOTES = 1;
private static final int NOTES_ID = 2;
private static HashMap<String, String> notesProjectionMap;
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(AUTHORITY, TABLE_NAME, NOTES);
sUriMatcher.addURI(AUTHORITY, TABLE_NAME + "/#", NOTES_ID);
}
public static interface NoteItems extends BaseColumns {
// notes in the CONTENT_URI is often plural where the actual table name is often singular version of this word
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/notes");
public static final String _ID = "_id";
public static final String TITLE = "title";
public static final String TEXT = "text";
public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd.example.providerexample";
// static final String SINGLE_RECORD = "vnd.android.cursor.item/vnd.example.providerexample";
public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/vnd.example.providerexample";
// static final String MULTIPLE_RECORDS = "vnd.android.cursor.dir/vnd.example.providerexample";
public static final String[] PROJECTION_ALL = {_ID, TITLE, TEXT};
public static final String SORT_ORDER_DEFAULT = TITLE + " ASC";
}
private static class DatabaseHelper extends SQLiteOpenHelper{
DatabaseHelper(Context context){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" + NoteItems._ID + " ID INTEGER PRIMARY KEY AUTOINCREMENT, " +
NoteItems.TITLE + " TEXT, " + NoteItems.TEXT + " TEXT);");
}
@Override
public void onUpgrade(SQLiteDatabase db, int previousVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}
private DatabaseHelper dbHelper;
@Override
public boolean onCreate() {
dbHelper = new DatabaseHelper(getContext());
return true;
}
@Override
public String getType(Uri uri) {
switch (sUriMatcher.match(uri)){
case NOTES:
return NoteItems.CONTENT_TYPE;
case NOTES_ID:
return NoteItems.CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Unknown URI" + uri);
}
}
and here is the MainActivity class
public class MainActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor>{
private SimpleCursorAdapter adapter;
private LoaderManager loaderManager;
private CursorLoader cursorLoader;
private ListView listView;
private int primaryKey;
private String primaryKeyString;
private String testTitle;
private String testText;
private static final int LOADER_ID = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
primaryKey = 0;
testTitle = "test title";
testText = "test text";
ProviderExample providerExample = new ProviderExample();
primaryKeyString = String.valueOf(primaryKey);
getLoaderManager().initLoader(LOADER_ID, null, this);
adapter = new SimpleCursorAdapter(this, R.layout.row_layout, null,
new String[]{primaryKeyString, testTitle, testText}, new int[]{R.id.textView1, R.id.textView2, R.id.textView3},
Adapter.NO_SELECTION);
listView = (ListView) findViewById(R.id.listView1);
listView.setAdapter(adapter);
// load database with test values
//loadDatabase();
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String[] projection = {ProviderExample.NoteItems._ID, ProviderExample.NoteItems.TITLE, ProviderExample.NoteItems.TEXT };
cursorLoader = new CursorLoader(this, ProviderExample.NoteItems.CONTENT_URI, projection, null, null, null);
return cursorLoader;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
adapter.swapCursor(cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
}