トランザクションのネスト
SQLを実行してデータを登録処理などします。
しかし、状況によっては必ずしもコミットになる前にデータエラーとなり
ロールバックが発生することもあります。
しかし、状況をログとして残したい場合にはトランザクションをネストすることにより
エラーをログテーブルに格納することができます。
このような例などに使用するトランザクションをネストするサンプルとなります。
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.util.Log;
/*
エラーとなるDMLを実行しても大元のネストのDMLが実行されることを
確認するテストです。
*/
public class clsDatabaseTransactionNest extends clsDatabaseOpen {
clsDatabaseTransactionNest(Context context)
{
super(context);
}
//SQLiteStatementを使用した実行するサンプル
public int testTest1()
{
//変数宣言
int iResult = 0;
int iError = 0;
String sSQL = "";//内側のネスト
String sSQL2 = "";//大元のネスト
SQLiteStatement stmt = null;//内側のネスト
SQLiteStatement stmt2 = null;//大元のネスト
try
{
//大元のネスト開始(トランザクション)
super.db.beginTransaction();
//ログに使用するテーブルをクリアします
sSQL2=" delete from tbl3";
stmt2 = super.db.compileStatement(sSQL2);
iResult = stmt2.executeUpdateDelete();
Log.d("iResult",String.valueOf(iResult));
if(iResult < 0)
{
//updateエラー
return 2;
}
//logとして使用するデータを登録します
sSQL2=" insert into tbl3 values(1,1,'test1 update before')";
stmt2 = super.db.compileStatement(sSQL2);
iResult = stmt2.executeUpdateDelete();
if(iResult < 0)
{
//updateエラー
return 2;
}
//エラーとなるDMLを実行します
try
{
//実行用のトランザクションを設定します
super.db.beginTransaction();
//エラーのDMLを発行します
//ここでは次のエラーが発生するため内側の
//トランザクションデータは反映されません
//E/SQLiteLog: (1) near "fld2": syntax error
// D/error: near "fld2": syntax error (code 1):
// , while compiling: update tbl1 fld2 = 10,fld3 = 'a10'
sSQL=" update tbl1 fld2 = 10,fld3 = 'a10'";
stmt = super.db.compileStatement(sSQL.toString());
iResult = stmt.executeUpdateDelete();
if(iResult > -1)
{
//更新件数が0件以上であれば実行できているのでコミットしておく
super.db.setTransactionSuccessful();
iError = 3;
}
}
catch (SQLException e3)
{
Log.d("error",e3.getMessage().toString());
iError = 3;
//returnで終わりにはしないで、ネストの元に戻すため
}
catch (Exception e4)
{
Log.d("error",e4.getMessage().toString());
iError = 3;
//returnで終わりにはしないで、ネストの元に戻すため
}
finally
{
super.db.endTransaction();
//ここでは確定処理をしないで大元のネストで行っています
stmt=null;
}
//ログに登録するデータを追加します。
sSQL2=" insert into tbl3 values(2,1,'test1 update end')";
stmt2 = super.db.compileStatement(sSQL2);
iResult = stmt2.executeUpdateDelete();
if(iResult < 0)
{
//updateエラー
//確定処理をしないで終わらさせる(ロールバック)
return 2;
}
//コミットします
super.db.setTransactionSuccessful();
//大元のネスト終了(コミット済み)
}
catch (SQLException e1)
{
Log.d("error",e1.getMessage().toString());
Log.d("testTest1","setTransactionSuccessfulをさせずにendTransactionで確定させる");
return 1;
}
catch (Exception e2)
{
Log.d("error",e2.getMessage().toString());
Log.d("testTest1","setTransactionSuccessfulをさせずにendTransactionで確定させる");
return 1;
}
finally
{
//確定処理
super.db.endTransaction();
stmt2 = null;
}
//確認用SQL
dispResult();
return iError;
}
//確認のためのデータ表示であり、本来はエラー処理を正しくする必要があります
private void dispResult()
{
//変数宣言
String sSQL = "";
Cursor cursor = null;
try
{
//SQLの実行
sSQL = "select fld1,fld2,fld3 from tbl3 order by fld1";
cursor = super.db.rawQuery(sSQL,null);
//カーソル位置を先頭にします
cursor.moveToFirst();
for (int i = 0; i < cursor.getCount(); i++)
{
Log.d("i",String.valueOf(i));
Log.d("fld1",String.valueOf(cursor.getInt(0)));
Log.d("fld2",String.valueOf(cursor.getDouble(1)));
Log.d("fld3",String.valueOf(cursor.getString(2)));
//カーソルを進める
cursor.moveToNext();
}
cursor.close();
}
catch (Exception e)
{
Log.d("error", e.getMessage().toString());
return;
}
finally
{
cursor = null;
}
}
//execSQLを使用した実行するサンプル
public int testTest2()
{
//変数宣言
int iResult = 0;
int iError = 0;
String sSQL = "";//内側のネスト
String sSQL2 = "";//大元のネスト
try
{
//トランザクションネスト開始
super.db.execSQL("BEGIN TRANSACTION");
//ログに使用するテーブルをクリアします
sSQL2=" delete from tbl3";
super.db.execSQL(sSQL2);
//ログに追加するデータを登録します
sSQL2=" insert into tbl3 values(3,2,'test2 update before')";
super.db.execSQL(sSQL2);
//失敗するDMLを実行させる
try
{
//内側のネスト
//トランザクションを開始します
super.db.execSQL("BEGIN TRANSACTION");
//エラーのDMLを発行します
//ここでは次のエラーが発生するため内側の
//トランザクションデータは反映されません
//E/SQLiteLog: (1) near "fld2": syntax error
// D/error: near "fld2": syntax error (code 1):
// , while compiling: update tbl1 fld2 = 10,fld3 = 'a10'
sSQL=" update tbl1 fld2 = 10,fld3 = 'a10'";
super.db.execSQL(sSQL.toString());
//エラーになるので、ここのコミットは通らない
super.db.execSQL("COMMIT TRANSACTION");
}
catch (SQLException e3)
{
Log.d("error",e3.getMessage().toString());
super.db.execSQL("ROLLBACK TRANSACTION");
iError = 3;
//returnで終わりにはしないで、ネストの元に戻すため
}
catch (Exception e4)
{
Log.d("error",e4.getMessage().toString());
super.db.execSQL("ROLLBACK TRANSACTION");
iError = 3;
//returnで終わりにはしないで、ネストの元に戻すため
}
finally
{
}
//大元のネストをコミットさせる
//ログにデータを登録します
sSQL2=" insert into tbl3 values(4,2,'test2 update end')";
super.db.execSQL(sSQL2);
//コミットしてログのテーブルを確定します
super.db.execSQL("COMMIT TRANSACTION");
}
catch (SQLException e)
{
Log.d("error",e.getMessage().toString());
super.db.execSQL("大元のネスト ROLLBACK TRANSACTION");
return 1;
}
catch (Exception e2)
{
Log.d("error",e2.getMessage().toString());
super.db.execSQL("大元のネスト ROLLBACK TRANSACTION");
return 1;
}
finally
{
}
//確認用SQL
dispResult();
return iError;
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
|
トランザクションを開始します。
これはログ用のトランザクションとして使っています。
次にデータを登録するトランザクションを開始します。
実行するのはエラーとなるDMLです。
エラーとなるので、データは登録されません。
ここでデータ登録用のトランザクションを閉じます。
でも、ログ用のトランザクションは健在です。
そこで、コミットをすることにより、ログに登録したテーブルは確定となり、
ログ用として登録した(このサンプルでいうinsert)データは登録されます。
ログ用のトランザクションが親とすれば
登録用のデータは子となります。
子のトランザクション処理では失敗したのでロールバックが実行されます。
すなわち、登録用のデータはロールバックにより登録されずに元の状態のままとなります。
しかし、ログは登録したいので親のトランザクション処理でコミットすることで
ログのデータは確定状態となるわけです。
データベースの接続・切断を継承しているクラスは次のページを参考にしてください。
URL [http://abc3.me/computer/android/4/android-7-1.php] (クリックをすると開きます)
クラスの呼び元です。
import android.database.sqlite.SQLiteDatabase;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
databaseTransactionNest();
//databaseDrop();
//databaseCreate();
//databaseTransaction();
//databaseUpdate();
//databaseDelete();
//databaseInsert();
//databaseSelect();
//databaseOpenClose();
//readFile();
}
private void databaseTransactionNest()
{
int iResult = 0;
clsDatabaseTransactionNest cls = new clsDatabaseTransactionNest(this);
if (cls.DatabaseOpen() > 0)
{
cls = null;
Log.d("databaseUpdate", "データ接続エラーが発生しました。");
return;
}
//beginTransaction
Log.d("beginTransaction ","testTest1 start --------------------------");
iResult = cls.testTest1();
if(iResult > 0)
{
//cls = null;
Log.d("databaseUpdate", "testTest1 エラーが発生しました。");
//次を実行させるためreturnにはしていません
//return;
}
Log.d("beginTransaction ","testTest1 end --------------------------");
//execSQL
Log.d("execSQL ","testTest2 start --------------------------");
iResult = cls.testTest2();
if(iResult > 0)
{
//cls = null;
Log.d("databaseUpdate", "testTest2 エラーが発生しました。");
//次を実行させるためreturnにはしていません
//return;
}
Log.d("execSQL ","testTest2 end --------------------------");
if (cls.DatabaseClose() > 0)
{
cls = null;
Log.d("databaseUpdate", "データ切断エラーが発生しました。");
return;
}
cls=null;
}
}
|
|
|