2012-12-30 3 views
20

Ich habe zwei Tabellen mit 1: n-Beziehung, ich verwende Content-Provider und Cursorloader.Wie verwendet man Join-Abfrage in CursorLoader, wenn sein Konstruktor es nicht unterstützt

Wie würde ich eine Join-Abfrage machen, um mit Cursor Loader zu arbeiten? Ich könnte es irgendwie mit rawSql Inside Content Provider hacken, aber wie es im Cursor Loader-Konstruktor zu tun ist, ist mir ein Rätsel.

Vielen Dank!

CursorLoader (Context Kontext uri Uri, String [] Projektion, String Auswahl, String [] selectionArgs, String sortOrder)

Wie für beitreten, wenn Uri abfragt konnte nur Punkt an einen Tisch

Antwort

31

Die Uri zeigt nicht auf eine Tabelle. Es zeigt auf was auch immer Sie darauf hinweisen möchten.

Lassen Sie uns so tun, als ob Ihre beiden Tabellen Customer und Order sind. Ein Kunde kann viele Bestellungen haben. Sie möchten eine Abfrage ausführen, um alle ausstehenden Aufträge zu erhalten ... Sie möchten jedoch einige kundenbezogene Spalten einfügen, die Sie benötigen, z. B. den Namen des Kunden.

Lassen Sie uns weiterhin so tun, als ob Sie bereits content://your.authority.goes.here/customer und content://your.authority.goes.here/order definiert haben, um diese Tabellen abzufragen.

Sie haben zwei Möglichkeiten:

  1. die Join-In von dem Anzeigenamen des Kunden auf Ihrem /orderUri. Mit einer anderen verfügbaren Spalte werden wahrscheinlich keine bestehenden Kunden des Anbieters durchbrochen (obwohl das Testen immer eine gute Idee ist). Das ist, was ContactsContract tut - es verbindet sich in einigen Basisspalten, wie der Name des Kontakts, in fast allen Abfragen aller Tabellen.

  2. Erstellen Sie content://your.authority.goes.here/orderWithCust, die die gleiche grundlegende Abfrage wie /order tut tut, aber enthält Ihre beitreten. In diesem Fall könnten Sie insert(), update() und delete() eine Art von RuntimeException werfen, um Sie daran zu erinnern, dass Sie keine Daten mit /orderWithCust als Uri ändern sollten.

Am Ende ist ein ContentProviderUri System der Gestaltung ähnlich ein REST Web-Service-URL-System zu entwerfen. In beiden Fällen muss der Join auf der Provider/Server-Seite ausgeführt werden. Daher müssen Sie möglicherweise die One-Table-to-One-URL-Baseline aufbrechen, um einige nützliche Joins bereitzustellen.

+1

ich Ihre Antwort oben, aber in meinem Fall Sinn der Verwendung von Join lesen ist Spalten abrufen aus mehreren verknüpften Tabellen mit nur einer Abfrage. Innerhalb eines einzelnen Cursors, den ich vom Cursor Loader bekomme, brauche ich DISPLAY_NAME_PRIMARY aus der ContactsContract.Contact Tabelle, ACCOUNT_NAME aus der ContactsContract.RawContact Tabelle und DATA1 aus der ContactsContract.DATA Tabelle. Nun verstehe ich nicht, wie einzelne Uri mir dabei helfen können, denn wenn ich Uri gebe Auf die ContactsContract.RawContact-Tabelle zeigend, wie werde ich dann auf verwandte Spalten von ContactsContract.Contact und ContactsContract.Data zugreifen? –

+1

@AbhishekChauhan: Nun, in Ihrem Fall haben Sie den 'ContentProvider' nicht geschrieben, was bedeutet, dass Sie nicht" abhacken "können irgendwie mit rawSql Inside Content Provider ", wie du in deiner Frage behauptet hast. Meine Antwort richtet sich an jemanden, der den 'ContentProvider' schreibt und somit den Join in ihm machen kann. In Ihrem Fall müssen Sie Ihre Daten manuell von mehreren 'Cursor'-Objekten" verbinden ", und das' Loader'-Framework wird in diesem Bereich nicht viel helfen. Aus dem Stegreif, würde ich eine 'AsyncTask' verwenden, dann Abfragen mit' ContentResolver' durchführen und Ihre Daten in 'doInBackground()' zusammenführen, denke ich. – CommonsWare

+0

ya ... danke, ich werde asynctask dafür verwenden und später zusammenführen ... dieser wird mir besser passen –

4

Ich fand eine Lösung mit Unterklassen von ContentProvider. Nehmen wir an, Sie haben Tabelle tblA und eine andere Tabelle tblB. Ich empfehle zwei Klassen "AContentProvider" und "BContentProvider" zu erstellen. Stellen Sie vor allem sicher, dass beide Tabellen in derselben Datenbank eingerichtet sind.

Der Hauptteil der Lösung ist ContentProvider.query() in dem Contentprovider außer Kraft zu setzen, die Sie von Ihrem CursorLoader nennen - die URI entscheidet, welches es ist:

@Override 
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sort) { 
    SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); 

    qb.setTables(
      "tblA LEFT JOIN tblB" 
        + " ON (" 
        + "tblA.b_id" 
        + " = " 
        + "tblB.id" 
        + ")" 
    ); 

    ... 
    // Content of projection is set by CursorLoader 
    // usually in an Activity that implements LoaderManager.LoaderCallbacks<> 

    Cursor c = qb.query(
      database, 
      projection, 
      selection, 
      selectionArgs, 
      groupBy, 
      having, 
      orderBy 
    ); 

    ... 
    return c; 
} 

Wie Sie, die sehen JOIN erfolgt in SetTables().Mit Projektion Sie sicherstellen, dass Sie nur die Spalten angezeigt werden Sie wirklich brauchen, und vor allem, haben Sie keine doppelten Spalten, wie „id“ aus beiden Tabellen:

final String[] projection = new String[] { 
     "tblA.*", 
     "tblB.columnThatOnlyBHas" 
}; 

Nutzen Sie die Überschreibung und versuchen, In den Unterklassen so viel Arbeit wie möglich erledigen; zum Beispiel: alle meine überschriebene query() Methoden setNotificationUri() aufrufen, die ContentResolver zu benachrichtigen, wenn die Cursor-Ergebnismenge ändert .:

c.setNotificationUri(getContext().getContentResolver(), uri); 
Verwandte Themen