Rust Dieselでトランザクション処理

個人開発したアプリの宣伝
目的地が設定できる手帳のような使い心地のTODOアプリを公開しています。
Todo with Location

Todo with Location

  • Yoshiko Ichikawa
  • Productivity
  • Free

スポンサードリンク


updateの更新結果件数を見てcommit or rollbackなんかしたかったので、明示的にbegin, rollback, commitできるインターフェースが欲しかった。

ちょっと探してみたけど、見当たらなくて、transaction_builderなんてものもあったけど、どうやら隔離レベルを指定するインターフェースみだいだし、Trait diesel::connection::TransactionManagerパブリックじゃないっぽい。

追記:AnsiTransactionManagerで明示的にトランザクションセッションを張れる。っぽい(コンパイルのみ確認。動作確認はしていない)。

use diesel::connection::{AnsiTransactionManager, TransactionManager};
let ansi = AnsiTransactionManager::new();
ansi.begin_transaction(&conn);
ansi.commit_transaction(&conn);
ansi.rollback_transaction(&conn);

ここから先は追記前。


ちょっとわからなかったので自前でトランザクションセッションを張れる処理を書いた。


トランザクション管理

transaction.rs

use crate::repositories::connection::DBConnection;
use diesel::dsl::sql;
use diesel::sql_types::{Bool, Integer, Text};
use diesel::RunQueryDsl;
use diesel::QueryResult;

pub struct Transaction<'a> {
  db: &'a DBConnection<'a>
}

impl <'a>Transaction<'a> {
  pub fn new(db: &'a DBConnection) -> Self {
    Transaction{db}
  }
  pub fn begin(&self) -> QueryResult<usize>{
    let setup = sql::<Bool>("begin");
    let db = self.db.get();
    setup.execute(db)
  }

  pub fn commit(&self) -> QueryResult<usize>{
    let setup = sql::<Bool>("commit");
    let db = self.db.get();
    setup.execute(db)
  }

  pub fn rollback(&self) -> QueryResult<usize>{
    let setup = sql::<Bool>("rollback");
    let db = self.db.get();
    setup.execute(db)
  }
}

use repositories::connection::DBConnection;は自前のDBインスタンスの抽象なだけなので、PgConnectionなりMySQLConnectionなりに読み替えて下さい。見てわかる通り平たいSQL喋ってるだけ笑

結構、探したんだけどやり方わからなくて、こりゃSQL喋ったほうが早いなと思ってこっちの方針にした。


使い方 

これもrepositoryやself.connなんか出てくるけど、中でPgConnectionでクエリ発行してるだけなので、トランザクションセッションを張る接続でクエリ投げて結果を見て、commit or rollbackするようなカジュアルな感じ。

use.rust

let transaction = Transaction::new( self.conn );
let result = transaction.begin().and_then(|_| {
    self.account_repository.save( &account ).and_then(|_| {
        self.team_repository.save( &team )
    })
});
let ans = match result {
    Err(error) => {
        transaction.rollback().expect("");
        error.to_string()
    },
    Ok(id) => {
        transaction.commit().expect("commitに失敗しました");
        id
    }
};

追記の通りTransactionManagerでトランザクション張るのでいいんだけど、結局モックオブジェクトに差し替えたかったりで、自前の使ってしまってる(^_^;)