오픈소스 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 드라이버를 로드하기 위해 자바 클래스 이름이 필요한 프레임 워크를 사용하는 경우에는 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]");
  }
}

자동 커밋 모드에서 트랜잭션을 사용하여 행 추가

여러 작업을 그룹으로 커밋할 필요가 없는 경우 트랜잭션을 기본 동작인 자동 커밋 모드에서 사용할 수 있습니다. 다음 코드 예시에서는 자동 커밋 모드에서 트랜잭션을 사용하여 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));
      }
    }
  }
}

그룹으로 여러 작업이 커밋되는 방식 제어

Cloud Spanner가 여러 작업을 그룹으로 함께 커밋할지 여부를 제어하려면 자동 커밋 모드를 사용 중지하면 됩니다. 다음 코드 예시에서는 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));
        }
      }
    }
  }
}

세션 관리 문

Cloud Spanner용 오픈소스 JDBC(Java Database Connectivity) 드라이버SQL 쿼리 문 외에도 연결 상태를 수정하고 트랜잭션을 실행하며 문의 배치를 효율적으로 실행할 수 있는 세션 관리 문을 지원합니다.

연결 문

다음 문은 현재 연결의 속성을 변경하거나 표시합니다.

변수 READONLY 표시

연결이 현재 읽기 전용 모드인지 여부를 나타내는 BOOL 유형인 하나의 행과 하나의 열로 결과 조합을 반환합니다.

READONLY 설정

구문
SET READONLY = { true | false }

연결이 읽기 전용 모드인지 여부를 설정합니다.

활성 트랜잭션이 없는 경우에만 이 문을 실행할 수 있습니다.

변수 AUTOCOMMIT 표시

연결이 AUTOCOMMIT 모드인지 여부를 나타내는 BOOL 유형인 하나의 행과 하나의 열로 결과 조합을 반환합니다.

AUTOCOMMIT 설정

구문
SET AUTOCOMMIT = { true | false }

연결의 AUTOCOMMIT 모드를 설정합니다.

활성 트랜잭션이 없는 경우에만 이 문을 실행할 수 있습니다.

변수 RETRY_ABORTS_INTERNALLY 표시

연결이 취소된 트랜잭션을 자동으로 재시도하는지 여부를 나타내는 BOOL 유형인 하나의 행과 하나의 열로 결과 조합을 반환합니다.

RETRY_ABORTS_INTERNALLY 설정

구문
SET RETRY_ABORTS_INTERNALLY = { true | false }

연결이 취소된 트랜잭션을 자동으로 재시도할지 여부를 설정합니다.

활성 트랜잭션이 없는 경우에만 이 문을 실행할 수 있습니다.

RETRY_ABORTS_INTERNALLY를 사용 설정하면 연결이 클라이언트 애플리케이션에 반환하는 모든 데이터의 암호화 체크섬을 유지하고 트랜잭션 중에 연결이 보고하는 모든 업데이트 수를 유지합니다. Cloud Spanner가 트랜잭션을 취소하면 연결은 동일한 트랜잭션을 실행하고 반환된 데이터가 원래 트랜잭션에서 반환된 데이터와 정확하게 일치하는지 확인합니다. 데이터가 일치하면 Cloud Spanner는 트랜잭션을 계속 진행합니다. 데이터가 일치하지 않는 경우 AbortedDueToConcurrentModification이 발생하여 트랜잭션이 실패합니다.

이 설정은 기본적으로 사용 설정되어 있습니다. 애플리케이션에서 이미 취소된 트랜잭션을 재시도하는 경우 이 설정을 사용 중지하는 것이 좋습니다.

변수 AUTOCOMMIT_DML_MODE 표시

DML(데이터 조작 언어) 문에 대한 자동 커밋 모드를 나타내는 STRING 유형인 하나의 행과 하나의 열로 결과 조합을 반환합니다.

이 변수는 연결에서 AUTOCOMMIT 모드를 사용 설정한 경우에만 적용됩니다.

AUTOCOMMIT_DML_MODE 설정

구문
SET AUTOCOMMIT_DML_MODE = { 'TRANSACTIONAL' | 'PARTITIONED_NON_ATOMIC' }

DML 문의 자동 커밋 모드를 설정합니다.

  • TRANSACTIONAL 모드에서 드라이버는 별도의 원자적 트랜잭션으로 DML 문을 실행합니다. 드라이버는 새 트랜잭션을 생성하고 DML 문을 실행하며 성공적인 실행 시 트랜잭션을 커밋하거나 오류가 발생한 경우 트랜잭션을 롤백합니다.
  • PARTITIONED_NON_ATOMIC 모드에서 드라이버는 파티션을 나눈 업데이트 문으로 DML 문을 실행합니다. 파티션을 나눈 업데이트 문은 각각 영향을 받는 하위 집합을 다루는 일련의 많은 트랜잭션으로 실행할 수 있으며, 파티션을 나눈 문은 확장성과 성능을 향상하기 위해 거래에서 약화된 시맨틱스를 제공합니다.

