Android-Spalte '_id' existiert nicht?

73

Ich habe Probleme mit etwas, das im Notepad-Beispiel funktioniert. Hier ist der Code aus dem 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);

Dieser Code scheint gut zu funktionieren. Um jedoch klar zu sein, habe ich das ADB- Dienstprogramm und SQLite 3 ausgeführt. Ich habe das Schema wie folgt überprüft:

sqlite> .schema

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

Alles scheint mir gut zu sein.


Nun zu meiner Bewerbung, die, soweit ich sehen kann, mit ein paar geringfügigen Änderungen im Grunde dieselbe ist. Ich habe meinen Code vereinfacht und vereinfacht, aber das Problem besteht weiterhin.

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);
}

Wenn ich meine Anwendung ausführe, erhalte ich eine RuntimeException und die folgenden Ausdrucke in LogCat aus meiner Log.e()Anweisung:

LogCat-Nachricht:

java.lang.IllegalArgumentException: Spalte '_id' existiert nicht

Zurück zu SQLite 3, um zu sehen, was an meinem Schema anders ist:

sqlite> .schema CREATE TABLE android_metadata (Gebietsschema TEXT); CREATE TABLE-Kreise (_id Integer-Primärschlüssel-Autoinkrement, Sequenz-Integer, Radius Real, x Real, y Real);

Ich sehe nicht, wie mir die '_id' fehlt.

Was habe ich falsch gemacht?

Eine Sache, die sich zwischen meiner Anwendung und dem Notepad-Beispiel unterscheidet, ist, dass ich meine Anwendung zunächst mit dem Eclipse-Assistenten von Grund auf neu erstellt habe, während die Beispielanwendung bereits zusammengestellt ist. Gibt es eine Art von Umgebungsänderung, die ich vornehmen muss, damit eine neue Anwendung eine SQLite-Datenbank verwendet?

Andrew
quelle
1
Können Sie weitere Details dazu angeben, wie Sie den Cursor erstellt haben und wie Sie möglicherweise die Datenbank geöffnet haben?
EboMike
3
Kehren Sie von Ihrer Auswahl der Spalte _ID zurück? Überprüfen Sie die Cursorerstellung.
Pentium10

Antworten:

153

Ich sehe, die Dokumentation für CursorAdapter besagt:

Der Cursor muss eine Spalte mit dem Namen enthalten, sonst _idfunktioniert diese Klasse nicht.

Das SimpleCursorAdapterist eine abgeleitete Klasse, daher scheint diese Aussage zutreffend zu sein. Die Aussage ist jedoch technisch falsch und für einen Neuling etwas irreführend. Die Ergebnismenge für den Cursor muss enthalten _id, nicht den Cursor selbst.
Ich bin sicher, dass dies für einen DBA klar ist, da diese Art von Kurzdokumentation für ihn klar ist, aber für diese Neulinge führt die Unvollständigkeit der Aussage zu Verwirrung. Cursor sind wie Iteratoren oder Zeiger, sie enthalten nichts als einen Mechanismus zum Übertragen der Daten, sie enthalten selbst keine Spalten.

Die Loader-Dokumentation enthält ein Beispiel, in dem ersichtlich ist, dass das _idim Projektionsparameter enthalten ist.

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");
}
user405821
quelle
15
Ich empfehle, die Anweisung in der Dokumentation wie folgt zu schreiben: "Die Ergebnismenge des Cursors muss eine Spalte mit dem genauen Namen" _id "enthalten. Andernfalls wird eine RuntimeException ausgelöst." Dies wäre weniger verwirrend und korrekt.
user405821
6
@ user405821 - Danke Mann - dieser Typ hat Ihre Antwort nicht als richtig markiert - aber dies war eine hervorragende Ergänzung zu einem Teil von Android, der schlecht dokumentiert ist. +1
tpow
Ich habe eine Notiz über in den Android-Dokumenten mit Google Sidewiki gemacht. Sidewiki ist perfekt für diese Art von Dingen, und ich hoffe, dass mehr Leute es in Verbindung mit der Android-Dokumentation verwenden. developer.android.com/reference/android/widget/…
Ben H
Oh man, das ist die erstaunliche Android SDK + Dokumentation für Sie! Vielen Dank für die Antwort. Ich habe mehr als eine Stunde damit verbracht, herauszufinden, wo das Problem lag.
the_dark_destructor
Vielen Dank! In meinem Fall war der Fehler auf ein Problem mit Groß- und Kleinbuchstaben zurückzuführen.
Vish
107

