トランザクションとはなにか？どんな時に使うか？ Railsでの使用方法 

# トランザクションとは
`トランザクションとは複数の処理をまとめて１つの大きな処理として扱う機能です。`
複数のリソースにまたがった変更をひとまとまりで扱う時、処理の１つで例外が発生したら、複数の処理を巻き戻すことができます。
トランザクションを適切に使えば、`致命的なデータの不整合性防ぐ`ことができるのです。



#トランザクションはなぜ必要になるのか？
トランザクションがなぜ必要になるのかの理解を深める為に、ここではをよく使われている例の銀行口座への振込を使って説明したいと思います。

残高が10000円のAさん、残高が20000円のBさんがいるとしましょう。
AさんはBさんに5000円振り込むとします。

その時は以下の2つの処理から構成されます。

` 1、 Aさんの口座から5000円差し引き、残高が10000円 - 5000円 = 5000円になる`

` 2、Bさんの口座に5000円プラスされ、残高が20000円 + 5000円 = 25000円になる`

<img width="785" alt="スクリーンショット 2019-07-15 3.45.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459450/c3c28ca1-4a75-5a8a-876d-a7091101b6a2.png">

ですが、この2つの処理は振込という1つの処理として考えるべきでしょう。
なぜなら、2つの処理を分離して考えてしますと、Aさんが5000円送金した直後にシステムトラブルが起きた時以下のようになってします為です。

` 1、 Aさんの口座から5000円差し引き、残高が10000円 - 5000円 = 5000円になる`

　　`----------------------システムトラブル発生----------------------`

` 2、Bさんの口座にお金お金が振り込まれない`

<img width="720" alt="スクリーンショット 2019-07-15 4.26.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459450/f420f02a-b980-46d7-b581-4f758d6dec94.png">


このような事態にならない為にも、2つの処理を1つの振込として処理することにより、仮にAさんが5000円を振り込んだ後にシステムトラブルが発生したとしても1つの振込としての処理は失敗したことになり、最初から振込がなかったことにすることで5000円の消失を防ぐことができるのです。

このような場合に、複数の処理をまとめて１つの大きな処理として扱うトランザクションは必要となるのです。



# Railsでの使用方法
## transactionメソッドの基本構文


transactionメソッドの基本的な書き方は以下の通りです。とてもシンプルな構造です。


```ruby

ActiveRecord::Base.transaction do
  # 例外が発生するかもしれない処理
end
  # 例外が発生しなかった場合の処理
rescue => e
  # 例外が発生した場合の処理
```


## 使用方法



ここでは`Payment`（入金）、`PaymentHistory`（入金履歴）のModelが存在するとしましょう。
入金があると入金履歴も作成する処理を見ていきます。


以下はtransactionブロックを使わず実装した処理です。

```ruby
def create
  payment = Payment.new(
    amount: 1000
  )
  payment.save!

  payment_history = PaymentHistory.new(
    amount: 1000,
    payment_day: Time.zone.now.strftime('%Y/%m/%d/%H時%M分%S秒')
  )
  payment_history.save!
end
```

このまま実行した場合、もし`PaymentHistory`作成時に何らかの例外が発生してしまうと入金は行なったが、入金履歴が無いというデータの不整合性が発生してしまいます。

実際に例外が発生した場合のデータを見て見ましょう。

```sql
mysql > SELECT * FROM payments;
     +-----+--------+
     | id  | amount | 
     +-----+--------+
     | 1   | 1000   |
     +-----+--------+
```

```sql
mysql > SELECT * FROM payment_histories;
    Empty set (0.00 sec)
```

上記の通り、Paymentのレコードが作成されており、PaymentHistoryのレコードは存在しないことが分かります。


上記のようなデータの不整合性を避ける為にも、transactionブロックを活用すると`全処理が成功`または`処理が失敗しキャンセル`のどちらかに処理結果を限定できる。

以下はtransactionブロックを使用し、実装した処理です。

```ruby
def create
  ActiveRecord::Base.transaction do
    payment = Payment.new(
      amount: 1000
    )
    payment.save! # まだコミットされない

    payment_history = PaymentHistory.new(
      amount: 1000,
      payment_day: Time.zone.now.strftime('%Y/%m/%d/%H時%M分%S秒')
    )
    payment_history.save! # まだコミットされない
  end
   # transactionブロックを正常に抜けるとコミットされる
end
```

transactionブロックを使用したコードの場合、transactionブロック内の処理が例外なく完了すれば、コミットされ登録成功となる。

下記はその時のデータです。

```sql
mysql > SELECT * FROM payments;
     +-----+--------+
     | id  | amount | 
     +-----+--------+
     | 1   | 1000   |
     +-----+--------+
```

```sql
mysql > SELECT * FROM payment_histories;
     +-----+--------+--------------------------+
     | id  | amount |       payment_day        | 
     +-----+--------+--------------------------+
     | 1   | 1000   | "2019/07/15/06時35分10秒" |
     +-----+--------+--------------------------+
```

一方、transactionブロック内で例外が発生した場合はロールバックされ、途中まで成功していた処理もキャンセルされる。

下記はその時のデータです。

```sql
mysql > SELECT * FROM payments;
    Empty set (0.00 sec)
```

```sql
mysql > SELECT * FROM payment_histories;
    Empty set (0.00 sec)
```


中途半端な更新も良しとする要件もある為、一概には言えないがこのように、複数の`save!`メソッドを使用する場合はtransactionブロックを使用するのが良いでしょう。



# まとめ
というわけで、トランザクションについて説明していきました。

トランザクションは、複数のリソースにまたがった変更をひとまとまりで扱う時、処理の１つで例外が発生したら、複数の処理を巻き戻すことができます。

予期せぬバグを未然に防げるので、使えそうな場面では一連の処理にトランザクションを導入することを検討しましょう。