オープンソース JDBC ドライバの使用

このページでは、オープンソース JDBC ドライバを使用して Cloud Spanner の基本オペレーションを実行する方法を説明します。

JDBC ドライバをインストールする

Cloud Spanner クライアント ライブラリの手順に従って認証を設定し、次のスニペットに示されているように、Cloud Spanner JDBC ドライバの依存関係を pom.xml ファイルに追加します。

<dependencies>
  <!-- The Spanner JDBC driver dependency -->
  <dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-spanner-jdbc</artifactId>
  </dependency>

  <!-- Test dependencies -->
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>com.google.truth</groupId>
    <artifactId>truth</artifactId>
    <version>1.1.2</version>
    <scope>test</scope>
  </dependency>
</dependencies>
JDBC ドライバを読み込むために Java クラス名を必要とするフレームワークを使用している場合、クラス名は com.google.cloud.spanner.jdbc.JdbcDriver です。 接続の設定方法については、JdbcDriver の API ドキュメントをご覧ください。

エミュレータに接続する

エミュレータに接続するには、たとえば次のように SPANNER_EMULATOR_HOST 環境変数を設定します。

Linux / macOS

export SPANNER_EMULATOR_HOST=localhost:9010

Windows

set SPANNER_EMULATOR_HOST=localhost:9010

これにより、JDBC ドライバは、デフォルトの本番環境サービスではなく、localhost で実行されているエミュレータに接続するように指示されます。

スキーマ更新を行う

次のコード例では、最初に JDBC 接続を作成してからテーブルを作成し、Singers テーブルをデータベースに追加します。

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

class CreateTableExample {

  static void createTable() throws SQLException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project";
    String instanceId = "my-instance";
    String databaseId = "my-database";
    createTable(projectId, instanceId, databaseId);
  }

  @SuppressFBWarnings(
      value = "OBL_UNSATISFIED_OBLIGATION",
      justification = "https://github.com/spotbugs/spotbugs/issues/293")
  static void createTable(String projectId, String instanceId, String databaseId)
      throws SQLException {
    String connectionUrl =
        String.format(
            "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
            projectId, instanceId, databaseId);
    try (Connection connection = DriverManager.getConnection(connectionUrl)) {
      try (Statement statement = connection.createStatement()) {
        statement.execute(
            "CREATE TABLE Singers (\n"
                + "  SingerId   INT64 NOT NULL,\n"
                + "  FirstName  STRING(1024),\n"
                + "  LastName   STRING(1024),\n"
                + "  SingerInfo BYTES(MAX),\n"
                + "  Revenues   NUMERIC,\n"
                + ") PRIMARY KEY (SingerId)\n");
      }
    }
    System.out.println("Created table [Singers]");
  }
}

自動 commit モードでトランザクションを使用して行を追加する

複数のオペレーションをグループとして commit する必要がない場合は、トランザクションを autocommit モードで使用できます(デフォルトの動作)。次のコード例では、トランザクションを autocommit モードで使用して、Singers テーブルに行を追加します。

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
import java.util.List;

class InsertDataExample {
  // Class to contain singer sample data.
  static class Singer {
    final long singerId;
    final String firstName;
    final String lastName;
    final BigDecimal revenues;

    Singer(long singerId, String firstName, String lastName, BigDecimal revenues) {
      this.singerId = singerId;
      this.firstName = firstName;
      this.lastName = lastName;
      this.revenues = revenues;
    }
  }

  static final List<Singer> SINGERS =
      Arrays.asList(
          new Singer(10, "Marc", "Richards", new BigDecimal("104100.00")),
          new Singer(20, "Catalina", "Smith", new BigDecimal("9880.99")),
          new Singer(30, "Alice", "Trentor", new BigDecimal("300183")),
          new Singer(40, "Lea", "Martin", new BigDecimal("20118.12")),
          new Singer(50, "David", "Lomond", new BigDecimal("311399.26")));

