VoltDBの開発者向けドキュメントのまとめ（7章：アプリケーション開発の簡略化）

# はじめに

VoltDBの開発者向けの公式ドキュメント[Using VoltDB](https://docs.voltdb.com/UsingVoltDB/index.php)のまとめです。対象のバージョンはv8.3です。
正確に翻訳することや網羅することは目的ではないため、重要でない記述等は削除したり、不足部分は追記しています。

今回は7章です。

現時点でまとめたのは以下の章です。
・[3. データベースの起動](https://qiita.com/mkyz08/items/647459cfe67641e4b888)
・[7. アプリケーション開発の簡略化](https://qiita.com/mkyz08/items/ff9d15cf050af6508964)
・[9. クラスタでVoltDBを使用する](https://qiita.com/mkyz08/items/80f29883a006d1f393e2)
・[13. VoltDBデータベースの保存と復元](https://qiita.com/mkyz08/items/552e83b231e02d47314c)

なお、本投稿のまとめ元である「Using VoltDB」のライセンスはAGPLであり、これらのまとめ投稿はAGPLが適用されます。


# 7. アプリケーション開発の簡略化

・VoltDBには開発を容易にする以下の3つの機能がある。

- デフォルトプロシージャの利用
- 単純なストアドプロシージャを定義するためのショートカット
- 期待されるクエリ結果の確認 


# 7.1. デフォルトプロシージャの利用

・キーに基づいてレコードをinsert、select、update、deleteする単純な操作を簡単に実行するために、VoltDBには各テーブルにデフォルトストアドプロシージャを自動で定義する。
・デフォルトのストアドプロシージャでは、「テーブル名.ステートメント」の名前がで定義される。たとえば、HELLOWORLDというテーブルでは、以下のデフォルトストアドプロシージャを自動で定義される。

・全てのテーブルでinsertを実行するデフォルトストアドプロシージャが作成される。

- HELLOWORLD.insert：　パラメータはテーブルの列と同じ順序。

・テーブルに主キーがある場合は、update、upsert、deleteをデ実行するデフォルトストアドプロシージャが作成される。

- HELLOWORLD.update：　パラメータはテーブルの列と同じ順序。その後に主キー値が続く。つまり、主キー値は2回指定される。
- HELLOWORLD.upsert：　パラメータはテーブルの列と同じ順序。
- HELLOWORLD.delete：　パラメータは主キー値。複合主キーの場合は定義の順序となる。

・テーブルに主キーがありパーティション化されている場合、selectを実行するデフォルトストアドプロシージャが作成される。
HELLOWORLD.select ：　パラメータは主キー値。複合主キーの場合は定義の順序となる。

sqlcmdコマンドのshow proceduresを使用して、使用可能なすべてのストアドプロシージャをリストし、必要なパラメータの数と種類を指定します。 

[補足]　show proceduresの実行例

```
# sqlcmd
1> show procedures
～省略～
--- User Procedures ------------------------------------------
KEISOKU_DATA.delete             varchar, timestamp
KEISOKU_DATA.insert             varchar, timestamp, smallint, smallint, smallint, smallint, smallint, timestamp, varchar
KEISOKU_DATA.select             varchar, timestamp
```

・コード例

```java
VoltTable[] results;
client.callProcedure("HELLOWORLD.insert",
                     "American","Howdy","Earth");
results = client.callProcedure("HELLOWORLD.select",
                     "American").getResults();
client.callProcedure("HELLOWORLD.update",
                     "American","Yo","Biosphere",
                     "American");
client.callProcedure("HELLOWORLD.delete",
                     "American");
```


## 7.2. 単純なストアドプロシージャを定義するためのショートカット

・1つのSQLクエリを実行して呼び出し元のアプリケーションに結果を返すだけでよい単純なケースでは、ストアドプロシージャのJavaコードを書くのは面倒な作業なので、VoltDBではショートカットを提供している。
・ショートカットでは、ストアドプロシージャをデータベーススキーマの一部として定義できる。
・通常、ストアドプロシージャはJavaのクラスから以下のように定義する。

```
CREATE PROCEDURE FROM CLASS MakeReservation;
CREATE PROCEDURE FROM CLASS CancelReservation;
```

・ショートカットでは、以下のようにAS句にSQLクエリを記述して定義できる。

```
CREATE PROCEDURE CountReservations AS
    SELECT COUNT(*) FROM RESERVATION;
```

・単純なストアドプロシージャでは、以下のようにSQLクエリへ引数を渡すこともできる。

```
CREATE PROCEDURE MyReservationsByTrip AS
        SELECT R.RESERVEID, F.FLIGHTID, F.DEPARTTIME
        FROM RESERVATION AS R, FLIGHT AS F
        WHERE R.CUSTOMERID = ?  
        AND R.FLIGHTID = F.FLIGHTID
        AND F.ORIGIN=? AND F.DESTINATION=?;
```

・単純なプロシージャーをシングルパーティションにするかどうかを指定することもできる。
・デフォルトでは、ストアド・プロシージャはマルチパーティションと見なされる。
・シングルパーティションにする場合は、PARTITION ON句でそのパーティションを指定する。

```
CREATE PROCEDURE FetchReservations 
   PARTITION ON 
      TABLE Reservation COLUMN flightid
   AS
      SELECT * FROM RESERVATION WHERE FLIGHTID=?;
```

・単純なプロシージャ内で複数のSQL文を実行する場合は、SQLをBEGIN-END句に囲む。

```
CREATE PROCEDURE OpenOrders
   AS BEGIN
      SELECT fullname FROM CUSTOMER WHERE CUSTOMERID=?;
      SELECT * FROM ORDER WHERE CUSTOMERID=?;
   END;
```

・各ステートメントはセミコロン（;）で終了する。
・プロシージャは、プロシージャ内の各文に対するVoltTableの配列を返す。
・引数は再利用されないので、上の例ではCUSTOMERIDの値を2回引数に与えて実行する。

## 7.3. 期待されるクエリ結果の確認

・クエリによって返される結果の正確性を検証するためにコーディングが必要となる場合がある。例えば、正しい数のレコードを取得したか？クエリは正しい値を返したか？
・VoltDBはクエリの結果を手動で検証するコードを記述するのではなく、クエリ自体に検証を実行する方法を提供する。
・クエリが検証で期待値とならない場合、ストアドプロシージャは自動的にVoltAbortExceptionをスローしてロールバックされる。

・期待値を指定する場合、voltQueueSQLメソッドの2番目のパラメータに指定する。
・次のコードは、EXPECT_ONE_ROWの期待値を使用して1レコードだけが返ってくることを検証している。

```
import org.voltdb.Expectation;
           .
           .
           .
public final SQLStmt GetSeats = new SQLStmt(
        "SELECT numberofseats FROM Flight WHERE flightid=?;");
 
voltQueueSQL(GetSeats, EXPECT_ONE_ROW, flightid); -- EXPECT_ONE_ROWで検証
VoltTable[] recordset = voltExecuteSQL();
Long numofseats = recordset[0].asScalarLong();
```

・このように期待値を使用することにより、結果に1行しか存在しないことを検証するためのエラー・チェックを行う必要がなくなる。 

・次の表は、ストアドプロシージャで使用できるすべての期待値を示す。


| 期待                         | 説明                                                               |
|------------------------------|--------------------------------------------------------------------|
| EXPECT_EMPTY                 | クエリはレコードを返されない。                                   |
| EXPECT_ONE_ROW               | クエリは1レコードのみを返す必要があります。                          |
| EXPECT_ZERO_OR_ ONE_ROW      | クエリは、0個以上のレコードを返す必要があります。                           |
| EXPECT_NON_EMPTY             | クエリは少なくとも1レコードを返す必要があります。                    |
| EXPECT_SCALAR                | クエリは単一の値（1列のみ）を返す必要があります。 |
| EXPECT_SCALAR_LONG          | クエリは、Long型のデータ型を持つ単一の値を返す必要があります。     |
| EXPECT_SCALAR_MATCH(long) | クエリは、指定されたLong値に等しい単一の値を返す必要があります。   |