メインコンテンツまでスキップ
バージョン: 3.18

ScalarDB SQL API ガイド

注記

このページは英語版のページが機械翻訳されたものです。英語版との間に矛盾または不一致がある場合は、英語版を正としてください。

このガイドでは、ScalarDB SQL API の使用方法について説明します。

プロジェクトに ScalarDB SQL API を追加する

Gradle を使用して ScalarDB SQL API への依存関係を追加するには、次のコードを使用します。<VERSION> は、使用している ScalarDB SQL API と関連ライブラリのバージョンに置き換えてください。

dependencies {
implementation 'com.scalar-labs:scalardb-sql:<VERSION>'
implementation 'com.scalar-labs:scalardb-cluster-java-client-sdk:<VERSION>'
}

Maven を使用して依存関係を追加するには、以下を使用します (... を使用している ScalarDB SQL API のバージョンに置き換えます)。

<dependencies>
<dependency>
<groupId>com.scalar-labs</groupId>
<artifactId>scalardb-sql</artifactId>
<version>...</version>
</dependency>
<dependency>
<groupId>com.scalar-labs</groupId>
<artifactId>scalardb-cluster-java-client-sdk</artifactId>
<version>...</version>
</dependency>
</dependencies>

SqlSessionFactory

ScalarDB SQL API では、SqlSessionFactory でインスタンス化された SqlSession インスタンスを通じてすべての操作を実行します。このセクションでは、それらの使用方法を説明します。

SqlSessionFactory を説明する前に、接続モードとトランザクションモードについて説明します。

トランザクションモード

また、ScalarDB SQL には、トランザクション モードと2フェーズコミットトランザクションモードの2つのトランザクションモードがあります。

トランザクションモードでは、ユーザーには commit インターフェイスのみが公開され、バックグラウンドで2フェーズコミットが実行されます。一方、2フェーズコミットトランザクションモードでは、ユーザーに2フェーズコミットスタイルのインターフェイス (preparecommit) が公開されます。

デフォルトのトランザクションモードは、設定ファイルで指定するか、SqlSessionFactory をビルドするときに指定できます。

また、SqlSessionsetTransactionMode() メソッドを使用して変更することもできます。

SqlSessionFactory をビルドする

次のように、プロパティファイルを使用して SqlSessionFactory をビルドできます。

SqlSessionFactory sqlSessionFactory = SqlSessionFactory.builder()
.withPropertiesFile("<your configuration file>")
// If you need to set custom properties, you can specify them with withProperty() or withProperties()
.withProperty("<custom property name>", "<custom property value>")
.build();

設定の詳細については、ScalarDB Cluster SQL クライアント設定を参照してください。

SqlSession インスタンスを取得する

次のように、SqlSessionFactory を使用して SqlSession インスタンスを取得できます。

SqlSession sqlSession = sqlSessionFactory.createSqlSession();

SqlSession はスレッドセーフではないことに注意してください。

複数のスレッドから同時に使用しないでください。

SqlSession インスタンスを閉じる

SqlSession インスタンスですべての操作が完了したら、SqlSession インスタンスを閉じる必要があります。

sqlSession.close();

SqlSessionFactory インスタンスを閉じる

sqlSessionFactory も、不要になったら閉じる必要があります。

sqlSessionFactory.close();

SQL を実行する

次のように SqlSession を使用して SQL を実行できます。

ResultSet resultSet = sqlSession.execute("<SQL>");

次のように SqlSession を使用して Statement オブジェクトを実行することもできます。

// Build a statement
Statement statement = StatementBuilder.<factory method>...;

// Execute the statement
ResultSet resultSet = sqlSession.execute(statement);

ミューテーションステートメント(INSERTUPSERTUPDATE、または DELETE)の場合、返される ResultSet には、影響を受けた行数を報告する updateCount という名前の 1 つの INT 列を持つ単一の Record が含まれます。

DML、DDL、および DCL ステートメントがトランザクションとどのように相互作用するかについては、トランザクションの実行を参照してください。

Statement オブジェクトは、対応する SQL のファクトリメソッドを持つ StatementBuilder によって構築できます。詳細については、Javadoc の StatementBuilder ページおよび ScalarDB SQL 文法を参照してください。

ResultSet オブジェクトの処理

SQL 実行の結果として、SqlSessionResultSet オブジェクトを返します。

