Populate a list from a database in Android - android

I’m trying to query a single table database that I’ve created in my code. To the best of my knowledge the database is being created correctly. The query is supposed to be used to populate a ListView but when I try to use the resulting cursor from my query to create SimpleCursorAdapter, it crashes with: java.lang.IllegalArgumentException: column '_id' does not exist. I am assuming this can be traced to the cursor, and also the cursor seems to be empty.
The database is created in the following way within the onCreate() of my implementation of a SQLiteOpenHelper:
db.execSQL("CREATE TABLE " + TABLE_NAME + " (_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT NOT NULL, title TEXT NOT NULL, path TEXT NOT NULL);");
And then the actual query is set up and executed in my DataHelper Class which is used to interact with the database:
public Cursor selectEntryStartsWith(String partialName , String title)
{
String where = "name LIKE '" + partialName + "%' AND title LIKE '" + title + "'";
if (title== null || title.equals("")){
where = "name LIKE '" + partialName + "%'";
}
Cursor cur = mDatabase.query(TABLE_NAME, new String[] {"_id", "name", "title"}, where, null, null, null, "name");
return cur;
}
The code that uses the cursor is as follows:
Cursor cursor = mDataHelper.selectEntryStartsWith("ex", null); //get all entries that start with "ex"
String [] from = new String [] { "name", "title" };
int [] to = new int [] { R.id.name, R.id.title };
SimpleCursorAdapter adapter = new SimpleCursorAdapter(mContext, R.layout.listview_entry, cursor, from, to);
songList.setAdapter(adapter);
I'm using tabs to this last piece of code is from within the onActivityCreated() of a Fragment; I not that it might be better to extend a ListFragment, but I don't think this is the problem here in particularity.
Sorry in advance if I have missed an information that you may require, I've been banging my head on this problem for some time now.

Do you know the data is actually in the database? Run 'adb shell', cd to your data directory '/data/data/[app package name]/databases'. Then run sqlite3 [db file name]. Run some direct sql queries and make sure data exists.
If there is data there, rather than going right to the SimpleCursorAdapter, run some text queries in code, and see if you can access the results.
Once all of that works out, add the ListView stuff as a last step.
Some things to mention. If the user is typing in query values, you need to escape those statement values. Either use selectionArgs in the query statement:
http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#query(java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String)
or use my stripped-down apache commons-lang, and the StringEscapeUtils class.
http://www.touchlab.co/blog/android-mini-commons/
Another thing to consider, although if you're not having trouble, its probably not an issue. 'name' and 'title' might be tricky keywords in sql statements.

The problem was indeed the database was not set up properly, I tried another method of inserting the data i.e. using ContentValues and inserting directly into the database, as opposed to using the precompiled insert statement I was using before.
The insert method now looks like this:
public long insert(String name, String title)
{
ContentValues cv = new ContentValues();
cv.put("name", name);
cv.put("title", title);
return mDatabase.insert(TABLE_NAME, null, cv);
/* The old code was using this precompiled statement
mInsertStatement.bindString(1, name);
mInsertStatement.bindString(2, title);
return mInsertStatement.executeInsert();
*/
}

Related

Android - populate ListView SQLite, cursor null pointer