  static void insertData() throws SQLException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project";
    String instanceId = "my-instance";
    String databaseId = "my-database";
    insertData(projectId, instanceId, databaseId);
  }

  @SuppressFBWarnings(
      value = "OBL_UNSATISFIED_OBLIGATION",
      justification = "https://github.com/spotbugs/spotbugs/issues/293")
  static void insertData(String projectId, String instanceId, String databaseId)
      throws SQLException {
    String connectionUrl =
        String.format(
            "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
            projectId, instanceId, databaseId);
    try (Connection connection = DriverManager.getConnection(connectionUrl)) {
      try (PreparedStatement ps =
          connection.prepareStatement(
              "INSERT INTO Singers\n"
                  + "(SingerId, FirstName, LastName, SingerInfo, Revenues)\n"
                  + "VALUES\n"
                  + "(?, ?, ?, ?, ?)")) {
        for (Singer singer : SINGERS) {
          ps.setLong(1, singer.singerId);
          ps.setString(2, singer.firstName);
          ps.setString(3, singer.lastName);
          ps.setNull(4, Types.BINARY);
          ps.setBigDecimal(5, singer.revenues);
          ps.addBatch();
        }
        int[] updateCounts = ps.executeBatch();
        System.out.printf("Insert counts: %s%n", Arrays.toString(updateCounts));
      }
    }
  }
}

複数のオペレーションをグループとして commit する方法を制御する

Cloud Spanner で複数のオペレーションをグループとしてまとめて commit するかどうかを制御する場合、自動 commit モードを無効にできます。次のコード例では、connection.setAutoCommit(false)connection.commit() を使用して Singers テーブルに行を追加します。

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;

class BatchDmlExample {

  static void batchDml() throws SQLException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project";
    String instanceId = "my-instance";
    String databaseId = "my-database";
    batchDml(projectId, instanceId, databaseId);
  }

  @SuppressFBWarnings(
      value = "OBL_UNSATISFIED_OBLIGATION",
      justification = "https://github.com/spotbugs/spotbugs/issues/293")
  static void batchDml(String projectId, String instanceId, String databaseId) throws SQLException {
    String connectionUrl =
        String.format(
            "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
            projectId, instanceId, databaseId);
    try (Connection connection = DriverManager.getConnection(connectionUrl)) {
      connection.setAutoCommit(false);
      try (Statement statement = connection.createStatement()) {
        statement.addBatch(
            "INSERT INTO Singers (SingerId, FirstName, LastName, Revenues)\n"
                + "VALUES (10, 'Marc', 'Richards', 100000)");
        statement.addBatch(
            "INSERT INTO Singers (SingerId, FirstName, LastName, Revenues)\n"
                + "VALUES (11, 'Amirah', 'Finney', 195944.10)");
        statement.addBatch(
            "INSERT INTO Singers (SingerId, FirstName, LastName, Revenues)\n"
                + "VALUES (12, 'Reece', 'Dunn', 10449.90)");
        int[] updateCounts = statement.executeBatch();
        connection.commit();
        System.out.printf("Batch insert counts: %s%n", Arrays.toString(updateCounts));
      }
    }
  }
}

SQL クエリを実行する

次のコード例では、Singers テーブルのすべての行を、歌手の姓で並べ替えて返します。

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class SingleUseReadOnlyExample {

  static void singleUseReadOnly() throws SQLException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project";
    String instanceId = "my-instance";
    String databaseId = "my-database";
    singleUseReadOnly(projectId, instanceId, databaseId);
  }

  @SuppressFBWarnings(
      value = "OBL_UNSATISFIED_OBLIGATION",
      justification = "https://github.com/spotbugs/spotbugs/issues/293")
  static void singleUseReadOnly(String projectId, String instanceId, String databaseId)
      throws SQLException {
    String connectionUrl =
        String.format(
            "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
            projectId, instanceId, databaseId);
    try (Connection connection = DriverManager.getConnection(connectionUrl);
        Statement statement = connection.createStatement()) {
      // When the connection is in autocommit mode, any query that is executed will automatically
      // be executed using a single-use read-only transaction, even if the connection itself is in
      // read/write mode.
      try (ResultSet rs =
          statement.executeQuery(
              "SELECT SingerId, FirstName, LastName, Revenues FROM Singers ORDER BY LastName")) {
        while (rs.next()) {
          System.out.printf(
              "%d %s %s %s%n",
              rs.getLong(1), rs.getString(2), rs.getString(3), rs.getBigDecimal(4));
        }
      }
    }
  }
}