ここでは、ResultSet オブジェクトの処理方法について説明します。

ResultSet オブジェクトから結果を1つずつ取得する場合は、次のように one() メソッドを使用できます。

Optional<Record> record = resultSet.one();

または、すべての結果を List として一度に取得したい場合は、次のように all() メソッドを使用できます。

List<Record> records = resultSet.all();

また、ResultSetIterable を実装しているので、次のように for-each ループで使用できます。

for (Record record : resultSet) {
...
}

ResultSet オブジェクトのメタデータを取得する場合は、次のように getColumnDefinitions() メソッドを使用できます。

ColumnDefinitions columnDefinitions = resultSet.getColumnDefinitions();

詳細については、Javadoc の ColumnDefinition ページを参照してください。

Record オブジェクトの処理

前述のように、ResultSet オブジェクトは、データベースのレコードを表す Record オブジェクトを返します。

次のように、getXXX("<column name>") メソッドまたは getXXX(<column index>) メソッド (XXX は型名) を使用して結果の列値を取得できます。

// Get a BOOLEAN value of a column
boolean booleanValueGottenByName = record.getBoolean("<column name>");
boolean booleanValueGottenByIndex = record.getBoolean(<column index>);

// Get an INT value of a column
int intValueGottenByName = record.getInt("<column name>");
int intValueGottenByIndex = record.getInt(<column index>);

// Get a BIGINT value of a column
long bigIntValueGottenByName = record.getBigInt("<column name>");
long bigIntValueGottenByIndex = record.getBigInt(<column index>);

// Get a FLOAT value of a column
float floatValueGottenByName = record.getFloat("<column name>");
float floatValueGottenByIndex = record.getFloat(<column index>);

// Get a DOUBLE value of a column
double doubleValueGottenByName = record.getDouble("<column name>");
double doubleValueGottenByIndex = record.getDouble(<column index>);

// Get a TEXT value of a column
String textValueGottenByName = record.getText("<column name>");
String textValueGottenByIndex = record.getText(<column index>);

// Get a BLOB value of a column (as a ByteBuffer)
ByteBuffer blobValueGottenByName = record.getBlob("<column name>");
ByteBuffer blobValueGottenByIndex = record.getBlob(<column index>);

// Get a BLOB value of a column as a byte array
byte[] blobValueAsBytesGottenByName = record.getBlobAsBytes("<column name>");
byte[] blobValueAsBytesGottenByIndex = record.getBlobAsBytes(<column index>);

// Get a DATE value of a column as a LocalDate
LocalDate dateValueGottenByName = record.getDate("<column name>");
LocalDate dateValueGottenByName = record.getDate(<column index>);

// Get a TIME value of a column as a LocalTime
LocalTime timeValueGottenByName = record.getTime("<column name>");
LocalTime timeValueGottenByName = record.getTime(<column index>);

// Get a TIMESTAMP value of a column as a LocalDateTime
LocalDateTime timestampValueGottenByName = record.getTimestamp("<column name>");
LocalDateTime timestampValueGottenByName = record.getTimestamp(<column index>);

// Get a TIMESTAMPTZ value of a column as an Instant
Instant timestampTZValueGottenByName = record.getTimestampTZ("<column name>");
Instant timestampTZValueGottenByName = record.getTimestampTZ(<column index>);

列の値が null かどうかを確認する必要がある場合は、isNull("<列名>") または isNull(<列インデックス>) メソッドを使用できます。

// Check if a value of a column is null
boolean isNullGottenByName = record.isNull("<column name>");
boolean isNullGottenByIndex = record.isNull(<column index>);

詳細については、Javadoc の Record ページを参照してください。

準備済みステートメント

アプリケーションで複数回実行されるクエリには、PreparedStatement を使用できます:

PreparedStatement preparedStatement = sqlSession.prepareStatement("<SQL>");
ResultSet result = sqlSession.execute(preparedStatement.bind());

同じクエリを2回目以降に実行すると、キャッシュされた事前解析済みステートメントオブジェクトが使用されます。 したがって、クエリを複数回実行すると、PreparedStatement を使用するとパフォーマンス上の利点が得られます。 クエリを1回だけ実行する場合、準備済みステートメントは余分な処理が必要になるため非効率的です。 その場合は、代わりに sqlSession.execute() メソッドの使用を検討してください。

