web-dev-qa-db-de.com

Wie kann die Raumdatenbank zur Laufzeit dynamisch abgefragt werden?

Das Problem

Kann eine Abfrage zur Laufzeit erstellt werden?


Anwendungsfall

@Query("SELECT * FROM playlist " +
        "WHERE playlist_title LIKE '% :playlistTitle %' " +
        "GROUP BY playlist_title " +
        "ORDER BY playlist_title " +
        "LIMIT :limit")
 List<IPlaylist> searchPlaylists(String playlistTitle, int limit);

Der Teil limit ist optional. Das heißt, es sollte in der Lage sein, dieselbe Abfrage mit oder ohne Begrenzung auszuführen.


Ein komplizierterer Anwendungsfall

Im vorherigen Fall ist es möglich, zwei statische Abfragen mit und ohne Limit-Teil durchzuführen, und es kann jeweils ein entsprechender verwendet werden. Manchmal müssen wir uns jedoch mit komplexeren Situationen befassen, wie dem Erstellen eines Filters.

In diesem Fall gibt es im Gegensatz zum vorherigen Beispiel mehrere optionale Teile. Für eine Tabelle mit Büchern müssen wir möglicherweise nach der Kategorie filtern, zu der das Buch gehört, dem Namen des Autors, der Preisspanne, dem Veröffentlichungsdatum usw. Es ist fast unmöglich, statische Abfragen mit allen Kombinationen dieser Teile zu erstellen.

18
Anderson K

Nach meiner Erfahrung (kurz) mit Room ist dies nicht möglich, und zwar nicht, weil es sich um eine Raumbegrenzung handelt, sondern, wie von @CommonsWare implizit kommentiert, eine Einschränkung für SQLite. Sie benötigen zwei Abfragen und daher zwei Methoden in Ihrem DAO.

Ich hätte so etwas wie:

@Query("SELECT * FROM playlist " +
    "WHERE playlist_title LIKE '% :playlistTitle %' " +
    "GROUP BY playlist_title " +
    "ORDER BY playlist_title " +
    "LIMIT :limit")
List<IPlaylist> searchPlaylists(String playlistTitle, int limit);

@Query("SELECT * FROM playlist " +
    "WHERE playlist_title LIKE '% :playlistTitle %' " +
    "GROUP BY playlist_title " +
    "ORDER BY playlist_title ")
List<IPlaylist> searchPlaylists(String playlistTitle);

Dann machen Sie woanders den Bypass:

if (limit.isPresent()) {
   return playlistDao.searchPlaylists(title, limit.get());
} else {
   return playlistDao.searchPlaylists(title);
}

Das ist die beste Option, die ich im Moment denken kann.

15
Juanky Soriano

Anstatt mehrere Abfragen zu schreiben, verweise ich auf negative Klausel. Wenn es eine Änderung in der Abfrage gibt, muss ich die Abfrage aktualisieren, die fehleranfälliger ist.

Offizielles doc -> Wenn der LIMIT-Ausdruck einen negativen Wert ergibt, dann gibt es keine Obergrenze für die Anzahl der zurückgegebenen Zeilen. finden Sie es hier https://sqlite.org/lang_select.html und lesen Sie den Abschnitt über die Limitklausel.

Also würde ich so etwas tun,

@Query("SELECT * FROM playlist " +
    "WHERE playlist_title LIKE '% :playlistTitle %' " +
    "GROUP BY playlist_title " +
    "ORDER BY playlist_title " +
    "LIMIT :limit")
List<IPlaylist> searchPlaylists(String playlistTitle, int limit);

und negativ übergeben, wenn Sie keinen Filter anwenden möchten.

return playlistDao.searchPlaylists(title, limit.isPresent() ? limit.get() : -1)

Es funktioniert in meinem Fall.

Aktualisiert [21. Dezember 2018]

Wenn Sie Kotlin verwenden, verwenden Sie den Standardwert.

@JvmOverloads
@Query("SELECT * FROM playlist " +
        "WHERE playlist_title LIKE '% :playlistTitle %' " +
        "GROUP BY playlist_title " +
        "ORDER BY playlist_title " +
        "LIMIT :limit")
fun searchPlaylists(playlistTitle: String, limit: Int = -1): List<IPlaylist>

@JvmOverloads, um es mit Java kompatibel zu machen. Es werden zwei separate Methoden für Java generiert. 

10
Moinkhan

In Room gibt es keine optionalen Parameter, aber es gibt eine @RawQuery-Annotation, in der Sie die Abfrage als String übergeben können, um Ihre SQL-Abfrage in der Laufzeit zu erstellen. Ich denke, das wird für dich funktionieren. 

Hier ist das Beispiel aus der offiziellen Dokumentation:

@Dao
 interface RawDao {
     @RawQuery
     User getUser(String query);
 }

Und so können Sie es verwenden:

