web-dev-qa-db-fra.com

La colonne Android '_id' n'existe pas?

J'ai des problèmes avec quelque chose qui fonctionne dans l'exemple de Notepad . Voici le code de 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);

Ce code semble bien fonctionner. Mais juste pour être clair, j’ai exécuté l’utilitaire ADB Puis j’ai exécuté SQLite 3. J’ai inspecté le schéma comme suit:

sqlite> .schema

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

Tout me semble bien.


Passons maintenant à ma demande qui, pour autant que je sache, est fondamentalement la même chose avec Quelques modifications mineures. J'ai simplifié et simplifié mon code, mais le problème 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);
}

Lorsque j'exécute mon application, je reçois une exception RuntimeException et les impressions suivantesin LogCat à partir de mon instruction Log.e():

Message LogCat:

Java.lang.IllegalArgumentException: la colonne '_id' n'existe pas

Revenons donc à SQLite 3 pour voir ce qui est différent de mon schéma:

sqlite> .schema CREATE TABLE Android_metadata (locale TEXT); CREATE TABLE cercles (_id entier auto incrémentation, séquence entier, rayon réel, x réel, y réel);

Je ne vois pas comment je manque le '_id'.

Qu'est ce que j'ai mal fait?

Une chose qui diffère entre mon application et l'exemple de Notepad est J'ai commencé par créer ma propre application à l'aide de l'assistant Eclipse alors que l'exemple d'application est déjà assemblé. Y at-il une sorte de changement d’environnement à faire pour une nouvelle applicationà utiliser une base de données SQLite?

69
Andrew

Je vois, la documentation pour CursorAdapter États:

Le curseur doit inclure une colonne nommée _id sinon cette classe ne le sera pas travail.

La SimpleCursorAdapter est une classe dérivée, il semble donc que cette instruction s'applique. Cependant, la déclaration est techniquement fausse et trompeuse pour un débutant. Le résultat défini pour le curseur doit contenir _id, pas le curseur lui-même.
.__ Je suis sûr que les administrateurs de bases de données ont bien compris la chose, car ce type de documentation abrégée est clair pour eux, mais pour ces débutants, le fait d'être incomplet dans la déclaration est source de confusion. Les curseurs sont comme des itérateurs ou des pointeurs, ils ne contiennent qu'un mécanisme pour traverser les données, ils ne contiennent pas de colonnes. 

La documentation Loaders contient un exemple dans lequel on peut voir que le _id est inclus dans le paramètre projection.

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<Cursor> onCreateLoader(int id, Bundle args) {
    // ...
    return new CursorLoader(getActivity(), baseUri,
            CONTACTS_SUMMARY_PROJECTION, select, null,
            Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}
148
user405821

Ceci a été répondu et je voudrais le rendre plus complet ici.

SimpleCursorAdapter requiert que le jeu de résultats du curseur inclue une colonne nommée exactement "_id". Ne vous précipitez pas pour changer de schéma si vous n'avez pas défini la colonne "_id" dans votre table . SQLite a automatiquement ajouté une colonne masquée appelée "rowid" pour chaque table. Tout ce que vous avez à faire est de sélectionner explicitement rowid et de le nommer "_id" Ex.

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

Le code de Tim Wu fonctionne vraiment ...

Si vous utilisez db.query, alors ce serait comme ça ...

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

Ce qui a résolu mon problème avec cette erreur est que je n'avais pas inclus la colonne _id dans ma requête de base de données. Ajouter cela a résolu mon problème.

7
RoadXY

Oui, je modifie également la requête de chaîne SELECT pour résoudre ce problème. 

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

Ce n'est probablement plus pertinent, mais je viens de poser le même problème aujourd'hui. Il s'avère que les noms de colonne sont sensibles à la casse. J'avais une colonne _ID, mais Android s'attend à une colonne _id.

6
zmbq

Si vous lisez les docs sur sqlite, créer une colonne de type INTEGER PRIMARY KEY aliasera en interne le ROWID. Il ne vaut donc pas la peine d'ajouter un alias dans chaque SELECT, à la différence des utilitaires courants une énumération de colonnes définissant la table.

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

Il est également plus simple d’utiliser ceci comme ROWID au lieu de l’option AUTOINCREMENT, ce qui peut entraîner une déviation de _ID par rapport au ROWID. En liant _ID à ROWID, cela signifie que la clé primaire est renvoyée par insert/insertOrThrow; Si vous écrivez un fournisseur de contenu, vous pouvez utiliser cette clé dans l'URI renvoyé.

0
Eric Woodruff

Une autre façon de traiter le manque de colonne _id dans la table consiste à écrire une sous-classe de CursorWrapper qui ajoute une colonne _id si nécessaire.

Cela présente l'avantage de ne nécessiter aucune modification des tables ou des requêtes.

J'ai écrit un tel cours, et s'il est intéressant, vous pouvez le trouver à https://github.com/cmgharris/WithIdCursorWrapper

0
cmgharris