PreparedStatementbind(...) を呼び出すと、パラメータ値を保持する BoundStatement が返されます。その後、BoundStatementsqlSession.execute() に渡します。

パラメータは、位置指定または名前付きのいずれかになります:

// Positional parameters
PreparedStatement preparedStatement1 =
sqlSession.prepareStatement("INSERT INTO tbl (c1, c2) VALUES (?, ?)");

// Named parameters
PreparedStatement preparedStatement2 =
sqlSession.prepareStatement("INSERT INTO tbl (c1, c2) VALUES (:a, :b)");

bind(...) に渡すことで、1 回の呼び出しで値をバインドできます:

// Positional values
sqlSession.execute(preparedStatement1.bind(Value.ofInt(10), Value.ofText("value")));

// Named values
Map<String, Value> namedValues = new HashMap<>();
namedValues.put("a", Value.ofInt(10));
namedValues.put("b", Value.ofText("value"));
sqlSession.execute(preparedStatement2.bind(namedValues));

または、bind() によって返される BoundStatement で流暢なセッターを使用することもできます:

// Positional setters
sqlSession.execute(
preparedStatement1.bind()
.setInt(0, 10)
.setText(1, "value"));

// Named setters
sqlSession.execute(
preparedStatement2.bind()
.setInt("a", 10)
.setText("b", "value"));

たとえば BatchedStatements を構築する際に、プレーンな SQL 文字列を Statement としてラップするには、SimpleStatement.of("<SQL>") を使用します。

詳細については、Javadoc の PreparedStatementBoundStatement、および SimpleStatement ページを参照してください。

バッチステートメントの実行

単一のラウンドトリップで複数のステートメントを実行する必要がある場合は、sqlSession.executeBatch(...) を使用します。これは、一つまたは複数のネームスペースに対する一括挿入、更新、または混合ミューテーションに役立ちます。

各ステートメントが独自のデフォルトネームスペースを持つことができるバッチを構築するには、BatchedStatements を使用します:

BatchedStatements batchedStatements =
BatchedStatements.builder()
.add(boundStatement1, "namespace1")
.add(boundStatement2, "namespace2")
.add(SimpleStatement.of("INSERT INTO tbl (c1, c2) VALUES (1, 'a')"))
.build();

List<ResultSet> results = sqlSession.executeBatch(batchedStatements);

すべてのステートメントがセッションのデフォルトネームスペースを使用する場合は、代わりに List<Statement> を渡すことができます:

List<ResultSet> results =
sqlSession.executeBatch(Arrays.asList(boundStatement1, boundStatement2));

executeBatch(...) は、入力と同じ順序で、入力ステートメントごとに 1 つの ResultSet を含む List<ResultSet> を返します。

バッチ実行は DML、DDL、および DCL ステートメントを受け入れます。コマンドステートメント(BEGINCOMMITROLLBACK、およびその他のトランザクション制御ステートメント)は拒否されるため、代わりに対応する SqlSession メソッドを使用してください。DML、DDL、および DCL ステートメントがトランザクションとどのように相互作用するかについては、トランザクションの実行を参照してください。

詳細については、Javadoc の BatchedStatements および SqlStatementExecutable ページを参照してください。

トランザクションの実行

ScalarDB SQL では、DML ステートメント(SELECTINSERTUPSERTUPDATE、および DELETE)は常にトランザクション内で実行されます。トランザクションは、sqlSession.begin() で明示的に開始したもの、または ScalarDB が自動的に管理するもののいずれかです:

  • トランザクションがアクティブな場合(sqlSession.begin() の後)、sqlSession.execute(...)sqlSession.executeBatch(...) の両方がそのトランザクションで DML ステートメントを実行します。
  • トランザクションがアクティブでない場合、各 sqlSession.execute(...) 呼び出しはその DML ステートメントを独自の自動管理トランザクションで実行し、各 sqlSession.executeBatch(...) 呼び出しはバッチ内のすべての DML ステートメントを単一の自動管理トランザクションでアトミックに実行します。

begin() に加えて、SqlSession は読み取り専用トランザクションを開始するための beginReadOnly() や、トランザクション属性を受け入れるオーバーロードなど、他のバリアントも提供します。詳細については、Javadoc の SqlSession ページを参照してください。