セッション管理ステートメント

SQL クエリ ステートメントに加えて、Cloud Spanner 用のオープンソース JDBC(Java Database Connectivity)ドライバはセッション管理ステートメントをサポートします。これにより、接続の状態の変更、トランザクションの実行、ステートメントのバッチの効率的な実行が可能になります。

接続ステートメント

次のステートメントは、現在の接続のプロパティを変更するか、表示します。

SHOW VARIABLE READONLY

接続が現在読み取り専用モードかどうかを示す BOOL 型の 1 行と 1 列で結果セットを返します。

SET READONLY

構文
SET READONLY = { true | false }

接続が読み取り専用モードかどうかを設定します。

このステートメントは、アクティブなトランザクションが存在していない場合にのみ実行できます。

SHOW VARIABLE AUTOCOMMIT

接続が AUTOCOMMIT モードかどうかを示す BOOL 型の 1 行と 1 列で結果セットを返します。

SET AUTOCOMMIT

構文
SET AUTOCOMMIT = { true | false }

接続を AUTOCOMMIT モードに設定します。

このステートメントは、アクティブなトランザクションが存在していない場合にのみ実行できます。

SHOW VARIABLE RETRY_ABORTS_INTERNALLY

接続で中止されたトランザクションを自動的に再試行するかどうかを示す BOOL 型の 1 行と 1 列で結果セットを返します。

SET RETRY_ABORTS_INTERNALLY

構文
SET RETRY_ABORTS_INTERNALLY = { true | false }

接続で中止されたトランザクションを自動的に再試行するかどうかを設定します。

このステートメントは、アクティブなトランザクションが存在していない場合にのみ実行できます。

RETRY_ABORTS_INTERNALLY を有効にすると、接続でクライアント アプリケーションに返されるすべてのデータの暗号チェックサムと、トランザクション中に接続で報告されたすべての更新数が維持されます。Cloud Spanner でトランザクションが中止された場合、接続で同じトランザクションの実行が再試行され、返されたデータが元のトランザクションで返されたデータと正確に同じであるかどうかが確認されます。データが一致する場合、Cloud Spanner でトランザクションが継続されます。データが一致しない場合、AbortedDueToConcurrentModification がスローされてトランザクションが失敗します。

この設定はデフォルトで有効になっています。中止されたトランザクションがアプリケーションですでに再試行されている場合は、この設定をオフにすることをおすすめします。

SHOW VARIABLE AUTOCOMMIT_DML_MODE

データ操作言語(DML)ステートメントの自動 commit モードを示す STRING 型の 1 行と 1 列で結果セットを返します。

この変数は、接続で AUTOCOMMIT モードが有効になっている場合にのみ効果があります。

SET AUTOCOMMIT_DML_MODE

構文
SET AUTOCOMMIT_DML_MODE = { 'TRANSACTIONAL' | 'PARTITIONED_NON_ATOMIC' }

DML ステートメントの自動 commit モードを設定します。

  • TRANSACTIONAL モードでは、ドライバが DML ステートメントを独立したアトミック トランザクションとして実行します。ドライバが新しいトランザクションを作成し、DML ステートメントが実行し、実行時にトランザクションを commit するか、エラーが発生した場合にトランザクションをロールバックします。
  • PARTITIONED_NON_ATOMIC モードでは、ドライバが DML ステートメントをパーティション化更新ステートメントとして実行します。パーティション化更新ステートメントは一連のトランザクションの 1 つとして実行でき、パーティション化ステートメントではスケーラビリティとパフォーマンスが向上する緩和されたセマンティクスが提供されます。

