La columna de Android ‘_id’ no existe?

Tengo problemas con algo que funciona en el ejemplo del Bloc de notas. Aquí está el código del NotepadCodeLab / Notepadv1Solution:

String[] from = new String[] { NotesDbAdapter.KEY_TITLE }; int[] to = new int[] { R.id.text1 }; SimpleCursorAdapter notes = new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to); 

Este código parece funcionar bien. Pero para ser claros, ejecuté la utilidad ADB y ejecuté SQLite 3. Inspeccioné el esquema de la siguiente manera:

sqlite> .schema

 CREATE TABLE android_metadata (locale TEXT); CREATE TABLE notes (_id integer primary key autoincrement, title text not null, body text not null); 

Todo me parece bien.


Ahora a mi aplicación, que, por lo que puedo ver, es básicamente la misma con algunos cambios menores. Simplifiqué y simplifiqué mi código, pero el problema persiste.

 String[] from = new String[] { "x" }; int[] to = new int[] { R.id.x }; SimpleCursorAdapter adapter = null; try { adapter = new SimpleCursorAdapter(this, R.layout.circle_row, cursor, from, to); } catch (RuntimeException e) { Log.e("Circle", e.toString(), e); } 

Cuando ejecuto mi aplicación, obtengo una RuntimeException y las siguientes impresiones en LogCat desde mi Log.e() :

Mensaje de LogCat:

java.lang.IllegalArgumentException: la columna ‘_id’ no existe

Entonces, regrese a SQLite 3 para ver qué hay de diferente en mi esquema:

sqlite> .schema CREATE TABLE android_metadata (locale TEXT); Círculos CREATE TABLE (_id autoincremento de clave primaria entera, entero de secuencia, radio real, x real, y real);

No veo cómo me falta el ‘_id’.

¿Qué he hecho mal?

Una cosa que es diferente entre mi aplicación y el ejemplo del Bloc de notas es que comencé creando mi aplicación desde cero utilizando el asistente de Eclipse, mientras que la aplicación de muestra ya viene integrada. ¿Hay alguna clase de cambio ambiental que deba hacer para que una nueva aplicación use una base de datos SQLite?

Veo, la documentación para los estados de CursorAdapter :

El Cursor debe incluir una columna llamada _id o esta clase no funcionará.

SimpleCursorAdapter es una clase derivada, por lo que parece que se aplica esta statement. Sin embargo, la statement es técnicamente incorrecta y un tanto engañosa para un novato. El conjunto de resultados para el cursor debe contener _id , no el cursor en sí.
Estoy seguro de que esto es claro para un DBA porque ese tipo de documentación taquigráfica es clara para ellos, pero para los novatos, estar incompleto en la statement causa confusión. Los cursores son como iteradores o punteros, no contienen nada más que un mecanismo para atravesar los datos, no contienen columnas.

La documentación de los cargadores contiene un ejemplo donde se puede ver que el _id está incluido en el parámetro de proyección .

 static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS, Contacts.CONTACT_PRESENCE, Contacts.PHOTO_ID, Contacts.LOOKUP_KEY, }; public Loader onCreateLoader(int id, Bundle args) { // ... return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); } 

Esto ha sido respondido y me gustaría hacerlo más completo aquí.

SimpleCursorAdapter requiere que el conjunto de resultados del Cursor debe incluir una columna llamada exactamente “_id”. No se apresure a cambiar el esquema si no definió la columna “_id” en su tabla. SQLite agregó automáticamente una columna oculta llamada “rowid” para cada tabla. Todo lo que necesita hacer es seleccionar rowid explícitamente y alias como ‘_id’ Ej.

 SQLiteDatabase db = mHelper.getReadableDatabase(); Cursor cur = db.rawQuery( "select rowid _id,* from your_table", null); 

El código de Tim Wu realmente funciona …

Si está utilizando db.query, entonces sería así …

 db.query(TABLE_USER, new String[] { "rowid _id", FIELD_USERNAME, }, FIELD_USERNAME + "=" + name, null, null, null, null); 

Lo que resolvió mi problema con este error fue que no había incluido la columna _id en mi consulta DB. Agregar eso resolvió mi problema.

Sí, también cambio la consulta de cadena SELECT para solucionar este problema.

 String query = "SELECT t.*,t.id as _id FROM table t "; 

Esto probablemente ya no sea relevante, pero hoy me tocó el mismo problema. Resulta que los nombres de las columnas son sensibles a mayúsculas y minúsculas. Tenía una columna _ID, pero Android espera una columna _id.

Si lee los documentos en sqlite, al crear cualquier columna de tipo INTEGER PRIMARY KEY se creará un alias interno de ROWID, por lo que no vale la pena agregar un alias en cada SELECT, desviándose de las utilidades comunes que podrían aprovechar algo como una enumeración de columnas que definen la tabla.

http://www.sqlite.org/autoinc.html

También es más sencillo usar esto como ROWID en lugar de la opción AUTOINCREMENT que puede causar que _ID se desvíe del ROWID. Al vincular _ID a ROWID, significa que la clave primaria se devuelve desde insert / insertOrThrow; si escribe un ContentProvider, puede usar esta clave en el Uri devuelto.

Otra forma de lidiar con la falta de una columna _id en la tabla es escribir una subclase de CursorWrapper que agregue una columna _id si es necesario.

Esto tiene la ventaja de no requerir ningún cambio en tablas o consultas.

He escrito una clase de este tipo, y si tiene algún interés, puede encontrarla en https://github.com/cmgharris/WithIdCursorWrapper.