【AndroidでSQLiteを制する】SQLiteDatabase#queryWithFactoryを使ってセレクトをする

このエントリーを Google ブックマーク に追加
Pocket
[`yahoo` not found]

SQLデータベース内のテーブルに対してクエリを実行します。
今回はSQLiteDatabaseに定義されているSQLiteDatabase#queryWithFactoryの使い方を説明します。
このメソッドを使うと自分が用意したカーソルクラスを利用してクエリの結果にアクセスすることができます。

SQLiteDatabase#queryWithFactoryのオーバーロード

SQLiteDatabase#queryWithFactoryには2つのオーバーロードが存在します。

オーバーロード 1

SQLiteDatabase.CursorFactory cursorFactory Cursorを継承したクラスを作成するためのファクトリクラスです。
SQLiteDatabase#queryを使用した時やnullを渡した時はSQLiteCursorが使用されます。
boolean distinct trueを指定すると出力するデータから重複行を削除します。
重複行を削除しない場合はfalseを指定します。
String table クエリに対象テーブルを指定します。
この引数がFROM句で使用されるので「 FROM table 」が生成されます。
String[] columns クエリの結果に含まれるカラムを複数指定します。
「SELECT columns[0],columns[1]….」が生成されます。
nullを指定すると「SELECT *」になりすべてのカラムが含まれます。
String selection WHERE句を指定します。
「[カラム名] = ? AND [カラム名] = ? 」のようにクエリに条件を設定します。
?はselectionArgsで指定します。
WHERE句が不要な場合はnullを指定してください。
String[] selectionArgs String selectionで指定した「?」を埋めるための文字列を配列で指定できます。
「?」の数よりも配列の要素数が多い場合は例外が発生します。
WHERE句に「?」が存在しない場合はnullを指定してください。
String groupBy GROUP BY句を設定します。
GROUP BY句が不要な場合はnullを設定してください。
String having HAVING句を設定します。
HAVING句が不要な場合はnullを設定してください。
String orderBy ORDER BY句を設定します。
ORDER BY句が不要な場合はnullを設定してください。
String limit LIMIT句を設定します、OFFSET句もここに書きます。
LIMIT句が不要な場合はnullを設定してください。

オーバーロード 2

SQLiteDatabase.CursorFactory cursorFactory Cursorを継承したクラスを作成するためのファクトリクラスです。
SQLiteDatabase#queryを使用した時やnullを渡した時はSQLiteCursorが使用されます。
boolean distinct trueを指定すると出力するデータから重複行を削除します。
重複行を削除しない場合はfalseを指定します。
String table クエリに対象テーブルを指定します。
この引数がFROM句で使用されるので「 FROM table 」が生成されます。
String[] columns クエリの結果に含まれるカラムを複数指定します。
「SELECT columns[0],columns[1]….」が生成されます。
nullを指定すると「SELECT *」になりすべてのカラムが含まれます。
String selection WHERE句を指定します。
「[カラム名] = ? AND [カラム名] = ? 」のようにクエリに条件を設定します。
?はselectionArgsで指定します。
WHERE句が不要な場合はnullを指定してください。
String[] selectionArgs String selectionで指定した「?」を埋めるための文字列を配列で指定できます。
「?」の数よりも配列の要素数が多い場合は例外が発生します。
WHERE句に「?」が存在しない場合はnullを指定してください。
String groupBy GROUP BY句を設定します。
GROUP BY句が不要な場合はnullを設定してください。
String having HAVING句を設定します。
HAVING句が不要な場合はnullを設定してください。
String orderBy ORDER BY句を設定します。
ORDER BY句が不要な場合はnullを設定してください。
String limit LIMIT句を設定します、OFFSET句もここに書きます。
LIMIT句が不要な場合はnullを設定してください。
CancellationSignal cancellationSignal クエリを途中で中断するキャンセル要求やキャンセル時のコールバックを管理するクラスです。
このインスタンスを経由して外部からクエリを中断することが可能になります。
CancellationSignal#cancelを実行するとOperationCanceledExceptionがスローされます。

SQLiteCursorを継承したクラス

SQLiteCursorを継承してBookテーブル専用のカーソルを作成します。
このクラスには現在のカーソル位置のデータをDTOに変換するメソッドを実装します。
本来はデータを検証する処理を差し込むために使うらしく、正直この使い方であっているのかわからない。
  public class BookCursor extends SQLiteCursor {

       public BookCursor(SQLiteCursorDriver driver, String editTable, SQLiteQuery query) {
           super(driver, editTable, query);
       }

       public int getId(){
        return getInt(getColumnIndex(LoadToSQLiteMasterContract.Book._ID));
       }

       public String getTitle(){
           return getString(getColumnIndex(LoadToSQLiteMasterContract.Book.COLUMN_NAME_TITLE));
       }

       public String getSubTitle(){
           return getString(getColumnIndex(LoadToSQLiteMasterContract.Book.COLUMN_NAME_SUBTITLE));
       }

       public BookDTO getDTO(){
           BookDTO bookDTO = new BookDTO();
           if(getColumnIndex(LoadToSQLiteMasterContract.Book._ID) != -1) {
               bookDTO.setId(getId());
           }
           if(getColumnIndex(LoadToSQLiteMasterContract.Book.COLUMN_NAME_TITLE) != -1) {
               bookDTO.setTitle(getTitle());
           }
           if(getColumnIndex(LoadToSQLiteMasterContract.Book.COLUMN_NAME_SUBTITLE) != -1) {
               bookDTO.setSubtitle("[BookCursor]" + getSubTitle());
           }
           return bookDTO;
       }
   }

SQLiteDatabase.CursorFactoryを実装する

SQLiteDatabase.CursorFactory#newCursorを実装する必要があります。
このメソッドの第3引数はこれからアクセスするテーブル名です、このテーブル名がBookの場合にBookCursorを返却するように実装します。
  public class LoadToSQLiteMasterCursorFactory implements SQLiteDatabase.CursorFactory {
    @Override
    public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery, String editTable, SQLiteQuery query) {
        if(LoadToSQLiteMasterContract.Book.TABLE_NAME.equals(editTable)) {
            return new BookCursor(masterQuery , editTable , query);
        } else {
            return new SQLiteCursor(masterQuery,editTable,query);
        }
    }
  }

SQLiteDatabase#queryWithFactoryを実際に使用する

BookCursorとSQLiteDatabase.CursorFactoryを使ってselectWithFactoryを実装します。
BookDAOにselectWithFactoryメソッドを実装します。
メソッドの中でBookCursorを引数にもつBookDAO#convertCursorToDTOを使いBookDTOのリストに変換して返却します。
  public static List<BookDTO> selectWithFactory(Context context){
      SQLiteDatabase sqLiteDatabase = getSqLiteDatabase(context,false);
      LoadToSQLiteMasterCursorFactory.BookCursor cursor = (LoadToSQLiteMasterCursorFactory.BookCursor) sqLiteDatabase.queryWithFactory(
              new LoadToSQLiteMasterCursorFactory(),
              false,
              LoadToSQLiteMasterContract.Book.TABLE_NAME,
              null,
              null,
              null,
              null,
              null,
              null,
              null
      );
      List<BookDTO> bookDTOList = convertCursorToDTO(cursor);
      return bookDTOList;
  }

  private static List<BookDTO> convertCursorToDTO(LoadToSQLiteMasterCursorFactory.BookCursor cursor) {
    List<BookDTO> ret = new ArrayList<>();
    while(cursor.moveToNext()){
        BookDTO bookDTO = cursor.getDTO();
        ret.add(bookDTO);
    }
    return ret;
  }