このステートメントを実行できるのは、AUTOCOMMIT モードを有効にしている場合のみです。

SHOW VARIABLE STATEMENT_TIMEOUT

ステートメントの現在のタイムアウト値を示す STRING 型の 1 行と 1 列の結果セットを返します。値は整数で、その後に時間単位を示す接尾辞が続きます。値 NULL は、タイムアウト値が設定されていないことを示します。ステートメントのタイムアウト値が設定されている場合、指定されているタイムアウト値より時間がかかるステートメントでは java.sql.SQLTimeoutException が発生し、トランザクションが無効になります。

SET STATEMENT_TIMEOUT

構文
SET STATEMENT_TIMEOUT = { '<INT64>{ s | ms | us | ns }' | NULL }

接続での後続のすべてのステートメントに対してステートメント タイムアウト値を設定します。タイムアウト値を NULL に設定すると、接続のステートメント タイムアウトが無効になります。

サポートされている時間単位は次のとおりです。

  • s: 秒
  • ms: ミリ秒
  • us: マイクロ秒
  • ns: ナノ秒

トランザクション中のステートメントのタイムアウトによりトランザクションが無効になり、無効になったトランザクションの後続のすべてのステートメント(ROLLBACK を除く)が失敗し、JDBC ドライバで java.sql.SQLTimeoutException がスローされます。

SHOW VARIABLE READ_ONLY_STALENESS

Cloud Spanner で AUTOCOMMIT モードで読み取り専用トランザクションとクエリを使用する現在の読み取り専用のステイルネス設定を示す STRING 型の 1 行と 1 列の結果セットを返します。デフォルトの設定は STRONG です。

SET READ_ONLY_STALENESS

構文
SET READ_ONLY_STALENESS = { 'STRONG' | 'MIN_READ_TIMESTAMP <timestamp>' | 'READ_TIMESTAMP <timestamp>' |
    'MAX_STALENESS <INT64>{ s | ms | us | ns }' | 'EXACT_STALENESS <INT64>{ s | ms | us | ns }' }

読み取り専用のステイルネス設定は、AUTOCOMMIT モードではない間に後続のすべての読み取り専用トランザクションに対して、AUTOCOMMIT モードの間にすべてのクエリに対して行います。

タイムスタンプ バウンド オプションは次のとおりです。

タイムスタンプには、次の形式を使用する必要があります。

YYYY-[M]M-[D]DT[[H]H:[M]M:[S]S[.DDDDDD]][timezone]

MAX_STALENESSEXACT_STALENESS の値を設定するためにサポートされる時間単位は次のとおりです。

  • s: 秒
  • ms: ミリ秒
  • us: マイクロ秒
  • ns: ナノ秒

このステートメントは、アクティブなトランザクションが存在していない場合にのみ実行できます。

トランザクション ステートメント

次のステートメントでは、Cloud Spanner トランザクションを管理して commit します。

SHOW VARIABLE READ_TIMESTAMP

最新の読み取り専用トランザクションの読み取りタイムスタンプが含まれる TIMESTAMP 型の 1 行と 1 列の結果セットを返します。このステートメントがタイムスタンプを返すのは、読み取り専用トランザクションがまだアクティブで、少なくとも 1 つのクエリを実行した場合、または読み取り専用トランザクションが commit されてから新しいトランザクションが開始されるまでの間のみです。それ以外の場合、結果は NULL です。

SHOW VARIABLE COMMIT_TIMESTAMP

Cloud Spanner が commit した最後に読み取り / 書き込みトランザクションの commit タイムスタンプが含まれる TIMESTAMP 型の 1 行と 1 列の結果セットを返します。このステートメントは、読み取り / 書き込みトランザクションを commit した後、後続の SELECTDML、またはスキーマ変更ステートメントを実行する前にだけタイムスタンプを返します。それ以外の場合、結果は NULL です。

BEGIN [TRANSACTION]