AUTOCOMMIT 모드를 사용 설정한 경우에는 해당 문만 실행할 수 있습니다.

변수 STATEMENT_TIMEOUT 표시

문에 대한 현재 제한 시간 값을 나타내는 STRING 유형인 하나의 행과 하나의 열로 결과 조합을 반환합니다. 이 값은 시간 단위를 나타내는 서픽스가 뒤에 오는 정수입니다. NULL의 값은 제한 시간 값이 설정되지 않았음을 나타냅니다. 문 제한 시간 값이 설정되어 있는 경우 문이 지정된 제한 시간 값보다 오래 걸리면 java.sql.SQLTimeoutException을 발생시키고 트랜잭션을 무효화합니다.

STATEMENT_TIMEOUT 설정

구문
SET STATEMENT_TIMEOUT = { '<INT64>{ s | ms | us | ns }' | NULL }

연결의 모든 후속 문에 대한 문 제한 시간 값을 설정합니다. 제한 시간 값을 NULL로 설정하면 연결에 대한 문 제한 시간이 사용 중지됩니다.

지원되는 시간 단위는 다음과 같습니다.

  • s: 초
  • ms: 밀리초
  • us: 마이크로초
  • ns: 나노초

트랜잭션 동안 문 제한 시간은 트랜잭션을 무효화하고 무효화된 트랜잭션(ROLLBACK 예외)의 모든 후속 문은 실패하고 JDBC 드라이버가 java.sql.SQLTimeoutException을 처리합니다.

변수 READ_ONLY_STALEEN 표시

Cloud Spanner가 AUTOCOMMIT 모드에서 읽기 전용 트랜잭션과 쿼리에 사용하는 현재 읽기 전용 비활성 설정을 나타내는 STRING 유형인 하나의 행과 하나의 열로 결과 조합을 반환합니다. 기본 설정은 STRONG입니다.

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 모드에 있는 모든 쿼리에 대해 읽기 전용 비활성 설정을 설정합니다.

타임스탬프 경계 옵션은 다음과 같습니다.

  • STRONG은 Cloud Spanner에게 강력 읽기를 수행하라고 지시합니다.
  • MAX_STALENESS는 Cloud Spanner가 now()를 기준으로 제한된 비활성 읽기를 수행하는 데 사용하는 시간 간격을 정의합니다.
  • MIN_READ_TIMESTAMP는 Cloud Spanner가 제한된 비활성 읽기를 수행하는 데 사용하는 절대 시간을 정의합니다.
  • EXACT_STALENESS는 Cloud Spanner가 now()를 기준으로 완전 비활성 읽기를 수행하는 데 사용하는 시간 간격을 정의합니다.
  • READ_TIMESTAMP는 Cloud Spanner가 완전 비활성 읽기를 수행하는 데 사용하는 절대 시간을 정의합니다.

타임스탬프는 다음 형식을 사용해야 합니다.

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

MAX_STALENESSEXACT_STALENESS 값 설정에 지원되는 시간 단위는 다음과 같습니다.

  • s: 초
  • ms: 밀리초
  • us: 마이크로초
  • ns: 나노초

활성 트랜잭션이 없는 경우에만 이 문을 실행할 수 있습니다.

트랜잭션 문

다음 문은 Cloud Spanner 트랜잭션을 관리하고 커밋합니다.

변수 READ_TIMESTAMP 표시

가장 최근의 읽기 전용 트랜잭션의 읽기 타임스탬프를 포함하는 TIMESTAMP 유형인 하나의 행과 하나의 열로 결과 조합을 반환합니다. 이 문은 읽기 전용 트랜잭션이 아직 활성 상태이고 하나 이상의 쿼리를 실행한 경우 또는 읽기 전용 트랜잭션이 커밋된 직후 및 새 트랜잭션이 시작되기 전에만 타임스탬프를 반환합니다. 그렇지 않은 경우 결과는 NULL입니다.

변수 COMMIT_TIMESTAMP 표시

Cloud Spanner가 커밋한 마지막 읽기-쓰기 트랜잭션의 커밋 타임스탬프를 포함하는 TIMESTAMP 유형인 하나의 행과 하나의 열로 결과 조합을 반환합니다. 이 문은 읽기-쓰기 트랜잭션을 커밋한 후와 이후 SELECT, DML 또는 스키마 변경 문을 실행하기 전에만 타임스탬프를 반환합니다. 그렇지 않은 경우 결과는 NULL입니다.

