# 悲観的なトランザクションロック

### 概要
+ この演習では、悲観的なトランザクションロックの使い方を学びます。
+ 最初にテーブルを作成し、2つの接続を確立します。どちらの接続も、テーブル内のまったく同じレコードを同時に更新しようとします。最初のトランザクションがコミットされて初めて、2番目のトランザクションを続行できることがわかります。

### 注意
+ いくつかのステップでは、2つの異なるターミナルウィンドウでコマンドを入力する必要があります。2つのターミナルを起動し、演習の間ずっと開いておくと便利でしょう。
+ この演習で利用する、`IP`、`port`、`username`、`password`、`directory`などは、あなたの環境と異なる場合があります。
+ ターミナルウィンドウの"`$`"とはOSプロンプトです。
+ ターミナルウィンドウ内の "`tidb>`" または "`mysql>`" は、SQL コマンドプロンプトです。
+ これらの演習は、ノートパソコンなどの単一のマシン上で実行することができます。

### 前提条件
+ macOSまたはLinuxを使用していること。
+ [lab repository](https://github.com/pingcap/tidb-course-201-lab.git)をクローン済みであること。
+ 演習「テスト用にTiDBクラスタを作成する」を完了していること。
+ [JDK 8 以降](https://docs.oracle.com/javase/8/docs/technotes/guides/install/install_overview.html)がインストールされていること。

### 所要時間
+ この演習は約10分程度かかります。

### タスク
1. デモプログラムのJavaソースコードを確認します。
2. デモスクリプトを実行して競合状況（2つのトランザクションで同じ行が同時に変更される）を作成し、スクリプトを実行して、結果を確認します。

### 手順
1. `tidb-course-201-lab/scripts`フォルダの下にあるJavaソースコード`DemoJdbcTxPessimisticLock.java`の次の行に注目します（すべてのソースコードを確認する必要はありません）。

      `main()`メソッドの次のコードは2つの接続を確立します。
      ```java
      for (int i = 0; i < 2; i++) {
          connections.add(DriverManager.getConnection(
                  "jdbc:mysql://" + tidbHost
                          + ":" + port
                          + "/test?"
                          + securityOption,
                  dbUsername, dbPassword));
      }
      ```

      最初の接続では、`id`と`name`という2つのカラムを持つ`test_tx_pessimistic`というテーブルが作成されます。`id`カラムはプライマリキーで、`AUTO_RANDOM`という属性があります。その後、キーが取得され、後で両方の接続でどのレコードを更新すべきかがわかります。

      ```java
      ...
      Statement s = c.createStatement();
      ...
      s.executeUpdate("CREATE TABLE test_tx_pessimistic (id BIGINT PRIMARY KEY AUTO_RANDOM, name char(20))");
      s.executeUpdate("INSERT INTO test_tx_pessimistic (name) VALUES ('INIT') ", Statement.RETURN_GENERATED_KEYS);
      ```

      `AUTOCOMMIT`は無効にされています。
      ```
      for (Connection c : connections) {
          c.setAutoCommit(false);
      }
      ```

      次のセクションでは、主な目的を示しています。どちらの接続も`RowUpdater`クラスを使用し、悲観的なトランザクションを初期化し、同じ行の`name`カラムを更新しようとします。
      ```java
      System.out.println(connectionTags[this.connectionNo] + " session: " + "BEGIN PESSIMISTIC");
      s.executeUpdate("BEGIN PESSIMISTIC");
      System.out.println(connectionTags[this.connectionNo] + " session: "
              + "UPDATE test_tx_pessimistic SET name = '" + connectionTags[this.connectionNo]
              + "' WHERE id = " + rowid);
      s.executeUpdate("UPDATE test_tx_pessimistic SET name = '" + connectionTags[this.connectionNo]
              + "' WHERE id = " + rowid);
      ```

2. デモンストレーションスクリプトを実行し、結果を観察します。一方の接続（出力例のB）は、もう一方の接続（出力例のA）がコミットした後にのみコミットでき、どちらの接続でも処理中にエラーが発生しないはずです。

   ```
   $ ./09-demo-jdbc-tx-pessimistic-01-show.sh local
   ```
   ```
   $ ./09-demo-jdbc-tx-pessimistic-01-show.sh local
   TiDB endpoint: 127.0.0.1
   TiDB username: root
   Default TiDB server port: 4000
   Security options: 
   Connection established.
   Connection B session started
   Connection A session started
   Connection A session: BEGIN PESSIMISTIC
   Connection A session: UPDATE test_tx_pessimistic SET name = 'Connection A' WHERE id = 8070450532247928833
   Connection B session: BEGIN PESSIMISTIC
   Connection B session: UPDATE test_tx_pessimistic SET name = 'Connection B' WHERE id = 8070450532247928833
   Connection A session: Commit
   Connection A session: Checking result

   /* Executing query: select id, name from test_tx_pessimistic; */
     Row#, id, name
     1) 8070450532247928833, Connection A

   Connection B session: Commit
   Connection B session: Checking result

   /* Executing query: select id, name from test_tx_pessimistic; */
     Row#, id, name
     2) 8070450532247928833, Connection B
   ```

## 演習終わり