ContentProviderとトランザクション

2,068 views
Skip to first unread message

あわ

unread,
Oct 9, 2009, 2:02:32 PM10/9/09
to android-g...@googlegroups.com
あわと申します。

AndroidのContentProviderを使っていて、疑問に思ったので質問です。

ContentProviderを使って、複数テーブルに対して複数レコードの更新を行う場合、
トランザクションを利用してエラーが発生した際にデータの不整合が
起きないようにロールバックをさせようと思ったのですが、
参考になるやり方が見つかりませんでした。

ContentProviderのリファレンスを見ると、
If you don't need to share data amongst multiple applications you can use a database directly viaSQLiteDatabase.
とあり、単に自アプリ内でのデータの更新にはSQLiteDatabaseを直接扱ってね、的なことが書いてあります。
確かに、それはそれでトランザクションの機能が利用できるし、理解できるのですが、
他のアプリケーションがContentProvider経由でデータの操作を許可している場合、
トランザクション機能を利用したいケースについてはどうやって実装すればよいのでしょうか?

そもそもこんな疑問を持つこと自体ナンセンスなのでしょうか?
どうぞよろしくお願いいたします。

--
aww...@gmail.com

Isher

unread,
Oct 30, 2009, 9:32:40 AM10/30/09
to 日本Androidの会
こんばんわ。
同じ事で悩んでいたのですが、一つの解決策を見つけました。

SQLiteはtransactionを利用するのとしないのとでは、数十件程度の更新でも処理時間に大きな違いが出るので、複数件のデータを扱うなら
是非利用したいです。
どうしてContentProviderにtransactionを扱う機能が無いのか不思議なのですが、無理矢理実装すれば使えなくもなさそうで
す。

ContentProviderの各処理は、受け取ったUriによって更新させるテーブルを分岐させていると思いますが、insert()に
transactionの開始とcommitとrollbackを行う分岐を追加してやります。下の2,3,4がそれです。

static{
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI("com.test.provider", "insert", 1);
sUriMatcher.addURI("com.test.provider", "transaction", 2);
sUriMatcher.addURI("com.test.provider", "commit", 3);
sUriMatcher.addURI("com.test.provider", "rollback", 4);
}

@Override
public Uri insert(Uri arg0, ContentValues arg1) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
switch (sUriMatcher.match(arg0)) {
case 1:
db.insert("test", null, arg1);
break;
case 2:
db.beginTransaction();
break;
case 3:
db.setTransactionSuccessful();
db.endTransaction();
break;
case 4:
db.endTransaction();
break;
}
return null;
}


そして、ContentProviderを使用する際は、最初にbeginTransaction()を実行するUriを発行してやり、更新処理を行っ
た後、commitかrollbackを実行するUriを発行します。

//transaction開始
getContentResolver().insert(Uri.parse("content://com.test.provider/
transaction"),null);
//色んな処理
getContentResolver().update(~~~)
getContentResolver().delete(~~~)
getContentResolver().insert(~~~)
//commit
getContentResolver().insert(Uri.parse("content://com.test.provider/
commit"),null);

色々試してみましたが、期待通りの動きをしてくれます。途中で失敗すればrollbackされますし、登録、更新、削除が入り乱れていても高速に処理出
来ます。
本当に無理矢理ですけど、いかがでしょうか。



On 10月10日, 午前3:02, あわ <awwa...@gmail.com> wrote:
> あわと申します。
>
> AndroidのContentProviderを使っていて、疑問に思ったので質問です。
>
> ContentProviderを使って、複数テーブルに対して複数レコードの更新を行う場合、
> トランザクションを利用してエラーが発生した際にデータの不整合が
> 起きないようにロールバックをさせようと思ったのですが、
> 参考になるやり方が見つかりませんでした。
>
> ContentProviderのリファレンスを見ると、
> If you don't need to share data amongst multiple applications you can use a
> database directly
> viaSQLiteDatabase<file:///C:/android-sdk-windows-1.6_r1/docs/reference/andr oid/database/sqlite/SQLiteDatabase.html>
> .
> とあり、単に自アプリ内でのデータの更新にはSQLiteDatabaseを直接扱ってね、的なことが書いてあります。
> 確かに、それはそれでトランザクションの機能が利用できるし、理解できるのですが、
> 他のアプリケーションがContentProvider経由でデータの操作を許可している場合、
> トランザクション機能を利用したいケースについてはどうやって実装すればよいのでしょうか?
>
> そもそもこんな疑問を持つこと自体ナンセンスなのでしょうか?
> どうぞよろしくお願いいたします。
>
> --
> awwa...@gmail.com

あわ

unread,
Nov 2, 2009, 11:06:36 PM11/2/09
to android-g...@googlegroups.com
こんにちは。

なるほど!
こういう実装の仕方は思いつきませんでした。
よくよく考えてみたら、ContentProviderの裏でデータの扱いをしているのが必ずしもSQLiteとは限らないわけですから、
SQLiteのトランザクションを使いたければ相応のI/Fを作ってやればよいわけですね。
とてもすっきりしました。

2009年10月30日22:32 Isher <mat...@gmail.com>:



--
----
Wataru Sato
Reply all
Reply to author
Forward
0 new messages