データベースへのクエリをまとめて発行したい場合はapplyBatchの使用を検討します。
●applyBatchのメリット
・ContentResolver経由で1つずつクエリ実行するより高速これは、ContentResolver.applyBatch内部でContentProviderClientを使用するためです。
引用:android.content.ContentResolver.applyBatch(String, ArrayList<ContentProviderOperation>)
public ContentProviderResult[] applyBatch(String authority, ArrayList<ContentProviderOperation> operations) throws RemoteException, OperationApplicationException { ContentProviderClient provider = acquireContentProviderClient(authority); if (provider == null) { throw new IllegalArgumentException("Unknown authority " + authority); } try { return provider.applyBatch(operations); } finally { provider.release(); } }ContentProviderClientによる高速化については過去の記事で取り上げています。
⇒高速化。ContentResolver?ContentProviderClient?
・トランザクション制御が容易
デフォルトではapplyBatchのアトミック性は保証されません。
これを保証するにはContentProviderのapplyBatchをオーバーライドしてトランザクション処理を追加します。
⇒bulkInsertとapplyBatchのアトミック性を保証する
●applyBatchのデメリット
・クエリの組み立てが若干複雑クエリの組み立て方に独特なルールがあります。
また、後方参照を使用しなければならないケースでは可読性が下がります。
といっても、理解すれば問題ないレベルです。
・クエリが途中で失敗した場合の対処
まず、applyBatchがアトミック性を保証していないということ。
さらに、クエリが途中で失敗すると、それ以降のクエリは実行されないということ。
この2点を念頭においてapplyBatchを使用する必要があります。
applyBatchのメリット・デメリットを列挙したところで、本稿ではapplyBatchの概要と、これを使用するためのContentProviderOperationについて記載します。
●概要
applyBatchはクエリ群をContentProviderOperationのリストとして受け取ります。ContentProviderOperationはinsert/update/deleteクエリを表現するクラスです。
クエリは、ContentProviderOperationに設定した値をもとに生成・発行されます。
⇒ContentResolver.applyBatch (String, ArrayList<ContentProviderOperation>)
⇒ContentProviderOperation
●ContentProviderOperation.Builder
ContentProviderOperationはContentProviderOperation.Builderクラスから生成します。⇒ContentProviderOperation.Builder
ContentProviderOperationを構築する簡単なサンプルは下記です。
// Insert文のオペレーションを生成 ContentProviderOperation.newInsert(uri).withValue("name", "test1").build() // Update文のオペレーションを生成 ContentProviderOperation.newUpdate(uri).withValue("name", "test2").build() // Delete文のオペレーションを生成 ContentProviderOperation.newDelete(uri).build()ContentProviderOperation.Builderは用途にあわせてInsert用、Update用、Delete用が用意されています。
ビルダは"どの種類のビルダなのか"を明示してインスタンスを生成します。
- newInsert(uri) : Insert用ビルダを生成
- newUpdate(uri) : Update用ビルダを生成
- newDelete(uri) : Delete用ビルダを生成
●ContentProviderOperationの構築
ContentProviderOperation.Builderには、ContentProviderOperationを構築するためのメソッドが用意されています。各メソッドはInsert文に特化したものや、Update/Delete文に特化したものがあります。
例えばInsert用ビルダでは、Update/Deleteに特化したwithSelection(...)メソッドは使用できません。
各ビルダ種別毎の使用可能/不可能メソッドの一覧はContentProviderOperation.Builderのjavadocで確認できます。
もし、使用不可能なメソッドを呼び出すと例外が投げられます。
# Insert用ビルダがwithSelectionメソッドを呼び出した場合 java.lang.IllegalArgumentException: only updates, deletes, and asserts can have selections
主なメソッドを下記に列挙します。
InsertやUpdateクエリで追加値・更新値を指定するには下記のメソッドを使用します。
- ContentProviderOperation.Builder.withValues(ContentValues)
- ContentProviderOperation.Builder.withValueBackReference(String, int)
- ContentProviderOperation.Builder.withValueBackReference(ContentValues)
***BackReferenceは後方参照です。これについては次回投稿します。
withValuesメソッドを使用した簡単なサンプルは下記です。
// nameにtest1を持つレコードを追加するInsert文 ContentProviderOperation.newInsert(uri).withValue("name", "test1").build();ContentProviderOperationで表現されるクエリは条件(selection)の指定が可能です。
条件指定には下記のメソッドを使用します。
- ContentProviderOperation.Builder.withSelection(String, String[])
- ContentProviderOperation.Builder.withSelectionBackReference(int, int)
****BackReferenceは後方参照です。これについては次回投稿します。
withSelectionメソッドを使用した簡単なサンプルは下記です。
// _idが3のレコードのnameを"test5"に更新するUpdate文 ContentProviderOperation.newUpdate(uri).withValue("name", "test5") .withSelection("_id=?", new String[]{"3"}).build();
●ContentProviderOperationの結果オブジェクト
applyBatchは複数のクエリ(ContentProviderOperation)を実行します。そのため、複数のクエリ結果をContentProviderResultの配列で返します。
ContentProviderResultはuriとcountを持つシンプルなクラスです。
Insertクエリの結果はuriに、Update/Deleteの結果はcountに格納されます。
⇒ContentProviderResult
applyBatchで実行される各クエリの結果を参照したい場合はContentProviderResultを参照します。
●applyBatchの実行
applyBatchを実行する簡単なコードは下記です。ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(); Uri uri = Uri .parse("content://yuki.contentprovider.mycontentprovider"); operations.add(ContentProviderOperation.newInsert(uri) .withValue("name", "test1").build()); operations.add(ContentProviderOperation.newUpdate(uri) .withValue("name", "test2").build()); try { getContentResolver().applyBatch( "yuki.contentprovider.mycontentprovider", operations); } catch (Exception e) { Log.e("yuki", "error"); }これを実行すると、Insert文とUpdate文がまとめて実行されます。
クエリはArrayList(operations)の先頭から順番に実行されます。
もし途中でクエリの実行が失敗すると、それ以降のクエリは実行されません。
# applyBatchにアトミック性を求めるなら「bulkInsertとapplyBatchのアトミック性を保証する」を参照
以上です。