[트랜잭션] 시작

새 트랜잭션을 시작합니다.

  • AUTOCOMMIT 모드를 사용 설정한 경우 이 문은 일시적으로 AUTOCOMMIT 모드에서 연결을 해제합니다. 트랜잭션이 종료되면 이 연결은 AUTOCOMMIT 모드로 돌아갑니다.
  • AUTOCOMMIT 모드를 사용하지 않으면 이 문은 선택사항이며 영향을 주지 않습니다.

활성 트랜잭션이 없는 경우에만 이 문을 실행할 수 있습니다.

[트랜잭션] 커밋

현재 트랜잭션을 커밋합니다.

  • 읽기-쓰기 트랜잭션을 커밋하면 이 트랜잭션의 모든 업데이트가 다른 트랜잭션에 표시되고 Cloud Spanner에서 트랜잭션의 모든 잠금이 해제됩니다.
  • 읽기 전용 트랜잭션을 커밋하면 현재 읽기 전용 트랜잭션이 종료됩니다. 이후 문은 새 트랜잭션을 시작합니다. 읽기 전용 트랜잭션의 경우 COMMITROLLBACK 간의 의미적 차이가 없습니다.

활성 트랜잭션이 있는 경우에만 이 문을 실행할 수 있습니다.

[트랜잭션] 롤백

현재 트랜잭션의 ROLLBACK을 수행합니다.

  • 읽기 전용 트랜잭션의 ROLLBACK을 수행하면 버퍼링된 변형이 삭제되고 Cloud Spanner에 트랜잭션이 롤백되고 트랜잭션에 있는 모든 잠금이 해제됩니다.
  • 읽기 전용 트랜잭션의 ROLLBACK을 수행하면 현재 읽기 전용 트랜잭션을 종료합니다. 이후 모든 문은 새 트랜잭션을 시작합니다. 연결에 대한 읽기 전용 트랜잭션의 경우 COMMITROLLBACK 간의 의미적 차이가 없습니다.

활성 트랜잭션이 있는 경우에만 이 문을 실행할 수 있습니다.

트랜잭션 설정

구문
SET TRANSACTION { READ ONLY | READ WRITE }

현재 트랜잭션의 트랜잭션 모드를 설정합니다.

AUTOCOMMIT 모드를 사용 설정하지 않았거나 BEGIN [TRANSACTION]을 실행하여 임시 트랜잭션을 실행했지만 트랜잭션에서 아직 어떤 문도 실행하지 않은 경우에만 이 문을 실행할 수 있습니다.

일괄 문

다음 문은 일괄 DDL 문을 관리하고 해당 일괄 문을 Cloud Spanner로 전송합니다.

일괄 DDL 시작

연결에서 일괄 DDL 문을 시작합니다. 일괄 처리되는 동안 모든 후속 문은 DDL 문이어야 합니다. DDL 문은 로컬에서 버퍼링되며 RUN BATCH 실행 시 하나의 일괄 처리로 Cloud Spanner에 전송됩니다. 여러 개의 DDL 문을 하나의 일괄 처리로 실행하는 것은 문을 개별적으로 실행하는 것보다 일반적으로 빠릅니다.

활성 트랜잭션이 없는 경우에만 이 문을 실행할 수 있습니다.

일괄 실행

현재 DDL 일괄 처리의 버퍼링된 DDL 문을 모두 데이터베이스로 전송하고 Cloud Spanner에서 이러한 문이 실행되기를 기다린 다음 현재 DDL 일괄 처리를 종료합니다.

Cloud Spanner에서 하나 이상의 DDL 문을 실행할 수 없는 경우 RUN BATCH는 Cloud Spanner가 실행할 수 없는 첫 번째 DDL 문에 대한 오류를 반환합니다. 그렇지 않은 경우 RUN BATCH를 반환합니다.

일괄 처리 취소

현재 DDL 일괄 처리에서 버퍼링된 DDL 문을 모두 지우고 일괄 처리를 종료합니다.

DDL 일괄 처리가 활성화된 경우에만 이 문을 실행할 수 있습니다. 일괄 처리가 DDL 문을 버퍼링했는지 여부와 상관없이 ABORT BATCH를 사용할 수 있습니다.

BATCH DML 시작

다음 문은 2개의 DML 문을 함께 일괄 처리하고 서버에 하나의 호출로 전송합니다. DML 일괄 처리는 트랜잭션의 일부로 또는 자동 커밋 모드에서 실행할 수 있습니다.

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

다음 단계

오픈소스 JDBC 드라이버와 관련하여 자주 묻는 질문(FAQ)과 그에 대한 답변을 제공합니다.