Androidアプリ開発 SQLiteデータベースを使用する(トランザクション) SQLポケリ [Androidアプリ開発]
前回の記事
SQLite Androidアプリ開発 SQLiteデータベースを使用する
SQLiteは小さいデータベースシステムではあるが、ちゃんとトランザクションをサポートしている。トランザクションをかけておけば、エラーになったときにまとめて「なかったこと」にできるので便利。
また、大量のデータをINSERTする場合は、トランザクションをかけた方が高速。
普通のデータベースでは、BEGIN TRANSACTIONによりトランザクションを開始して、INSERTやDELETEでレコードを追加、削除する。処理に成功したら、COMMIT TRANSACTIONでコミットしてトランザクションを閉じる。途中でエラーになったら、ROLLBACK TRANSACTIONでトランザクションをかける前の状態に戻す。
Javaのコードで書くと以下のような感じになる。
AndroidのSQLiteDatabaseクラスにもトランザクションを制御するメソッドが備わっているが、少々使い方が異なる。
SQLiteDatabase#beginTransactionメソッドを呼び出すことでトランザクションが開始される。SQL命令でいうところのBEGIN TRANSACTIONを実行したことに等しい。
レコードの追加や削除が終了し、それを確定させたい場合、SQLiteDatabase#setTransactionSuccessfulメソッドを呼び出す。これは、SQL命令のCOMMIT TRANSACTIONと同じではない。まだ確定はしていない。上記の例でsuccessのフラグを立てた状態と同じ。
beginTransactionで開始したトランザクションは、endTransactionで終了させる。successフラグが立っていれば(setTransactionSuccessfulが呼び出されていれば)、COMMIT TRANSACTIONと同じ。successフラグが立っていなければ、ROLLBACK TRANSACTIONと同じになる。
つまりですね。BEGIN TRANSACTIONに対応するメソッドはあるが、COMMIT TRANSACTIONとROLLBACK TRANSACTIONに対応するメソッドがないっていうこと。
トランザクションは、endTransactionで終了させるが、そのときにCOMMITされるかROLLBACKされるかは、setTransactionSuccessfullが呼び出されたかどうかで決定される。
えーと、こんな説明で分かって頂けたでしょうか。Javaのコードにしたらもっと簡単です。
processData内で例外が発生すると、setTransactionSuccessfulは実行されないので、endTransactionではロールバックされる。
例外が発生せず、setTransactionSuccessfulが呼び出されれば、endTransactionでコミットされる。
ロック
トランザクションとくれば、次はロックでしょう。Androidはマルチタスクやスレッドが可能なので、データの更新が競合する可能性がある。
SQLiteでのロックの粒度は、データベース単位。Oracleなどでは行単位のロックが可能。SQLiteは小さい組み込み用のデータベースなので、ロックの粒度はおおざっぱでかまわないのであろう。
どういうことかというと、あるコードでデータベースを更新している間は、他のコードからはデータベースを参照することができないようにロックがかかる。Oracleなどの高機能なデータベースは、行単位などの細かい粒度でロックされるが、SQLiteの場合は、データベース単位でロックされる。ということ。
テーブル単位ではなく、データベース単位。
どういうことかというと、どこかのスレッドでトランザクションを開始したら、そのデータベースは、そのスレッドがロック保持者になる。別のスレッドからデータベースにアクセスできない状態になる。
別のテーブルに書き込むからOKでしょ。というわけにはいかない。
トランザクションのネスト
トランザクションをネストさせる場合を考えてみて欲しい。ネストというのは、入れ子にすること。トランザクションの中でまたトランザクションをかけるのである。
transaction1の値を持つレコードはINSERTで作成されるが、transaction2のレコードは追加されない。
もし、外側のトランザクションがロールバックされれば、transaction1のレコードも追加されない。
トランザクションの開始、コミット、ロールバックをリスナーで受ける
トランザクションをbeginTransactionWithListenerで開始するとその状態変化をリスナーで受け取ることができる。ちょとやってみる。
SQLiteTransactionListenerインターフェースを実装したクラスで通知を受けることができる。ロールバックした際にエラーを表示する、といった用途に使用できそう。
お約束の宣伝を... SQLについての詳細は、手前味噌で恐縮ではあるが「SQLポケットリファレンス」を参照されたし。
「改訂第4版SQLポケットリファレンス」は、SQLiteにも対応しています。
関連記事
SQLite Androidアプリ開発 SQLiteデータベースを使用する
「SQLite ODBC」 Excel AccessからSQLiteデータベースを使用する
「SQLite for Excel」 Excel VBAからSQLiteデータベースを使用する
SQLiteでのデータ型
SQLポケットリファレンス 台湾版SQLポケットリファレンス 台湾版の紹介
SQLポケリが第4版になりました
「かんたんAndroidアプリ作成入門」には、sqliteのサンプルもあります。
サイト内を検索
SQLite Androidアプリ開発 SQLiteデータベースを使用する
SQLiteは小さいデータベースシステムではあるが、ちゃんとトランザクションをサポートしている。トランザクションをかけておけば、エラーになったときにまとめて「なかったこと」にできるので便利。
また、大量のデータをINSERTする場合は、トランザクションをかけた方が高速。
普通のデータベースでは、BEGIN TRANSACTIONによりトランザクションを開始して、INSERTやDELETEでレコードを追加、削除する。処理に成功したら、COMMIT TRANSACTIONでコミットしてトランザクションを閉じる。途中でエラーになったら、ROLLBACK TRANSACTIONでトランザクションをかける前の状態に戻す。
Javaのコードで書くと以下のような感じになる。
db.execSQL("BEGIN TRANSACTION"); // トランザクション開始 try { processData(); // データを追加したり削除したり success = true; } catch(Exception e){ // 例外発生 e.printStackTrace(); success = false; } if ( success ){ db.execSQL("COMMIT TRANSACTION"); // コミット } else { db.execSQL("ROLLBACK TRANSACTION"); // ロールバック }
AndroidのSQLiteDatabaseクラスにもトランザクションを制御するメソッドが備わっているが、少々使い方が異なる。
SQLiteDatabase#beginTransactionメソッドを呼び出すことでトランザクションが開始される。SQL命令でいうところのBEGIN TRANSACTIONを実行したことに等しい。
レコードの追加や削除が終了し、それを確定させたい場合、SQLiteDatabase#setTransactionSuccessfulメソッドを呼び出す。これは、SQL命令のCOMMIT TRANSACTIONと同じではない。まだ確定はしていない。上記の例でsuccessのフラグを立てた状態と同じ。
beginTransactionで開始したトランザクションは、endTransactionで終了させる。successフラグが立っていれば(setTransactionSuccessfulが呼び出されていれば)、COMMIT TRANSACTIONと同じ。successフラグが立っていなければ、ROLLBACK TRANSACTIONと同じになる。
つまりですね。BEGIN TRANSACTIONに対応するメソッドはあるが、COMMIT TRANSACTIONとROLLBACK TRANSACTIONに対応するメソッドがないっていうこと。
トランザクションは、endTransactionで終了させるが、そのときにCOMMITされるかROLLBACKされるかは、setTransactionSuccessfullが呼び出されたかどうかで決定される。
えーと、こんな説明で分かって頂けたでしょうか。Javaのコードにしたらもっと簡単です。
db.beginTransaction(); // トランザクション開始 try { processData(); // データを追加したり削除したり db.setTransactionSuccessful(); // 成功 } catch(Exception e){ // 例外発生 e.printStackTrace(); } finally{ db.endTransaction(); // トランザクション終了 }
processData内で例外が発生すると、setTransactionSuccessfulは実行されないので、endTransactionではロールバックされる。
例外が発生せず、setTransactionSuccessfulが呼び出されれば、endTransactionでコミットされる。
ロック
トランザクションとくれば、次はロックでしょう。Androidはマルチタスクやスレッドが可能なので、データの更新が競合する可能性がある。
SQLiteでのロックの粒度は、データベース単位。Oracleなどでは行単位のロックが可能。SQLiteは小さい組み込み用のデータベースなので、ロックの粒度はおおざっぱでかまわないのであろう。
どういうことかというと、あるコードでデータベースを更新している間は、他のコードからはデータベースを参照することができないようにロックがかかる。Oracleなどの高機能なデータベースは、行単位などの細かい粒度でロックされるが、SQLiteの場合は、データベース単位でロックされる。ということ。
テーブル単位ではなく、データベース単位。
どういうことかというと、どこかのスレッドでトランザクションを開始したら、そのデータベースは、そのスレッドがロック保持者になる。別のスレッドからデータベースにアクセスできない状態になる。
別のテーブルに書き込むからOKでしょ。というわけにはいかない。
トランザクションのネスト
トランザクションをネストさせる場合を考えてみて欲しい。ネストというのは、入れ子にすること。トランザクションの中でまたトランザクションをかけるのである。
db.beginTransaction(); // トランザクション開始 try { db.beginTransaction(); // 子トランザクション1開始 try { db.execSQL("INSERT INTO sample VALUES('transaction1')"); db.setTransactionSuccessful(); // 成功 } finally { db.endTransaction(); // 子トランザクション1終了 } db.beginTransaction(); // 子トランザクション2開始 try { db.execSQL("INSERT INTO sample VALUES('transaction2')"); } finally { db.endTransaction(); // 子トランザクション2終了 } db.setTransactionSuccessful(); // 成功 } finally { db.endTransaction(); // トランザクション終了 }
transaction1の値を持つレコードはINSERTで作成されるが、transaction2のレコードは追加されない。
もし、外側のトランザクションがロールバックされれば、transaction1のレコードも追加されない。
トランザクションの開始、コミット、ロールバックをリスナーで受ける
トランザクションをbeginTransactionWithListenerで開始するとその状態変化をリスナーで受け取ることができる。ちょとやってみる。
public class TestSQLiteActivity extends Activity implements SQLiteTransactionListener { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); MySQLiteOpenHelper helper = new MySQLiteOpenHelper(this); SQLiteDatabase db = helper.getWritableDatabase(); db.beginTransactionWithListener(this); try { db.execSQL("INSERT INTO sample(name) VALUES('test data')"); db.setTransactionSuccessful(); } catch(Exception e){ // 例外発生 e.printStackTrace(); } finally { db.endTransaction(); } Cursor c = db.rawQuery("SELECT * FROM sample", null); boolean hasRecord = c.moveToFirst(); while(hasRecord) { android.util.Log.v("database sample", c.getInt(0) + "," + c.getString(1)); hasRecord = c.moveToNext(); } c.close(); db.close(); } @Override public void onBegin() { android.util.Log.i("TestSQLite", "transaction begin"); } @Override public void onCommit() { android.util.Log.i("TestSQLite", "transaction commit"); } @Override public void onRollback() { android.util.Log.i("TestSQLite", "transaction rollback"); } }
SQLiteTransactionListenerインターフェースを実装したクラスで通知を受けることができる。ロールバックした際にエラーを表示する、といった用途に使用できそう。
お約束の宣伝を... SQLについての詳細は、手前味噌で恐縮ではあるが「SQLポケットリファレンス」を参照されたし。
「改訂第4版SQLポケットリファレンス」は、SQLiteにも対応しています。
関連記事
SQLite Androidアプリ開発 SQLiteデータベースを使用する
「SQLite ODBC」 Excel AccessからSQLiteデータベースを使用する
「SQLite for Excel」 Excel VBAからSQLiteデータベースを使用する
SQLiteでのデータ型
SQLポケットリファレンス 台湾版SQLポケットリファレンス 台湾版の紹介
SQLポケリが第4版になりました
「かんたんAndroidアプリ作成入門」には、sqliteのサンプルもあります。
かんたんAndroidアプリ作成入門 (プログラミングの教科書)
- 作者: 朝井 淳
- 出版社/メーカー: 技術評論社
- 発売日: 2013/04/16
- メディア: 単行本(ソフトカバー)
サイト内を検索
Copyright Atsushi Asai Google+朝井淳
[データベースの気持ちがわかる]SQLはじめの一歩 (WEB+DB PRESS plus)
- 作者: 朝井 淳
- 出版社/メーカー: 技術評論社
- 発売日: 2015/03/03
- メディア: 単行本(ソフトカバー)
Access クエリ 徹底活用ガイド ~仕事の現場で即使える
- 作者: 朝井 淳
- 出版社/メーカー: 技術評論社
- 発売日: 2018/05/25
- メディア: 大型本
コメント 0