サイトロゴ

Enjoy Creating
Web & Mobile Apps

MENU BOX
WEB
MOBILE
OPEN

ホーム

 > 

 > 

【Androidアプリ開発】ROOMで絞り込み検索を実装する方法

【Androidアプリ開発】ROOMで絞り込み検索を実装する方法

この記事にはプロモーションが含まれています。

ROOMは、アプリへのSQLiteデータベースの導入・操作などをシンプルにしてくれるライブラリです。ROOMの基本的な導入方法については、以前の記事で解説しているのでそちらをご参考ください。

・Androidアプリ開発でRoomを導入する方法【ローカルDB】
https://web-de-asobo.net/2024/03/22/jetpack-room/

テーブルから全ての行を取得したり、全ての行を削除したり…といった単純なクエリはとても簡単に記述できるのですが、動的にやや複雑なクエリを生成して実行したい場合は少し工夫が必要です。

そこで今回は、『シンプルな絞り込み検索』を例に、ROOMを利用して動的にクエリを実行する方法について紹介します。

この記事を読むことで分かること
  • ・ROOMで動的なクエリを定義する方法
  • ・うまくいきそうに見えてうまくいかない方法

– 目次 –

前提条件

この記事は、下記の事項を既に理解していることを前提としています。

  • ROOMの基本的な利用方法
  • SQLの基本構文
  • Kotlinの基本(Collectionなど)

これらの基本事項については、記事内でカバーしません。そのため、アプリ開発初心者の方にとってはやや説明不足のように感じられるかもしれませんが、ご了承下さい。

また、冒頭でも述べたように今回は『シンプルな絞り込み検索』を例にします。下表のように、【ID(主キー)】、【名前】、【チーム名】を持つテーブルをイメージして下さい。

テーブル名:sample_table
id name team
1 Tom A
2 Marie A
3 Dan B
4 Keiko C

ここで、チーム名で絞り込み検索を行う例を考えてみましょう。以下のパターンが考えられます。

  1. A、B、Cの全てのチームを含む
  2. Aチームのみ
  3. Bチームのみ
  4. Cチームのみ
  5. A、Bチーム(Cチームを除く)
  6. A、Cチーム(Bチームを除く)
  7. B、Cチーム(Aチームを除く)

たとえば『3つのチームのうち、AチームもしくはBチームであること』を検索の条件とする場合のSQL文は、次のように記述することができます。

SQL

SELECT * FROM sample_table WHERE team = 'A' OR team = 'B';

このようにチーム名(A〜C)を絞り込みの条件としてユーザー側で指定できるようにしたい場合、WHERE以下の条件文を動的に生成してクエリに組み込む必要があります。

この絞り込み検索の例を使って、一見問題なさそうに見えるのに動的なクエリの生成が機能しないアプローチと、期待通り動作するアプローチを確認してみたいと思います。

うまくいかないアプローチ

成功例を確認する前に、まずは陥りやすい失敗例を確認しておきましょう。

ROOMでは、コロン【 : 】を用いてクエリに受け取った引数を渡すことが可能です。具体的な使用例は下の通りです。

Kotlin

@Query("SELECT * FROM sample_table WHERE id = :id")
fun getItem(id: Int): List<MyEntity>

これを利用すれば、動的にクエリを生成して実行することも難しくないように思えるかもしれません。たとえば、チーム名を更新可能な ListSet で定義しておき、それを引数として受け取ってクエリに埋め込まれるようにする方法が考えられます。

たとえば、A、B、Cのチームのうち、AもしくはBチームであること(Cチーム以外)を絞り込みの条件とする場合のコードを次のように記述してみましょう。

kotlin

val teams = mutableSetOf('A', 'B', 'C')
teams.remove('C')

@Query("SELECT * FROM sample_table WHERE :query")
fun getItemsByQuery(query: String): List<SampleEntity>
fun getItemsByMultipleQueries(teams: Set<Char>): List<SampleEntity> {
        val query = teams.joinToString(" OR ") { "team = '$it'" } // team = 'A' OR team = 'B'
        return getItemsByQuery(query)
}

最終的にクエリは下のようになりますが、この部分だけ見ると完璧で何の問題もないように思えます。

SQL

SELECT * FROM sample_table WHERE team = 'A' OR team = 'B'

しかし、最初に言ったようにこれは失敗してしまう例です。仮にこの状態でアプリをビルドしてクエリを実行しても、テーブルから行は一行も取得することができません。(アプリのビルド自体は成功します)

これは、動的に生成されたクエリが全て単なる文字列として解釈されてしまい、 =OR といった特別な意味をもつ語句が機能しなくなるためだと思われます。

では、どうすればいいのでしょうか?次のステップで期待通りにちゃんと機能するケースを確認していきましょう。

期待通りの結果が得られるアプローチ

絞り込み検索など、ユーザーの指定に応じて動的にクエリを生成したい場合は @RawQueryアノテーションを利用します。これにより、動的に生成したSQLクエリを実行できるようになります。

Kotlin

@RawQuery
suspend fun getItemsByQuery(query: SupportSQLiteQuery): List<SampleEntity>

@RawQueryアノテーションで定義されたメソッドは、SupportSQLiteQueryオブジェクトをパラメタとして受け取る必要があります。SupportSQLiteQuery は、動的なSQLクエリを安全に実行するためのクラスです。

以上を踏まえて上記のコードの続きを記述すると、下のようになります。

Kotlin

@RawQuery
suspend fun getItemsByQuery(query: SupportSQLiteQuery): List<SampleEntity>
suspend fun getItemsByMultipleQueries(teams: Set<Char>): List<SampleEntity> {
   val placeholders = teams.joinToString(" OR ") { "team = ?" }
   val args = teams.map { "$it" }.toTypedArray()
   val query = SimpleSQLiteQuery("SELECT * FROM sample_table WHERE $placeholders", args)
   return getItemsByQuery(query)
}

teamsパラメタとして受け取った Set<Char> から動的にクエリを生成するためのプレースホルダー(placeholdersとして定義)及び、Array(argsとして定義)を宣言し、それぞれ、SimpleSQLiteQuery に渡すことで動的なクエリが生成されます。

最終的に、getItemsByQueryメソッドにクエリ(SupportSQLiteQuery)を渡して実行することで、動的なクエリが完全に実行され、結果(List)を得ることができます。

下の画像は、実際に絞り込み検索が正常に機能していることを表すものです。

動的なクエリが期待通り動作しているスクリーンショット画像

RawQueryはAndorid SQLite APIにおける概念であり、SQLiteやSQLについて調べても情報が全く出てこない点には注意が必要です。

RawQuery(Android SQLiteのクエリ)についての基本は、下の公式ドキュメントからご確認いただけます。

・SQLite 入門(Android SQLite のクエリ)
https://developer.android.com/courses/extras/sql-primer?hl=ja#queries-for-android-sqlite

最初はシンプルな構造のテーブルを使って、色々なクエリを試してみると理解しやすいかと思います。


« »

カテゴリーリンク

著者について- author profile -

ROYDOプロフィール写真
Michihiro

モバイルアプリ(iOS・Android)ディベロッパー&デザイナー

これまでに、可読性の高いカラーパターンを自動で生成するアプリや、『第3火曜日』といった形式で通知をスケジュールできるアプリなどを制作。

サブでWebデザイン・フロントエンドエンジニアとしても活動しています。

📝ツール・言語:JavaScript/React Native/Kotlin/Android Studio/Swift/SwiftUI

🎓資格:応用情報技術者/基本情報技術者/ウェブデザイン技能検定3級

Twitterアイコン Instagramアイコン