I have two tables atm, users and notes. I am trying to retrieve data that belongs to the user. So all data to list must be owned by the original user and shown only to him. I have made my table in Databasehelper.
I have made a new class that controls the notes table. In listNotes() I want to loop through the cursor row and get all data owned by the user. Am I quering it correctly?
// Listing all notes
public Cursor listNotes() {
Cursor c = db.query(help.NOTE_TABLE, new String[]{help.COLUMN_TITLE,help.COLUMN_BODY, help.COLUMN_DATE}, null, null, null, null, null);
if (c != null) {
c.moveToFirst();
}
db.close();
return c;
}
I then want to display the cursor data collected in a listview
public void populateList(){
Cursor cursor = control.listNotes();
getActivity().startManagingCursor(cursor);
//Mapping the fields cursor to text views
String[] fields = new String[]{help.COLUMN_TITLE,help.COLUMN_BODY, help.COLUMN_DATE};
int [] text = new int[] {R.id.item_title,R.id.item_body, R.id.item_date};
adapter = new SimpleCursorAdapter(getActivity(),R.layout.list_layout,cursor, fields, text,0);
//Calling list object instance
listView = (ListView) getView().findViewById(android.R.id.list);
adapter.notifyDataSetChanged();
listView.setAdapter(adapter);
}
You aren't creating the NOTE_TABLE right.
You miss a space and a comma here
+ COLUMN_DATE + "DATETIME DEFAULT CURRENT_TIMESTAMP"
It has to be
+ COLUMN_DATE + " DATETIME DEFAULT CURRENT_TIMESTAMP,"
There are two issues here:
One is you have missed a comma (after the Timestamp as specified in an earlier answer).
The other error you have is when using a SimpleCursorAdapter, you need to ensure that the Projection string array includes something to index the rows uniquely and this must be an integer column named as "_id". SQLite already has a feature built in for this and provides a column named "_id" for this purpose (however you can have your own integer column which you can rename to _id). To solve this, change your projection string array to something like:
new String[] {"ROW_ID AS _id", help.COLUMN_TITLE,help.COLUMN_BODY, help.COLUMN_DATE}
I guess the NullPointerException stems from this (but without the stacktrace I don't know for sure).

Android select statement returns no results via rawquery or query while using query directly works

I've spent the whole day so far trying to get a select query to execute viarawquery or query, but I've had no luck so far.
The select statement I want to run is as the following:
SELECT * FROM h_word WHERE category='GRE' AND DONE=0 ORDER BY RANDOM() LIMIT 1
category is a TEXT type column and DONE is an INTEGER type with the default value of 0.
While the query works fine when executed directly in SQLite, in android,it doesn't return any results.
I've tried the below with no luck (the method is located in a class extended from SQLiteAssetHelper which itself is a helper class originally extended from SQLiteOpenHelper originaly taken from here: https://github.com/jgilfelt/android-sqlite-asset-helper:
public Cursor getRandomWord() {
Cursor c;
SQLiteDatabase db = getWritableDatabase();
c=db.rawQuery(query, null);
String query = "SELECT * FROM h_word WHERE category='GRE' AND DONE='0'
ORDER BY RANDOM() LIMIT 1 ";
c=db.rawQuery(query, new String[] {});
c.moveToFirst();
db.close();
return c;
}
I also tested with GRE instead of 'GRE' and 0 instead of '0' but it made no difference.
did the following as well:
public Cursor getRandomWord() {
Cursor c;
SQLiteDatabase db = getReadableDatabase();
c=db.query(true, "h_word", new String[] {
"_id",
"word",
"english_meaning"
},
"category" + "=?" + " AND " +
"DONE" + "=?",
new String[]{"GRE" ,"0"},
null, null, "RANDOM() LIMIT 1" , null);
c.moveToFirst();
db.close();
return c;
}
but the cursor remains empty.
Any ideas what I might be doing wrong here?
Any help would be much appreciated.
PS: when running a simple select statement without a where clause it, works fine.
After another few hours of struggling, I figured it's a bug in android's SQLiteDatabase class.
I managed to solve the problem by changing the name of the "category" column to something else.
Seems like "category" is a key word in the android SQLiteDatabase code, and makes a query return nothing when written in where clauses on the android side.
Someone else also had this problem here:
Android rawquery with dynamic Where clause

Understanding SQLite Cursor Behavior

I'm writing a method to update default settings in a table. The table is very simple: two columns, the first containing labels to indicate the type of setting, the second to store the value of the setting.
At this point in the execution, the table is empty. I'm just setting up the initial value. So, I expect that this cursor will come back empty. But instead, I'm getting an error (shown below). The setting that I am working with is called "lastPlayer" and is supposed to get stored in the "SETTING_COLUMN" in the "SETTINGS_TABLE". Here's the code:
public static void updateSetting(String setting, String newVal) {
String table = "SETTINGS_TABLE";
String[] resultColumn = new String[] {VALUE_COLUMN};
String where = SETTING_COLUMN + "=" + setting;
System.err.println(where);
SQLiteDatabase db = godSimDBOpenHelper.getWritableDatabase();
Cursor cursor = db.query(table, resultColumn, where, null, null, null, null);
System.err.println("cursor returned"); //I never see this ouput
\\more
}
sqlite returned: error code = 1, msg = no such column: lastPlayer
Why is it saying that there is no such column lastPlayer? I thought that I was telling the query to look at the column "SETTING_COLUMN" and return the record where that column has a value "lastPlayer". I'm confused. Can somebody straighten me out? I've been looking a this for an hour and I just don't see what I am doing wrong.
Thanks!
You're not properly building/escaping your query. Since the value lastPlayer is not in quotes, your statement is checking for equality of two columns, which is what that error message is saying.
To properly build your query, it's best to not do this manually with String concatenation. Instead, the parameter selectionArgs of SQLiteDatabase.query() is meant to do this.
The parameters in your query should be defined as ? and then filled in based on the selectionArgs. From the docs:
You may include ?s in selection, which will be replaced by the values
from selectionArgs, in order that they appear in the selection. The
values will be bound as Strings.
So, your code would look like this:
String where = SETTING_COLUMN + " = ?";
Cursor cursor = db.query(table, resultColumn, where, new String[] { setting }, null, null, null);

Why am I getting a _id does not exists error when only trying to query one column from a table?

I was trying to solve the question on why I was getting this error yesterday with some code:
java.lang.IllegalArgumentException: column '_id' does not exist
I had a lot more code, especially that I did not need, so I stripped a lot of it out to make it easier to understand where I am going wrong. But essentially this is my schema:
database.execSQL("CREATE TABLE events (" +
"_id INTEGER PRIMARY KEY, event_name TEXT" +
")");
As one can tell, looks fine right.
Unless I forgot to read, it's most obviously there. But then I figured out where my error was coming from, or at least I am sure this is why. This code that retrieves a cursor:
public Cursor getEventsName() {
return database.rawQuery( "SELECT event_name FROM events", null);
}
According to android, this is the error. When I change it to this:
public Cursor getEventsName() {
return database.rawQuery( "SELECT * FROM events", null);
}
Everything is peachy. When the former, it crashes. Any reason as to why this is. I thought that in rawQuery() I could do that. So long as I am not including where clauses, which I am not. Any help much appreciated.
Let's call these, event cursor:
public Cursor getEventsName() {
return database.rawQuery( "SELECT event_name FROM events", null);
}
... and * cursor:
public Cursor getEventsName() {
return database.rawQuery( "SELECT * FROM events", null);
}
Most of the answers that you have received (even the ones here: In Android, does _id have to be present in any table created?) are guessing at the likely cause for your error. I figured I would answer your question as well:
Any reason as to why (the former crashes and the later is peachy?)
The difference between the * and event cursors is that * is selecting every column implicitly and event is only selecting event_name. In your events table, the * cursor is the equivalent of:
SELECT _id, event_name FROM events;
which is why the this cursor works just peachily. In other words you are not receiving this error:
java.lang.IllegalArgumentException: column '_id' does not exist
because you are implicitly selecting the _id column with *.
Of course the most probable reason for getting this error is when you bind your data with a ListView, Spinner, etc; they all tend to use a CursorAdapter of some form. This is from the CursorAdapter documentation:
Adapter that exposes data from a Cursor to a ListView widget. The Cursor must include a column named "_id" or this class will not work.
So the Solution is simple: you must select the _id column in your query as well as the other columns that you want. (The compiler isn't lying to you.)
That being said, if this still doesn't seem valid to your app or doesn't make sense please post the code where you use the Cursor and the error is thrown.
I suspect that whatever was handling the cursor was trying to get the _ID column but it wasn't specified in your select statement. Doing something like,
public Cursor getEventsName() {
return database.rawQuery( "SELECT _id, event_name FROM events", null);
}
Some Android components, such as the SimpleCursorAdapter require the _ID be available in the select statement since it uses internally when getItemId() is called.
java.lang.IllegalArgumentException: column '_id' does not exist
I had same problem, this exception is thrown because SimpleCursorAdapter need for SELECT column named _id so you can resolve it when for example if you created some table with column KEY_ID as PK so you can try it like this:
SELECT KEY_ID AS _id, column1, column2 FROM SomeTable.
public Cursor getEventsName() {
return database.rawQuery( "SELECT * FROM events", null);
Change it to
public Cursor getEventsName(){
final String[] columns = new String[]{"_id", "event_name "};
return database.query(events, columns, "" , null, null, null, null);
}

Android SQLite Repeated Elements

I have an issue with SQLite on android. Right now, I'm pulling a JSON object from a server, parsing it, and putting each sub-object in a Table with things such as the Name, Row_ID, unique ID, etc. using this code:
public void fillTable(Object[] detailedList){
for(int i=0;i<detailedList.length;++i){
Log.w("MyApp", "Creating Entry: " + Integer.toString(i));
String[] article = (String[]) detailedList[i];
createEntry(article[0], article[1], article[2], article[3], article[4], article[5]);
}
}
createEntry does what it sounds like. It takes 6 strings, and uses cv.put to make an entry. No problems.
When I try to order them however, via:
public String[] getAllTitles(int m){
Log.w("MyApp", "getTitle1");
String[] columns = new String[]{KEY_ROWID, KEY_URLID, KEY_URL, KEY_TITLE, KEY_TIME, KEY_TAGS, KEY_STATE};
Log.w("MyApp", "getTitle2");
Cursor c = ourDatabase.query(DATABASE_TABLENAME, columns, null, null, null, null, KEY_TIME);
Log.w("MyApp", "getTitle3");
String title[] = new String[m];
Log.w("MyApp", "getTitle4");
int i = 0;
int rowTitle = c.getColumnIndex(KEY_TITLE);
Log.w("MyApp", "getTitle5");
for(c.moveToFirst();i<m;c.moveToNext()){
title[i++] = c.getString(rowTitle);
Log.w("MyApp", "getTitle " + Integer.toString(i));
}
return title;
}
Each entry actually has many duplicates. I'm assuming as many duplicates as times I have synced. Is there any way to manually call the onUpgrade method, which drops the table and creates a new one, or a better way to clear out duplicates?
Secondary question, is there any way to order by reverse? I'm ordering by time now, and the oldest added entries are first (smallest number). Is there a reverse to that?
If you don't want duplicates in one column then create that column with the UNIQUE keyword. Your database will then check that you don't insert duplicates and you can even specify what should happen in that case. I guess this would be good for you:
CREATE TABLE mytable (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
theone TEXT UNIQUE ON CONFLICT REPLACE
)
If you insert something into that table that already exists it will delete the row that already has that item and inserts your new row then. That also means that the replaced row gets a new _id (because _id is set to automatically grow - you must not insert that id yourself or it will not work)
Your second question: you can specify the direction of the order of if you append ASC (ascending) or DESC (descending). You want DESC probably.
Cursor c = ourDatabase.query(DATABASE_TABLENAME, columns, null, null, null, null, KEY_TIME + " DESC");

Categories

Resources