新しいトランザクションを開始します。

  • AUTOCOMMIT モードが有効になっている場合、このステートメントは一時的に接続の AUTOCOMMIT モードを解除します。トランザクションが終了すると、接続が AUTOCOMMIT モードに戻ります。
  • AUTOCOMMIT モードが無効になっている場合、このステートメントはオプションであり、影響はありません。

このステートメントは、アクティブなトランザクションが存在していない場合にのみ実行できます。

COMMIT [TRANSACTION]

現在のトランザクションを commit します。

  • 読み取り / 書き込みトランザクションを commit すると、このトランザクションのすべての更新が他のトランザクションに表示され、Cloud Spanner でのトランザクションのすべてのロックが解除されます。
  • 読み取り専用トランザクションを commit すると、現在の読み取り専用トランザクションが終了します。後続のステートメントで、新しいトランザクションが開始されます。読み取り専用トランザクションの COMMITROLLBACK にはセマンティックの違いがありません。

このステートメントは、アクティブなトランザクションがある間にのみ実行できます。

ROLLBACK [TRANSACTION]

現在のトランザクションの ROLLBACK を実行します。

  • 読み取り / 書き込みトランザクションの ROLLBACK を実行すると、バッファリングされたミューテーションが消去され、Cloud Spanner でトランザクションがロールバックされ、トランザクションで保持されているロックがすべて解除されれます。
  • 読み取り専用トランザクションの ROLLBACK を実行すると、現在の読み取り専用トランザクションが終了します。後続のステートメントで、新しいトランザクションが開始されます。接続の読み取り専用トランザクションの COMMITROLLBACK にはセマンティックの違いがありません。

このステートメントは、アクティブなトランザクションがある間にのみ実行できます。

SET TRANSACTION

構文
SET TRANSACTION { READ ONLY | READ WRITE }

現在のトランザクションのトランザクション モードを設定します。

このステートメントを実行できるのは、AUTOCOMMIT モードを無効にしている場合、または BEGIN [TRANSACTION] を実行して一時的なトランザクションを開始し、トランザクションでまだステートメントを実行していない場合のみです。

バッチ ステートメント

次のステートメントでは、DDL ステートメントのバッチを管理し、これらのバッチを Cloud Spanner に送信します。

START BATCH DDL

接続で DDL ステートメントのバッチを開始します。バッチ中の後続のステートメントはすべて、DDL ステートメントにする必要があります。DDL ステートメントはローカルでバッファリングされ、RUN BATCH の実行時に 1 つのバッチとして Cloud Spanner に送信されます。複数の DDL ステートメントを 1 つのバッチとして実行すると、通常はステートメントを個別に実行するよりも高速になります。

このステートメントは、アクティブなトランザクションが存在していない場合にのみ実行できます。

RUN BATCH

現在の DDL バッチでバッファリングされたすべての DDL ステートメントをデータベースに送信し、Cloud Spanner がこれらのステートメントを実行するのを待機して、現在の DDL バッチを終了します。

Cloud Spanner が少なくとも 1 つの DDL ステートメントを実行できない場合は、RUN BATCH が Cloud Spanner で実行できない最初の DDL ステートメントに対してエラーを返します。それ以外の場合は、RUN BATCH は正常に動作します。

ABORT BATCH

現在の DDL バッチ内のバッファリングされた DDL ステートメントをすべて消去し、バッチを終了します。

このステートメントは、DDL バッチがアクティブな場合にのみ実行できます。バッチで DDL ステートメントがバファリングされいるかどうかにかかわらず、ABORT BATCH を使用できます。

START BATCH DML

次のステートメントは 2 つの DML ステートメントをまとめてバッチ処理し、1 回の呼び出しでサーバーに送信します。DML バッチは、トランザクションの一部として、または自動 commit モードで実行できます。

START BATCH DML
INSERT INTO MYTABLE (ID, NAME) VALUES (1, 'ONE')
INSERT INTO MYTABLE (ID, NAME) VALUES (2, 'TWO')
RUN BATCH

次のステップ

オープンソースの JDBC ドライバに関するよくある質問への回答を得る。