データベースへのクエリをまとめて発行したい場合は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のアトミック性を保証する」を参照
以上です。