Dies wurde beantwortet und ich möchte es hier umfassender machen.

SimpleCursorAdapter erfordert, dass die Ergebnismenge des Cursors eine Spalte mit dem genauen Namen "_id" enthält. Beeilen Sie sich nicht, das Schema zu ändern, wenn Sie die Spalte "_id" in Ihrer Tabelle nicht definiert haben. SQLite fügte automatisch eine versteckte Spalte mit dem Namen "rowid" für jede Tabelle hinzu. Alles, was Sie tun müssen, ist, einfach die Zeilen-ID explizit auszuwählen und sie als '_id' zu aliasen.

SQLiteDatabase db = mHelper.getReadableDatabase();      
Cursor cur =  db.rawQuery( "select rowid _id,* from your_table", null);
Tim Wu
quelle
9
oder wählen Sie id als _id
max4ever
Wie wäre es, wenn Sie getItemIdmeine eigene definierte ID überschreiben und angeben? Kann es das Problem lösen?
fikr4n
@ max4ever hat die beste Antwort. Es ist die perfekte Lösung für dieses Problem. Sie müssen keine unnötigen _id-Spalten in Ihrer Datenbank erstellen
Mike Baxter
+1, die Verwendung von "rowid _id" in einer Abfrage hilft wirklich. Aber was für ein Durcheinander ist Android, wenn seine Standard-DB-Implementierung einen rowidSchlüssel erstellt, während sein Standard-Cursoradapter dies erfordert _id?!
Stan
Hat auch für mich gearbeitet. Dies ist die Antwort, die mich davon abhält, _id in meine Datenbank aufzunehmen.
TryinHard
20

Tim Wus Code funktioniert wirklich ...

Wenn Sie db.query verwenden, wäre es so ...

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

Ja, ich ändere auch die SELECT-Zeichenfolgenabfrage, um dieses Problem zu beheben.

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

Was mein Problem mit diesem Fehler löste, war, dass ich die Spalte _id nicht in meine DB-Abfrage aufgenommen hatte. Das Hinzufügen löste mein Problem.

RoadXY
quelle
In diesem Fall können alle Lösungen das Problem nicht beheben. Der letzte Ausweg zu allem.
Tom_mai78101
6

Das ist wahrscheinlich nicht mehr relevant, aber ich habe heute das gleiche Problem. Es stellt sich heraus, dass Spaltennamen zwischen Groß- und Kleinschreibung unterscheiden. Ich hatte eine _ID-Spalte, aber Android erwartet eine _id-Spalte.

zmbq
quelle
Dies ist ein leichter Fehler; In der offiziellen Dokumentation heißt es, _ID zu verwenden.
0

Wenn Sie die Dokumente auf SQLite lesen und eine Spalte vom Typ INTEGER PRIMARY KEY erstellen, wird die ROWID intern als Alias ​​verwendet. Es lohnt sich also nicht, in jedem SELECT einen Alias ​​hinzuzufügen, der von gängigen Dienstprogrammen abweicht, die möglicherweise die Vorteile von so etwas nutzen eine Aufzählung von Spalten, die die Tabelle definieren.

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

Es ist auch einfacher, dies als ROWID anstelle der Option AUTOINCREMENT zu verwenden, was dazu führen kann, dass _ID von der ROWID abweichen kann. Wenn Sie _ID mit ROWID verknüpfen, bedeutet dies, dass der Primärschlüssel von insert / insertOrThrow zurückgegeben wird. Wenn Sie einen ContentProvider schreiben, können Sie diesen Schlüssel in der zurückgegebenen Uri verwenden.

Eric Woodruff
quelle
0

Eine andere Möglichkeit, mit dem Fehlen einer _id-Spalte in der Tabelle umzugehen, besteht darin, eine Unterklasse von CursorWrapper zu schreiben, die bei Bedarf eine _id-Spalte hinzufügt.

Dies hat den Vorteil, dass keine Änderungen an Tabellen oder Abfragen erforderlich sind.

Ich habe eine solche Klasse geschrieben, und wenn sie von Interesse ist, finden Sie sie unter https://github.com/cmgharris/WithIdCursorWrapper

cmgharris
quelle