web-dev-qa-db-ja.com

実行時にルームデータベースを動的にクエリする方法

問題

実行時にクエリを作成することは可能ですか?


使用事例

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

limit部分はオプションです。つまり、制限なしまたは制限なしで同じクエリを実行できる必要があります。


より複雑なユースケース

前のケースでは、制限部分がある場合とない場合の2つの静的クエリを作成することが可能で、毎回適切なクエリを使用できます。ただし、フィルターの作成など、より複雑な状況に対処しなければならない場合があります。

その場合、前の例とは異なり、複数のオプション部分があります。書籍の表の場合、書籍が属するカテゴリ、著者名、価格帯、出版日などに応じてフィルタリングを行う必要がある場合があります。これらの部分のすべての組み合わせで静的クエリを作成することはほとんど不可能です。

24
Anderson K

私の経験では(短い)ルームを使用することは不可能であり、ルームの制限のためではなく、@CommonsWareによって暗黙的にコメントされているように、SQLiteの制限です。 2つのクエリが必要なので、DAOには2つのメソッドが必要です。

私は次のようなものを持っているでしょう:

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

次に、バイパスを行う別の場所:

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

それが現時点で考えることができる最良の選択肢です。

17
Juanky Soriano

複数のクエリを記述する代わりに、limit句に負の値を渡します。クエリに変更がある場合は、両方のクエリを更新する必要があるため、エラーが発生しやすくなります。

公式ドキュメント->LIMIT式が負の値に評価される場合、返される行数に上限はありません。ここで見つけることができます https://sqlite.org/lang_select.html 制限条項セクションを読んでください。

だから私はこのようなことをするだろう、

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

フィルタを適用したくない場合は負の値を渡します。

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

私の場合はうまくいっています。

更新済み[2018年12月21日]

場合kotlinを使用している場合は、デフォルト値を使用します。

@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 Javaとの互換性を確保します。 Java用に2つの個別のメソッドを生成します。

13
Moinkhan

ルームは @RawQuery アノテーションをサポートし、実行時にクエリを構築します。


ステップ1:DAOメソッドを作成する

通常の@RawQueryの代わりに@RawQueryアノテーションでDAOメソッドをマークします。

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


ステップ2:クエリを構築する

Roomは、セキュリティとコンパイル時の検証のために準備されたステートメントを使用します。したがって、クエリを構築する際に、クエリ文字列とバインドパラメータを別々に保存する必要があります。

この例では、クエリ文字列に変数queryStringを使用し、バインドパラメーターにargsを使用します。

(テキストエディタを使用してコードを記述したことに注意してください。そのため、タイプミスや単純な構文エラーが発生する可能性があります。

// 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 += ";";


ステップ3:クエリを実行する

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




ノート

  • 通常のQueryと同様に、RawQueryは未加工のカーソル、エンティティ、POJO、および埋め込みフィールドを持つPOJOを返すことをサポートします
  • RawQueryはリレーションをサポートします
8
Anees

ルームにはオプションのパラメータのようなものはありませんが、クエリを文字列として渡すことができる@RawQueryアノテーションがあり、ランタイムでSQLクエリを作成できます。これはあなたに役立つと思います。

公式ドキュメントの例を次に示します。

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

そして、それを使用する方法は次のとおりです。

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

重要: RawQueryメソッドは非void型を返す必要があります

重要:これはRoom 1.1.0-alpha3で利用可能です

7
Mladen Rakonjac

SupportSQLiteQueryを使用します。

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

最新リリース1.1.1では、SupportSQLiteQueryが使用されるようになりました。

型付きバインディングを使用したクエリ。タイプセーフなパラメーターをバインドできるため、rawQuery(String、String [])の代わりにこのAPIを使用する方が適切です。

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

使用法:

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

グラドルを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"

注:1.1.1にアップグレードし、SupportSQLiteQueryの代わりにStringを使用している場合、

エラーが表示されます:

RawQueryは文字列を渡すことを許可しなくなりました。 Android.Arch.persistence.db.SupportSQLiteQueryを使用してください。

上記のようにSupportSQLiteQueryを使用すると、問題が解決します。

注:SupportSQLiteQueryクエリパラメーターを必ず渡すと、このエラーが発生します。

RawQueryメソッドには、String型またはSupportSQLiteQuery型のパラメーターが1つだけ必要です。

4
live-love

もっとシンプルに。 2つの変数を使用するwhere句を使用した例を示します。次のようにします:

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

stdName1およびstdId1は列名です

1