diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 4dd9974f2..b9b8be0c3 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -25,7 +25,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- java: [11, 17, 21]
+ java: [11, 17, 21, 25]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
diff --git a/.github/workflows/unmanaged_dependency_check.yaml b/.github/workflows/unmanaged_dependency_check.yaml
index 7394b52af..2cf303dc3 100644
--- a/.github/workflows/unmanaged_dependency_check.yaml
+++ b/.github/workflows/unmanaged_dependency_check.yaml
@@ -14,6 +14,6 @@ jobs:
shell: bash
run: .kokoro/build.sh
- name: Unmanaged dependency check
- uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.52.2
+ uses: googleapis/sdk-platform-java/java-shared-dependencies/unmanaged-dependency-check@google-cloud-shared-dependencies/v3.52.3
with:
bom-path: pom.xml
diff --git a/.kokoro/presubmit/graalvm-native-a.cfg b/.kokoro/presubmit/graalvm-native-a.cfg
index 631631deb..85695810d 100644
--- a/.kokoro/presubmit/graalvm-native-a.cfg
+++ b/.kokoro/presubmit/graalvm-native-a.cfg
@@ -3,7 +3,7 @@
# Configure the docker image for kokoro-trampoline.
env_vars: {
key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.52.2"
+ value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_a:3.52.3"
}
env_vars: {
diff --git a/.kokoro/presubmit/graalvm-native-b.cfg b/.kokoro/presubmit/graalvm-native-b.cfg
index 16cf471b0..c2efdcebf 100644
--- a/.kokoro/presubmit/graalvm-native-b.cfg
+++ b/.kokoro/presubmit/graalvm-native-b.cfg
@@ -3,7 +3,7 @@
# Configure the docker image for kokoro-trampoline.
env_vars: {
key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.52.2"
+ value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_b:3.52.3"
}
env_vars: {
diff --git a/.kokoro/presubmit/graalvm-native-c.cfg b/.kokoro/presubmit/graalvm-native-c.cfg
index 5d2697122..20946e7b0 100644
--- a/.kokoro/presubmit/graalvm-native-c.cfg
+++ b/.kokoro/presubmit/graalvm-native-c.cfg
@@ -3,7 +3,7 @@
# Configure the docker image for kokoro-trampoline.
env_vars: {
key: "TRAMPOLINE_IMAGE"
- value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_c:3.52.2"
+ value: "gcr.io/cloud-devrel-public-resources/graalvm_sdk_platform_c:3.52.3"
}
env_vars: {
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 38f6d8d3f..d5fe61cbd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,25 @@
# Changelog
+## [2.33.1](https://github.com/googleapis/java-spanner-jdbc/compare/v2.33.0...v2.33.1) (2025-10-09)
+
+
+### Dependencies
+
+* Update dependency com.google.api.grpc:proto-google-cloud-trace-v1 to v2.76.0 ([#2240](https://github.com/googleapis/java-spanner-jdbc/issues/2240)) ([442565e](https://github.com/googleapis/java-spanner-jdbc/commit/442565ea8bf22ee926126fbd11a68a869b0772c7))
+* Update dependency com.google.cloud:google-cloud-spanner to v6.101.1 ([#2227](https://github.com/googleapis/java-spanner-jdbc/issues/2227)) ([275c6bd](https://github.com/googleapis/java-spanner-jdbc/commit/275c6bd7f3f58b3f980e9ffaa576d1f910ecf08b))
+* Update dependency com.google.cloud:google-cloud-spanner to v6.102.0 ([#2244](https://github.com/googleapis/java-spanner-jdbc/issues/2244)) ([383a392](https://github.com/googleapis/java-spanner-jdbc/commit/383a39222c7526163323575734c8473a34d4098d))
+* Update dependency com.google.cloud:google-cloud-spanner-bom to v6.101.1 ([#2228](https://github.com/googleapis/java-spanner-jdbc/issues/2228)) ([ded7944](https://github.com/googleapis/java-spanner-jdbc/commit/ded79447b8ecd294e56560b43cb02e45464706f1))
+* Update dependency com.google.cloud:google-cloud-spanner-bom to v6.102.0 ([#2246](https://github.com/googleapis/java-spanner-jdbc/issues/2246)) ([7162ac2](https://github.com/googleapis/java-spanner-jdbc/commit/7162ac236a77c24b4f0faccefa2cf3bdee6b96ab))
+* Update dependency com.google.cloud:google-cloud-trace to v2.76.0 ([#2241](https://github.com/googleapis/java-spanner-jdbc/issues/2241)) ([ff2432d](https://github.com/googleapis/java-spanner-jdbc/commit/ff2432d46cd23df2230671297f06c8e6fca8f1d7))
+* Update dependency com.google.cloud:sdk-platform-java-config to v3.52.3 ([#2236](https://github.com/googleapis/java-spanner-jdbc/issues/2236)) ([9a44975](https://github.com/googleapis/java-spanner-jdbc/commit/9a44975e0ab7ca49514be1d9ea03e8e760a0f3bc))
+* Update dependency net.bytebuddy:byte-buddy to v1.17.8 ([#2245](https://github.com/googleapis/java-spanner-jdbc/issues/2245)) ([60a3a8f](https://github.com/googleapis/java-spanner-jdbc/commit/60a3a8f0fcf86742128f8803ee3297670b713923))
+* Update dependency net.bytebuddy:byte-buddy-agent to v1.17.8 ([#2243](https://github.com/googleapis/java-spanner-jdbc/issues/2243)) ([952c08a](https://github.com/googleapis/java-spanner-jdbc/commit/952c08afbf2f7838ad864b8bb58423e8b566d325))
+
+
+### Documentation
+
+* Add samples for transaction isolation level ([#2030](https://github.com/googleapis/java-spanner-jdbc/issues/2030)) ([ca243d1](https://github.com/googleapis/java-spanner-jdbc/commit/ca243d194ef392d44cea561a148c0376b9af630d))
+
## [2.33.0](https://github.com/googleapis/java-spanner-jdbc/compare/v2.32.3...v2.33.0) (2025-09-27)
diff --git a/README.md b/README.md
index 61b3d5cb2..8fb6a054d 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@ If you are using Maven, add this to your pom.xml file:
com.google.cloud
google-cloud-spanner-jdbc
- 2.33.0
+ 2.33.1
```
@@ -30,7 +30,7 @@ If you are using Gradle without BOM, add this to your dependencies
```Groovy
-implementation 'com.google.cloud:google-cloud-spanner-jdbc:2.33.0'
+implementation 'com.google.cloud:google-cloud-spanner-jdbc:2.33.1'
```
@@ -38,7 +38,7 @@ If you are using SBT, add this to your dependencies
```Scala
-libraryDependencies += "com.google.cloud" % "google-cloud-spanner-jdbc" % "2.33.0"
+libraryDependencies += "com.google.cloud" % "google-cloud-spanner-jdbc" % "2.33.1"
```
@@ -109,6 +109,7 @@ See [Supported Connection Properties](documentation/connection_properties.md) fo
supported connection properties.
#### Commonly Used Properties
+- default_isolation_level (String): Spanner supports isolation levels REPEATABLE_READ or SERIALIZABLE. SERIALIZABLE is the default. Using isolation level REPEATABLE_READ improves performance by reducing the amount of locks that are taken by transactions that execute a large number of queries in read/write transactions. See https://cloud.google.com/spanner/docs/isolation-levels for more information on the supported isolation levels in Spanner.
- credentials (String): URL for the credentials file to use for the connection. If you do not specify any credentials at all, the default credentials of the environment as returned by `GoogleCredentials#getApplicationDefault()` is used. Example: `jdbc:cloudspanner:/projects/my-project/instances/my-instance/databases/my-db;credentials=/path/to/credentials.json`
- autocommit (boolean): Sets the initial autocommit mode for the connection. Default is true.
- readonly (boolean): Sets the initial readonly mode for the connection. Default is false.
diff --git a/documentation/latency-debugging-guide.md b/documentation/latency-debugging-guide.md
index cdcfec826..0d2084c3f 100644
--- a/documentation/latency-debugging-guide.md
+++ b/documentation/latency-debugging-guide.md
@@ -5,6 +5,43 @@ queries and transactions and to determine whether transactions or requests are b
addition, all metrics described in [Latency points in a Spanner request](https://cloud.google.com/spanner/docs/latency-points)
are also collected by the JDBC driver and can be used for debugging.
+## Isolation Level
+
+A common reason for high latency in read/write transactions is lock contention. Spanner by default
+uses isolation level `SERIALIZABLE`. This causes all queries in read/write transactions to take
+locks for all rows that are scanned by a query. Using isolation level `REPEATABLE_READ` reduces the
+number of locks that are taken during a read/write transaction, and can significantly improve
+performance for applications that execute many and/or large queries in read/write transactions.
+
+Enable isolation level `REPEATABLE_READ` by default for all transactions that are executed by the
+JDBC driver by setting the `default_isolation_level` connection property like this in the connection
+URL:
+
+```java
+String projectId = "my-project";
+String instanceId = "my-instance";
+String databaseId = "my-database";
+String isolationLevel = "REPEATABLE_READ";
+
+try (Connection connection =
+ DriverManager.getConnection(
+ String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s?default_isolation_level=%s",
+ projectId, instanceId, databaseId, isolationLevel))) {
+ try (Statement statement = connection.createStatement()) {
+ try (ResultSet rs = statement.executeQuery("SELECT CURRENT_TIMESTAMP()")) {
+ while (rs.next()) {
+ System.out.printf(
+ "Connected to Cloud Spanner at [%s]%n", rs.getTimestamp(1).toString());
+ }
+ }
+ }
+}
+```
+
+See https://cloud.google.com/spanner/docs/isolation-levels for more information on the supported
+isolation levels in Spanner.
+
## Configuration
You can configure the OpenTelemetry instance that should be used in two ways:
diff --git a/pom.xml b/pom.xml
index b457a2356..fcd478ebb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
google-cloud-spanner-jdbc
- 2.33.0
+ 2.33.1
jar
Google Cloud Spanner JDBC
https://github.com/googleapis/java-spanner-jdbc
@@ -14,7 +14,7 @@
com.google.cloud
sdk-platform-java-config
- 3.52.2
+ 3.52.3
@@ -61,7 +61,7 @@
com.google.cloud
google-cloud-spanner-bom
- 6.101.1
+ 6.102.0
pom
import
@@ -216,13 +216,13 @@
com.google.cloud
google-cloud-trace
- 2.75.0
+ 2.76.0
test
com.google.api.grpc
proto-google-cloud-trace-v1
- 2.75.0
+ 2.76.0
test
diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml
index 06eab9662..041fab8ac 100644
--- a/samples/install-without-bom/pom.xml
+++ b/samples/install-without-bom/pom.xml
@@ -29,7 +29,7 @@
com.google.cloud
google-cloud-spanner-jdbc
- 2.32.3
+ 2.33.0
diff --git a/samples/quickperf/pom.xml b/samples/quickperf/pom.xml
index 1a79d8825..ca7335c4f 100644
--- a/samples/quickperf/pom.xml
+++ b/samples/quickperf/pom.xml
@@ -12,7 +12,7 @@
com.google.cloud
sdk-platform-java-config
- 3.52.2
+ 3.52.3
@@ -27,7 +27,7 @@
com.google.cloud
libraries-bom
- 26.68.0
+ 26.69.0
pom
import
@@ -95,7 +95,7 @@
org.codehaus.mojo
exec-maven-plugin
- 3.5.1
+ 3.6.1
com.google.cloud.jdbc.quickperf.QuickPerf
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml
index 59afa6f0d..46627619c 100644
--- a/samples/snapshot/pom.xml
+++ b/samples/snapshot/pom.xml
@@ -29,7 +29,7 @@
com.google.cloud
google-cloud-spanner-jdbc
- 2.33.0
+ 2.33.1
diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml
index e82086db7..07c7ab457 100644
--- a/samples/snippets/pom.xml
+++ b/samples/snippets/pom.xml
@@ -9,7 +9,7 @@
com.google.cloud
sdk-platform-java-config
- 3.52.2
+ 3.52.3
@@ -26,7 +26,7 @@
com.google.cloud
libraries-bom
- 26.68.0
+ 26.69.0
pom
import
diff --git a/samples/snippets/src/main/java/com/example/spanner/jdbc/IsolationLevel.java b/samples/snippets/src/main/java/com/example/spanner/jdbc/IsolationLevel.java
new file mode 100644
index 000000000..7153b7c7a
--- /dev/null
+++ b/samples/snippets/src/main/java/com/example/spanner/jdbc/IsolationLevel.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.spanner.jdbc;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Properties;
+
+final class IsolationLevel {
+
+ static void isolationLevel(
+ final String project,
+ final String instance,
+ final String database,
+ final Properties properties)
+ throws SQLException {
+ String url = String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ project, instance, database);
+ try (Connection connection = DriverManager.getConnection(url, properties)) {
+ connection.setAutoCommit(false);
+
+ // Spanner supports setting the isolation level to:
+ // 1. TRANSACTION_SERIALIZABLE (this is the default)
+ // 2. TRANSACTION_REPEATABLE_READ
+
+ // The following line sets the default isolation level that will be used
+ // for all read/write transactions on this connection.
+ connection.setTransactionIsolation(
+ Connection.TRANSACTION_REPEATABLE_READ);
+
+ // This query will not take any locks when using
+ // isolation level repeatable read.
+ try (ResultSet resultSet = connection
+ .createStatement()
+ .executeQuery("SELECT SingerId, Active "
+ + "FROM Singers "
+ + "ORDER BY LastName")) {
+ while (resultSet.next()) {
+ try (PreparedStatement statement = connection.prepareStatement(
+ "INSERT OR UPDATE INTO SingerHistory "
+ + "(SingerId, Active, CreatedAt) "
+ + "VALUES (?, ?, CURRENT_TIMESTAMP)")) {
+ statement.setLong(1, resultSet.getLong(1));
+ statement.setBoolean(2, resultSet.getBoolean(2));
+ statement.executeUpdate();
+ }
+ }
+ }
+ connection.commit();
+ }
+ }
+
+ private IsolationLevel() {
+ }
+}
diff --git a/samples/snippets/src/test/java/com/example/spanner/jdbc/IsolationLevelTest.java b/samples/snippets/src/test/java/com/example/spanner/jdbc/IsolationLevelTest.java
new file mode 100644
index 000000000..e1c7bfbe0
--- /dev/null
+++ b/samples/snippets/src/test/java/com/example/spanner/jdbc/IsolationLevelTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.spanner.jdbc;
+
+import static com.example.spanner.jdbc.IsolationLevel.isolationLevel;
+import static org.junit.Assume.assumeTrue;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Properties;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.testcontainers.DockerClientFactory;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.images.PullPolicy;
+import org.testcontainers.utility.DockerImageName;
+
+@RunWith(JUnit4.class)
+public class IsolationLevelTest {
+
+ private static GenericContainer> emulator;
+
+ private static final String PROJECT = "emulator-project";
+ private static final String INSTANCE = "my-instance";
+ private static final String DATABASE = "my-database";
+ private static final Properties PROPERTIES = new Properties();
+
+ @BeforeClass
+ public static void setupEmulator() throws Exception {
+ assumeTrue("This test requires Docker", DockerClientFactory.instance().isDockerAvailable());
+
+ emulator =
+ new GenericContainer<>(DockerImageName.parse("gcr.io/cloud-spanner-emulator/emulator"));
+ emulator.withImagePullPolicy(PullPolicy.alwaysPull());
+ emulator.addExposedPort(9010);
+ emulator.setWaitStrategy(Wait.forListeningPorts(9010));
+ emulator.start();
+
+ PROPERTIES.setProperty("endpoint",
+ String.format("localhost:%d", emulator.getMappedPort(9010)));
+ PROPERTIES.setProperty("autoConfigEmulator", "true");
+
+ String url = String.format(
+ "jdbc:cloudspanner:/projects/%s/instances/%s/databases/%s",
+ PROJECT, INSTANCE, DATABASE);
+ try (Connection connection = DriverManager.getConnection(url, PROPERTIES)) {
+ try (Statement statement = connection.createStatement()) {
+ statement.addBatch(
+ "CREATE TABLE Singers ("
+ + "SingerId INT64 PRIMARY KEY, "
+ + "FirstName STRING(MAX), "
+ + "LastName STRING(MAX), "
+ + "Active BOOL)");
+ statement.addBatch(
+ "CREATE TABLE SingerHistory ("
+ + "SingerId INT64, "
+ + "Active BOOL, "
+ + "CreatedAt TIMESTAMP) "
+ + "PRIMARY KEY (SingerId, CreatedAt)");
+ statement.executeBatch();
+ }
+ }
+ }
+
+ @AfterClass
+ public static void stopEmulator() {
+ if (emulator != null) {
+ emulator.stop();
+ }
+ }
+
+ @Test
+ public void testIsolationLevel() throws SQLException {
+ isolationLevel("emulator-project", "my-instance", "my-database", PROPERTIES);
+ }
+
+}
diff --git a/samples/spring-data-jdbc/googlesql/pom.xml b/samples/spring-data-jdbc/googlesql/pom.xml
index 31db916ba..be86aa508 100644
--- a/samples/spring-data-jdbc/googlesql/pom.xml
+++ b/samples/spring-data-jdbc/googlesql/pom.xml
@@ -30,14 +30,14 @@
com.google.cloud
google-cloud-spanner-bom
- 6.100.0
+ 6.102.0
import
pom
com.google.cloud
libraries-bom
- 26.68.0
+ 26.69.0
import
pom
diff --git a/samples/spring-data-jdbc/postgresql/pom.xml b/samples/spring-data-jdbc/postgresql/pom.xml
index aa78ad2eb..b7305065b 100644
--- a/samples/spring-data-jdbc/postgresql/pom.xml
+++ b/samples/spring-data-jdbc/postgresql/pom.xml
@@ -30,14 +30,14 @@
com.google.cloud
google-cloud-spanner-bom
- 6.100.0
+ 6.102.0
import
pom
com.google.cloud
libraries-bom
- 26.68.0
+ 26.69.0
import
pom
@@ -110,6 +110,19 @@
testlib
test
+
+
+ net.bytebuddy
+ byte-buddy
+ 1.17.8
+ test
+
+
+ net.bytebuddy
+ byte-buddy-agent
+ 1.17.8
+ test
+
junit
junit
diff --git a/samples/spring-data-mybatis/googlesql/pom.xml b/samples/spring-data-mybatis/googlesql/pom.xml
index b4aa07c1d..0734042ae 100644
--- a/samples/spring-data-mybatis/googlesql/pom.xml
+++ b/samples/spring-data-mybatis/googlesql/pom.xml
@@ -35,14 +35,14 @@
com.google.cloud
google-cloud-spanner-bom
- 6.100.0
+ 6.102.0
import
pom
com.google.cloud
libraries-bom
- 26.68.0
+ 26.69.0
import
pom
@@ -94,7 +94,7 @@
com.google.cloud
google-cloud-spanner
- 6.100.0
+ 6.102.0
test-jar
test
@@ -104,6 +104,19 @@
testlib
test
+
+
+ net.bytebuddy
+ byte-buddy
+ 1.17.8
+ test
+
+
+ net.bytebuddy
+ byte-buddy-agent
+ 1.17.8
+ test
+
junit
junit
diff --git a/samples/spring-data-mybatis/postgresql/pom.xml b/samples/spring-data-mybatis/postgresql/pom.xml
index 5f2b06f55..449634a8a 100644
--- a/samples/spring-data-mybatis/postgresql/pom.xml
+++ b/samples/spring-data-mybatis/postgresql/pom.xml
@@ -35,7 +35,7 @@
com.google.cloud
libraries-bom
- 26.68.0
+ 26.69.0
import
pom
@@ -101,6 +101,19 @@
testlib
test
+
+
+ net.bytebuddy
+ byte-buddy
+ 1.17.8
+ test
+
+
+ net.bytebuddy
+ byte-buddy-agent
+ 1.17.8
+ test
+
junit
junit
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/JdbcDatabaseMetaDataTest.java b/src/test/java/com/google/cloud/spanner/jdbc/JdbcDatabaseMetaDataTest.java
index 7dc4667e4..d5a429078 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/JdbcDatabaseMetaDataTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/JdbcDatabaseMetaDataTest.java
@@ -23,7 +23,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import com.google.auth.oauth2.GoogleCredentials;
+import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.spanner.Dialect;
import com.google.cloud.spanner.connection.ConnectionOptions;
import com.google.cloud.spanner.connection.ConnectionOptionsTest;
@@ -555,8 +555,8 @@ public void testGetVersionColumns() throws SQLException {
@Test
public void testGetUserName() throws SQLException, IOException {
- GoogleCredentials credentials =
- GoogleCredentials.fromStream(
+ ServiceAccountCredentials credentials =
+ ServiceAccountCredentials.fromStream(
Objects.requireNonNull(ConnectionOptionsTest.class.getResource("test-key.json"))
.openStream());
JdbcConnection connection = mock(JdbcConnection.class);
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/JdbcTransactionOptionsTest.java b/src/test/java/com/google/cloud/spanner/jdbc/JdbcTransactionOptionsTest.java
index a34e449e7..8a5af27c0 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/JdbcTransactionOptionsTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/JdbcTransactionOptionsTest.java
@@ -27,10 +27,15 @@
import com.google.cloud.spanner.connection.AbstractMockServerTest;
import com.google.cloud.spanner.connection.SpannerPool;
import com.google.spanner.v1.CommitRequest;
+import com.google.spanner.v1.ExecuteSqlRequest;
+import com.google.spanner.v1.TransactionOptions.IsolationLevel;
+import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Duration;
+import java.util.Arrays;
+import java.util.stream.Collectors;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -138,4 +143,63 @@ public void testMaxCommitDelay() throws SQLException {
assertEquals(Duration.ofMillis(50).toNanos(), request.getMaxCommitDelay().getNanos());
}
}
+
+ @Test
+ public void testDefaultIsolationLevel() throws SQLException {
+ for (IsolationLevel isolationLevel :
+ Arrays.stream(IsolationLevel.values())
+ .filter(level -> !level.equals(IsolationLevel.UNRECOGNIZED))
+ .collect(Collectors.toList())) {
+ try (java.sql.Connection connection =
+ DriverManager.getConnection(
+ "jdbc:" + getBaseUrl() + ";default_isolation_level=" + isolationLevel.name())) {
+ connection.setAutoCommit(false);
+ try (ResultSet resultSet =
+ connection.createStatement().executeQuery(SELECT1_STATEMENT.getSql())) {
+ while (resultSet.next()) {
+ // ignore
+ }
+ }
+ connection.commit();
+ assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
+ ExecuteSqlRequest request = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0);
+ assertTrue(request.hasTransaction());
+ assertTrue(request.getTransaction().hasBegin());
+ assertTrue(request.getTransaction().getBegin().hasReadWrite());
+ assertEquals(isolationLevel, request.getTransaction().getBegin().getIsolationLevel());
+ assertEquals(1, mockSpanner.countRequestsOfType(CommitRequest.class));
+
+ mockSpanner.clearRequests();
+ }
+ }
+ }
+
+ @Test
+ public void testSetIsolationLevel() throws SQLException {
+ try (java.sql.Connection connection = createJdbcConnection()) {
+ connection.setAutoCommit(false);
+ for (int isolationLevel :
+ new int[] {Connection.TRANSACTION_REPEATABLE_READ, Connection.TRANSACTION_SERIALIZABLE}) {
+ connection.setTransactionIsolation(isolationLevel);
+ try (ResultSet resultSet =
+ connection.createStatement().executeQuery(SELECT1_STATEMENT.getSql())) {
+ while (resultSet.next()) {
+ // ignore
+ }
+ }
+ connection.commit();
+ assertEquals(1, mockSpanner.countRequestsOfType(ExecuteSqlRequest.class));
+ ExecuteSqlRequest request = mockSpanner.getRequestsOfType(ExecuteSqlRequest.class).get(0);
+ assertTrue(request.hasTransaction());
+ assertTrue(request.getTransaction().hasBegin());
+ assertTrue(request.getTransaction().getBegin().hasReadWrite());
+ assertEquals(
+ IsolationLevelConverter.convertToSpanner(isolationLevel),
+ request.getTransaction().getBegin().getIsolationLevel());
+ assertEquals(1, mockSpanner.countRequestsOfType(CommitRequest.class));
+
+ mockSpanner.clearRequests();
+ }
+ }
+ }
}
diff --git a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcConnectTest.java b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcConnectTest.java
index b42e81e5e..92e590e16 100644
--- a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcConnectTest.java
+++ b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcConnectTest.java
@@ -20,6 +20,7 @@
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.GoogleCredentials;
+import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.spanner.Database;
import com.google.cloud.spanner.Dialect;
import com.google.cloud.spanner.ParallelIntegrationTest;
@@ -230,7 +231,8 @@ public void testConnectWithDataSourceWithConflictingValues() throws SQLException
public void testConnectWithOAuthToken() throws Exception {
GoogleCredentials credentials;
if (hasValidKeyFile()) {
- credentials = GoogleCredentials.fromStream(Files.newInputStream(Paths.get(getKeyFile())));
+ credentials =
+ ServiceAccountCredentials.fromStream(Files.newInputStream(Paths.get(getKeyFile())));
} else {
try {
credentials = GoogleCredentials.getApplicationDefault();
diff --git a/versions.txt b/versions.txt
index 4edb3f7a6..515f0d555 100644
--- a/versions.txt
+++ b/versions.txt
@@ -1,4 +1,4 @@
# Format:
# module:released-version:current-version
-google-cloud-spanner-jdbc:2.33.0:2.33.0
+google-cloud-spanner-jdbc:2.33.1:2.33.1