User user = rawDao.getUser("SELECT * FROM User WHERE id = 3 LIMIT 1");

Wichtig: RawQuery-Methoden müssen einen nicht ungültigen Typ zurückgeben

Wichtig: Dies ist in Raum 1.1.0-alpha3 verfügbar

6
Mladen Rakonjac

Raum unterstützt @RawQuery annotation zum Erstellen von Abfragen zur Laufzeit.


Schritt 1: Erstellen Sie eine DAO-Methode

Markieren Sie die DAO-Methode mit @RawQuery-Anmerkung anstelle von normalem @RawQuery.

@Dao
interface BooksDao{
    @RawQuery
    List<Book> getBooks(SupportSQLiteQuery query);
}


Schritt 2: Konstruieren Sie die Abfrage

Room verwendet vorbereitete Anweisungen für die Überprüfung der Sicherheit und der Kompilierzeit. Daher müssen beim Erstellen von Abfragen Abfragezeichenfolge- und Bindungsparameter separat gespeichert werden.

In diesem Beispiel verwende ich die Variable queryString für die Abfragezeichenfolge und args für die Bindeparameter.

(Bitte beachten Sie, dass ich Texteditor zum Schreiben von Code verwendet habe. Daher kann es zu Tippfehlern oder einfachen Syntaxfehlern kommen. Wenn Sie etwas finden, lassen Sie es mich in den Kommentaren wissen oder bearbeiten Sie den Beitrag.)

// Query string
String queryString = new String();

// List of bind parameters
List<Object> args = new ArrayList();

boolean containsCondition = false;

// Beginning of query string
queryString += "SELECT * FROM BOOKS";

// Optional parts are added to query string and to args upon here

if(!authorName.isEmpty()){
    queryString += " WHERE";
    queryString += " author_name LIKE ?%";
    args.add(authorName);
    containsCondition = true;
}

if(fromDate!=null){

    if (containsCondition) {
        queryString += " AND";
    } else {
        queryString += " WHERE";
        containsCondition = true;
    }

    queryString += " publication_date AFTER ?";
    args.add(fromDate.getTime());
}

if(toDate!=null){

    if (containsCondition) {
        queryString += " AND";
    } else {
        queryString += " WHERE";
        containsCondition = true;
    }

    queryString += " publication_date BEFORE ?";
    args.add(toDate.getTime());
}

// End of query string
queryString += ";";


Schritt 3: Abfrage durchführen

SimpleSQLiteQuery query = new SimpleSQLiteQuery(queryString, args.toArray());
List<Book> result = booksDao.getBooks(query);




Anmerkungen

  • Wie normale Query unterstützt RawQuery die Rückgabe von rohen Cursors, Entitäten, POJOs und POJOs mit eingebetteten Feldern
  • RawQuery unterstützt Beziehungen
4
Anees

Verwenden Sie SupportSQLiteQuery.

https://developer.Android.com/reference/Android/Arch/persistence/db/SupportSQLiteQuery

Die neueste Version 1.1.1 verwendet jetzt SupportSQLiteQuery.

Eine Abfrage mit typisierten Bindungen. Es ist besser, diese API anstelle von .__ zu verwenden. rawQuery (String, String []), da der sichere Bindungstyp zulässig ist Parameter.

@Dao
     interface RawDao {
         @RawQuery(observedEntities = User.class)
         LiveData<List<User>> getUsers(SupportSQLiteQuery query);
     }

Verwendungszweck:

     LiveData<List<User>> liveUsers = rawDao.getUsers( new 
SimpleSQLiteQuery("SELECT * FROM User ORDER BY name DESC"));

Aktualisieren Sie Ihren Gradle auf 1.1.1

implementation 'Android.Arch.persistence.room:runtime:1.1.1'
implementation 'Android.Arch.lifecycle:extensions:1.1.1'
annotationProcessor "Android.Arch.persistence.room:compiler:1.1.1"

Hinweis: Wenn Sie ein Upgrade auf 1.1.1 durchführen und String anstelle von SupportSQLiteQuery verwenden,

sie erhalten den Fehler:

RawQuery lässt keine Zeichenfolge mehr zu. Benutzen Sie bitte Android.Arch.persistence.db.SupportSQLiteQuery.

Wenn Sie SupportSQLiteQuery wie oben beschrieben verwenden, wird das Problem gelöst.

Hinweis: Stellen Sie sicher, dass Sie den Abfrageparameter "SupportSQLiteQuery" übergeben. Andernfalls wird folgender Fehler angezeigt:

RawQuery-Methoden sollten 1 und nur 1 Parameter vom Typ String .__ haben. oder SupportSQLiteQuery

4
live-love
make it more simple.i will show you example using where clause using two variable.Do 
like this

  @Query("SELECT * FROM Student WHERE stdName1= :myname AND stdId1=:myid")
List<Student> fetchAllData(String myname,int myid);

stdName1 und stdId1 sind Spaltennamen

1