読者です 読者をやめる 読者になる 読者になる

備忘録代わりのブログ

タイトル通りの私用備忘録。適当に残しておきたいことを書いておくブログ

Android開発録 トランザクションの扱い

バルクインサートを導入した

理由としては、1000曲近い曲データ(×難易度分)を1行入れてDBコミット、1行入れてはコミット…ということをしていたらくっそ遅かったから。

それを改善するために、
トランザクションを張る→その間にインサートをする→最後にコミット
をする、いわゆる「バルクインサート」を導入した。

なんかインサートしなくなったぞ?

バルクインサートコードをほぼ書き込み、いざテストだ!と踏み込んだ結果、データがインサートされない。
理由を探ってみる…

※下のコードを見た瞬間わかった人はちょっと座っててください

SQLiteStatement stmt = db.compileStatement(sql.toString());  
db.beginTransaction();  
for(ContentValues val :values){
    int i=0;
    for(String col:val.keySet()){
        i++;
        stmt.bindString(i,val.getAsString(col));
    }
    returnRow = stmt.executeInsert();
}
db.setTransactionSuccessful();

んー、ぱっと見問題は見えない…

SQLが間違っていたらPreparedStatement作成時に落ちるだろうし、バインドも問題なく行ってるっぽい。

んでもって、トランザクションへの成功宣言をしておしまい…ん?

成功宣言ってなんだ?

ここで出てくる成功宣言とは、

db.setTransactionSuccessful();

のこと。

これを入れることでトランザクションの成功を宣言する…

でも、「成功を宣言する」だけであって、トランザクション自体が終わってない…?

ドキュメントを再度確認…

db.endTransaction();

こいつね、こいつが必要だったんね…。

ではsetTransactionSuccessfulとは何者か?

ソースコードを掘って確認してみよう。

下記のコードはSQLiteSession.javaの1ソースである。

public void setTransactionSuccessful() {
    throwIfNoTransaction();
    throwIfTransactionMarkedSuccessful();

    mTransactionStack.mMarkedSuccessful = true;
}

トランザクション成否のフラグをいじってるだけなんね…

明示的にフラグをいじるのはこのメソッドしかないようだ。

ロールバックとかどうすりゃいいんだこれ…要調査かな?

結果、こうなる

try{
    for(ContentValues val :values){
        int i=0;
        for(String col:val.keySet()){
            i++;
            stmt.bindString(i,val.getAsString(col));
        }
        returnRow = stmt.executeInsert();
    }
    db.setTransactionSuccessful();
}
finally {
    db.endTransaction();
}

try-finallyで囲んであるのは、最終処理でトランザクションを閉じることを明示的に表すため。

普通に書く分だったら、特に例外を吐かないメソッドたちなのでウォーターフォール式に書いても問題ない…と思う。

なんでどのレコードもインサートされてなかったのか?

トランザクションを張る→データインサートを順次行う→トランザクションは張られたまま

コレが起きていたため、トランザクションセッション内ではインサートが行われているのだが

外部セッション(目に見えるデータ)に関してはインサートが完了していなかった状態になっていた。

回りくどい言い方をしたが、要するに「トランザクションを閉じる」≒「コミットを行う」という状態。

感じたこと

せっかくSQL界隈には「commit」や「rollback」というありがたい言葉があるんだから、そっちを使ってくれませんかね…

この問題で一番感じたのはsetTransactionSuccessfulというメソッド名だとcommitも行っていると俺のように勘違いする人がいるんじゃないかなーって…。

余計な話

たまにソースのコピペではなく、自分でコードを読み解いて、実装して、躓いて、解決して、自分の解釈や考えを出してみるとけっこう面白いもんですね…。