別々の execute(...) 呼び出しからの複数の DML ステートメントをアトミックにするには、それらを実行する前に明示的なトランザクションを開始する必要があります。executeBatch(...) は、明示的なトランザクションを必要とせずに、そのバッチ内の DML ステートメント間でアトミシティを提供します。

DDL および DCL ステートメントは決してトランザクショナルではありません。明示的なトランザクション内で呼び出された場合やバッチの一部として呼び出された場合でも、それらはすぐに実行され、いかなるトランザクションにも参加しません。

このセクションでは、トランザクションモードと2フェーズコミットトランザクションモードの各トランザクションモードについて、明示的なトランザクションを実行する方法について説明します。

トランザクションモード

トランザクションモードのサンプルコードは次のとおりです。

try {
// Begin a transaction
sqlSession.begin();

// Execute statements (SELECT/INSERT/UPDATE/DELETE) in the transaction
...

// Commit the transaction
sqlSession.commit();
} catch (UnknownTransactionStatusException e) {
// If you catch `UnknownTransactionStatusException`, it indicates that the status of the
// transaction, whether it has succeeded or not, is unknown. In such a case, you need to check if
// the transaction is committed successfully or not and retry it if it failed. How to identify a
// transaction status is delegated to users
} catch (SqlException e) {
// For other exceptions, you can try retrying the transaction

// Rollback the transaction
sqlSession.rollback();

// For `TransactionRetryableException`, you can basically retry the transaction. However, for
// the other exceptions, the transaction may still fail if the cause of the exception is
// nontransient. For such a case, you need to limit the number of retries and give up retrying
}

UnknownTransactionStatusException をキャッチした場合、トランザクションのステータス (成功したかどうか) が不明であることを示します。 このような場合、トランザクションが正常にコミットされたかどうかを確認し、失敗した場合は再試行する必要があります。 トランザクションステータスを識別する方法はユーザーに委任されています。 トランザクションステータステーブルを作成し、他のアプリケーションデータを使用してトランザクション的に更新して、ステータステーブルからトランザクションのステータスを取得できるようにすることをお勧めします。

別の例外をキャッチした場合は、トランザクションを再試行することができます。 TransactionRetryableException の場合、基本的にトランザクションを再試行することができます。 ただし、他の例外の場合、例外の原因が一時的でない場合は、トランザクションが失敗する可能性があります。 このような場合は、再試行回数を制限し、再試行をあきらめる必要があります。

2フェーズコミットトランザクションモード

これを読む前に、このドキュメント を読んで、2フェーズコミットトランザクションの概念を学んでください。

コーディネータのトランザクションを開始するには、次のようにします。

sqlSession.begin();

コーディネータが参加者に渡すトランザクション ID を取得するには、getTransactionId() を使用します:

String transactionId =
sqlSession
.getTransactionId()
.orElseThrow(() -> new IllegalStateException("transaction ID is not available"));

参加者のトランザクションに参加するには、次のようにします。

sqlSession.join(transactionId);

2フェーズコミットトランザクションモードのコード例は次のとおりです。

try {
// Begin a transaction
sqlSession.begin();

// Execute statements (SELECT/INSERT/UPDATE/DELETE) in the transaction
...

// Prepare the transaction
sqlSession.prepare();

// Validate the transaction
sqlSession.validate();

// Commit the transaction
sqlSession.commit();
} catch (UnknownTransactionStatusException e) {
// If you catch `UnknownTransactionStatusException` when committing the transaction, it
// indicates that the status of the transaction, whether it has succeeded or not, is unknown.
// In such a case, you need to check if the transaction is committed successfully or not and
// retry it if it failed. How to identify a transaction status is delegated to users
} catch (SqlException e) {
// For other exceptions, you can try retrying the transaction

// Rollback the transaction
sqlSession.rollback();

// For `TransactionRetryableException`, you can basically retry the transaction. However, for
// the other exceptions, the transaction may still fail if the cause of the exception is
// nontransient. For that case, you need to limit the number of retries and give up retrying
}

例外処理はトランザクションモードと同じです。

メタデータの取得

次のように SqlSession.getMetadata() メソッドを使用してメタデータを取得できます。

Metadata metadata = sqlSession.getMetadata();

詳細については、Javadoc の Metadata ページを参照してください。

参考文献