Skip to content
This repository was archived by the owner on Mar 26, 2026. It is now read-only.

Commit 2695484

Browse files
committed
Merge branch 'main' into PR #2191 to update
2 parents 1bf679d + ed74a8e commit 2695484

10 files changed

Lines changed: 172 additions & 42 deletions

File tree

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,30 @@
11
# Changelog
22

3+
## [2.35.1](https://github.com/googleapis/java-spanner-jdbc/compare/v2.35.0...v2.35.1) (2026-01-26)
4+
5+
6+
### Bug Fixes
7+
8+
* Accept Arrays of Integer, Short, Byte for Array<Int64> ([#2365](https://github.com/googleapis/java-spanner-jdbc/issues/2365)) ([7429508](https://github.com/googleapis/java-spanner-jdbc/commit/7429508b82b26a55b6d7910416f3c72cfa63af2e))
9+
10+
11+
### Performance Improvements
12+
13+
* Optimize JdbcDataSource#getConnection() ([#2371](https://github.com/googleapis/java-spanner-jdbc/issues/2371)) ([832064c](https://github.com/googleapis/java-spanner-jdbc/commit/832064ca2bca0d2d29e00c6b1417c7a4ce22f847))
14+
15+
16+
### Dependencies
17+
18+
* Update dependency com.fasterxml.jackson.core:jackson-databind to v2.21.0 ([#2364](https://github.com/googleapis/java-spanner-jdbc/issues/2364)) ([a4b5c21](https://github.com/googleapis/java-spanner-jdbc/commit/a4b5c21a449fca70bab45eb756addcba57f0b080))
19+
* Update dependency org.springframework.boot:spring-boot to v4.0.2 ([#2369](https://github.com/googleapis/java-spanner-jdbc/issues/2369)) ([6c2b28e](https://github.com/googleapis/java-spanner-jdbc/commit/6c2b28e966dbb4c12a8c877b7eac2935146eaeb5))
20+
* Update dependency org.springframework.boot:spring-boot-starter-data-jdbc to v4.0.2 ([#2370](https://github.com/googleapis/java-spanner-jdbc/issues/2370)) ([61714fb](https://github.com/googleapis/java-spanner-jdbc/commit/61714fb5537d3676e7942738a86f08ec0b6e2707))
21+
* Update dependency org.springframework.boot:spring-boot-starter-parent to v3.5.10 ([#2368](https://github.com/googleapis/java-spanner-jdbc/issues/2368)) ([db1755c](https://github.com/googleapis/java-spanner-jdbc/commit/db1755ca41b4c7fd0a232be94f66d5965677e46e))
22+
23+
24+
### Documentation
25+
26+
* Update connection properties docs with DCP ([#2372](https://github.com/googleapis/java-spanner-jdbc/issues/2372)) ([094ab5a](https://github.com/googleapis/java-spanner-jdbc/commit/094ab5a62a723cd895a6ea54259a3bbef98d56fc))
27+
328
## [2.35.0](https://github.com/googleapis/java-spanner-jdbc/compare/v2.34.1...v2.35.0) (2026-01-16)
429

530

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ If you are using Maven, add this to your pom.xml file:
2020
<dependency>
2121
<groupId>com.google.cloud</groupId>
2222
<artifactId>google-cloud-spanner-jdbc</artifactId>
23-
<version>2.35.0</version>
23+
<version>2.35.1</version>
2424
</dependency>
2525
```
2626
<!--- {x-version-update-end} -->
@@ -30,15 +30,15 @@ If you are using Gradle without BOM, add this to your dependencies
3030

3131
<!--- {x-version-update-start:google-cloud-spanner-jdbc:released} -->
3232
```Groovy
33-
implementation 'com.google.cloud:google-cloud-spanner-jdbc:2.35.0'
33+
implementation 'com.google.cloud:google-cloud-spanner-jdbc:2.35.1'
3434
```
3535
<!--- {x-version-update-end} -->
3636

3737
If you are using SBT, add this to your dependencies
3838

3939
<!--- {x-version-update-start:google-cloud-spanner-jdbc:released} -->
4040
```Scala
41-
libraryDependencies += "com.google.cloud" % "google-cloud-spanner-jdbc" % "2.35.0"
41+
libraryDependencies += "com.google.cloud" % "google-cloud-spanner-jdbc" % "2.35.1"
4242
```
4343
<!--- {x-version-update-end} -->
4444

documentation/connection_properties.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ The 'Context' value indicates whether the property can only be set when a connec
2222
| credentialsprovider | The class name of the com.google.api.gax.core.CredentialsProvider implementation that should be used to obtain credentials for connections. | | | STARTUP |
2323
| databaserole | Sets the database role to use for this connection. The default is privileges assigned to IAM role | | | STARTUP |
2424
| databoostenabled | Enable data boost for all partitioned queries that are executed by this connection. This setting is only used for partitioned queries and is ignored by all other statements. | false | true, false | USER |
25+
| dcpinitialchannels | The initial number of channels in the dynamic channel pool. Only used when enableDynamicChannelPool is true. The default is SpannerOptions.DEFAULT_DYNAMIC_POOL_INITIAL_SIZE (4). | | | STARTUP |
26+
| dcpmaxchannels | The maximum number of channels in the dynamic channel pool. Only used when enableDynamicChannelPool is true. The default is SpannerOptions.DEFAULT_DYNAMIC_POOL_MAX_CHANNELS (10). | | | STARTUP |
27+
| dcpminchannels | The minimum number of channels in the dynamic channel pool. Only used when enableDynamicChannelPool is true. The default is SpannerOptions.DEFAULT_DYNAMIC_POOL_MIN_CHANNELS (2). | | | STARTUP |
2528
| ddlintransactionmode | Determines how the connection should handle DDL statements in a read/write transaction. | ALLOW_IN_EMPTY_TRANSACTION | FAIL, ALLOW_IN_EMPTY_TRANSACTION, AUTO_COMMIT_TRANSACTION | USER |
2629
| default_isolation_level | The transaction isolation level that is used by default for read/write transactions. The default is isolation_level_unspecified, which means that the connection will use the default isolation level of the database that it is connected to. | ISOLATION_LEVEL_UNSPECIFIED | ISOLATION_LEVEL_UNSPECIFIED, SERIALIZABLE, REPEATABLE_READ | USER |
2730
| defaultsequencekind | The default sequence kind that should be used for the database. This property is only used when a DDL statement that requires a default sequence kind is executed on this connection. | | | USER |
@@ -30,6 +33,7 @@ The 'Context' value indicates whether the property can only be set when a connec
3033
| directed_read | The directed read options to apply to read-only transactions. | | | USER |
3134
| enableapitracing | Add OpenTelemetry traces for each individual RPC call. Enable this to get a detailed view of each RPC that is being executed by your application, or if you want to debug potential latency problems caused by RPCs that are being retried. | | true, false | STARTUP |
3235
| enabledirectaccess | Configure the connection to try to connect to Spanner using DirectPath (true/false). The client will try to connect to Spanner using a direct Google network connection. DirectPath will work only if the client is trying to establish a connection from a Google Cloud VM. Otherwise it will automatically fallback to the standard network path. NOTE: The default for this property is currently false, but this could be changed in the future. | | true, false | STARTUP |
36+
| enabledynamicchannelpool | Enable dynamic channel pooling for automatic gRPC channel scaling. When enabled, the client will automatically scale the number of channels based on load. Setting numChannels will disable dynamic channel pooling even if this is set to true. The default is currently false (disabled), but this may change to true in a future version. Set this property explicitly to ensure consistent behavior. | | true, false | STARTUP |
3337
| enableendtoendtracing | Enable end-to-end tracing (true/false) to generate traces for both the time that is spent in the client, as well as time that is spent in the Spanner server. Server side traces can only go to Google Cloud Trace, so to see end to end traces, the application should configure an exporter that exports the traces to Google Cloud Trace. | false | true, false | STARTUP |
3438
| enableextendedtracing | Include the SQL string in the OpenTelemetry traces that are generated by this connection. The SQL string is added as the standard OpenTelemetry attribute 'db.statement'. | | true, false | STARTUP |
3539
| encodedcredentials | Base64-encoded credentials to use for this connection. If neither this property or a credentials location are set, the connection will use the default Google Cloud credentials for the runtime environment. WARNING: Enabling this property without proper validation can expose the application to security risks. It is intended for use with credentials from a trusted source only, as it could otherwise allow end-users to supply arbitrary credentials. For more information, seehttps://cloud.google.com/docs/authentication/client-libraries#external-credentials | | | STARTUP |

pom.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
55
<modelVersion>4.0.0</modelVersion>
66
<artifactId>google-cloud-spanner-jdbc</artifactId>
7-
<version>2.35.1-SNAPSHOT</version><!-- {x-version-update:google-cloud-spanner-jdbc:current} -->
7+
<version>2.35.1</version><!-- {x-version-update:google-cloud-spanner-jdbc:current} -->
88
<packaging>jar</packaging>
99
<name>Google Cloud Spanner JDBC</name>
1010
<url>https://github.com/googleapis/java-spanner-jdbc</url>
@@ -260,6 +260,7 @@
260260
<spanner.testenv.instance>
261261
projects/gcloud-devel/instances/spanner-testing-east1
262262
</spanner.testenv.instance>
263+
<java.util.logging.config.file>logging.properties</java.util.logging.config.file>
263264
</systemPropertyVariables>
264265
</configuration>
265266
</plugin>
@@ -274,6 +275,7 @@
274275
<spanner.testenv.instance>
275276
projects/gcloud-devel/instances/spanner-testing-east1
276277
</spanner.testenv.instance>
278+
<java.util.logging.config.file>logging.properties</java.util.logging.config.file>
277279
</systemPropertyVariables>
278280
<forkedProcessTimeoutInSeconds>2400</forkedProcessTimeoutInSeconds>
279281
<forkCount>4</forkCount>

samples/snapshot/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
<dependency>
3030
<groupId>com.google.cloud</groupId>
3131
<artifactId>google-cloud-spanner-jdbc</artifactId>
32-
<version>2.35.1-SNAPSHOT</version>
32+
<version>2.35.1</version>
3333
</dependency>
3434
<!-- {x-version-update-end} -->
3535

src/main/java/com/google/cloud/spanner/jdbc/JdbcDataSource.java

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616

1717
package com.google.cloud.spanner.jdbc;
1818

19+
import static com.google.cloud.spanner.jdbc.JdbcDriver.appendPropertiesToUrl;
20+
import static com.google.cloud.spanner.jdbc.JdbcDriver.buildConnectionOptions;
21+
import static com.google.cloud.spanner.jdbc.JdbcDriver.maybeAddUserAgent;
22+
1923
import com.google.cloud.spanner.connection.ConnectionOptions;
2024
import com.google.rpc.Code;
2125
import java.io.PrintWriter;
2226
import java.sql.Connection;
23-
import java.sql.DriverManager;
2427
import java.sql.SQLException;
2528
import java.sql.SQLFeatureNotSupportedException;
2629
import java.util.Properties;
@@ -35,6 +38,8 @@ public class JdbcDataSource extends AbstractJdbcWrapper implements DataSource {
3538
private Boolean readonly;
3639
private Boolean retryAbortsInternally;
3740

41+
private volatile ConnectionOptions cachedConnectionOptions;
42+
3843
// Make sure the JDBC driver class is loaded.
3944
static {
4045
try {
@@ -76,12 +81,22 @@ public Connection getConnection() throws SQLException {
7681
throw JdbcSqlExceptionFactory.of(
7782
"There is no URL specified for this data source", Code.FAILED_PRECONDITION);
7883
}
79-
if (!JdbcDriver.getRegisteredDriver().acceptsURL(getUrl())) {
80-
throw JdbcSqlExceptionFactory.of(
81-
"The URL " + getUrl() + " is not valid for the data source " + getClass().getName(),
82-
Code.FAILED_PRECONDITION);
84+
if (cachedConnectionOptions == null) {
85+
synchronized (this) {
86+
if (cachedConnectionOptions == null) {
87+
if (!JdbcDriver.getRegisteredDriver().acceptsURL(getUrl())) {
88+
throw JdbcSqlExceptionFactory.of(
89+
"The URL " + getUrl() + " is not valid for the data source " + getClass().getName(),
90+
Code.FAILED_PRECONDITION);
91+
}
92+
Properties properties = createProperties();
93+
maybeAddUserAgent(properties);
94+
String connectionUri = appendPropertiesToUrl(url.substring(5), properties);
95+
cachedConnectionOptions = buildConnectionOptions(connectionUri, properties);
96+
}
97+
}
8398
}
84-
return DriverManager.getConnection(getUrl(), createProperties());
99+
return new JdbcConnection(getUrl(), cachedConnectionOptions);
85100
}
86101

87102
@Override
@@ -114,6 +129,12 @@ public boolean isClosed() {
114129
return false;
115130
}
116131

132+
private void clearCachedConnectionOptions() {
133+
synchronized (this) {
134+
cachedConnectionOptions = null;
135+
}
136+
}
137+
117138
/**
118139
* @return the JDBC URL to use for this {@link DataSource}.
119140
*/
@@ -125,6 +146,7 @@ public String getUrl() {
125146
* @param url The JDBC URL to use for this {@link DataSource}.
126147
*/
127148
public void setUrl(String url) {
149+
clearCachedConnectionOptions();
128150
this.url = url;
129151
}
130152

@@ -143,6 +165,7 @@ public String getCredentials() {
143165
* connection URL will be used.
144166
*/
145167
public void setCredentials(String credentials) {
168+
clearCachedConnectionOptions();
146169
this.credentials = credentials;
147170
}
148171

@@ -161,6 +184,7 @@ public Boolean getAutocommit() {
161184
* the connection URL will be used.
162185
*/
163186
public void setAutocommit(Boolean autocommit) {
187+
clearCachedConnectionOptions();
164188
this.autocommit = autocommit;
165189
}
166190

@@ -179,6 +203,7 @@ public Boolean getReadonly() {
179203
* URL will be used.
180204
*/
181205
public void setReadonly(Boolean readonly) {
206+
clearCachedConnectionOptions();
182207
this.readonly = readonly;
183208
}
184209

@@ -197,6 +222,7 @@ public Boolean getRetryAbortsInternally() {
197222
* this property, the value in the connection URL will be used.
198223
*/
199224
public void setRetryAbortsInternally(Boolean retryAbortsInternally) {
225+
clearCachedConnectionOptions();
200226
this.retryAbortsInternally = retryAbortsInternally;
201227
}
202228
}

src/main/java/com/google/cloud/spanner/jdbc/JdbcDriver.java

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import java.sql.SQLWarning;
3838
import java.util.Map.Entry;
3939
import java.util.Properties;
40+
import java.util.function.Supplier;
4041
import java.util.logging.Logger;
4142
import java.util.regex.Matcher;
4243
import java.util.regex.Pattern;
@@ -247,7 +248,7 @@ public Connection connect(String url, Properties info) throws SQLException {
247248
return null;
248249
}
249250

250-
private ConnectionOptions buildConnectionOptions(String connectionUrl, Properties info) {
251+
static ConnectionOptions buildConnectionOptions(String connectionUrl, Properties info) {
251252
ConnectionOptions.Builder builder =
252253
ConnectionOptions.newBuilder().setTracingPrefix("JDBC").setUri(connectionUrl);
253254
if (info.containsKey(OPEN_TELEMETRY_PROPERTY_KEY)
@@ -272,40 +273,42 @@ static void maybeAddUserAgent(Properties properties) {
272273
}
273274
}
274275

275-
static boolean isHibernate() {
276-
// Cache the result as the check is relatively expensive, and we also don't want to create
277-
// multiple different Spanner instances just to get the correct user-agent in every case.
278-
return Suppliers.memoize(
279-
() -> {
280-
try {
281-
// First check if the Spanner Hibernate dialect is on the classpath. If it is, then
282-
// we assume that Hibernate will (eventually) be used.
283-
Class.forName(
284-
"com.google.cloud.spanner.hibernate.SpannerDialect",
285-
/* initialize= */ false,
286-
JdbcDriver.class.getClassLoader());
287-
return true;
288-
} catch (Throwable ignore) {
289-
}
276+
private static final Supplier<Boolean> isHibernate =
277+
Suppliers.memoize(
278+
() -> {
279+
try {
280+
// First check if the Spanner Hibernate dialect is on the classpath. If it is, then
281+
// we assume that Hibernate will (eventually) be used.
282+
Class.forName(
283+
"com.google.cloud.spanner.hibernate.SpannerDialect",
284+
/* initialize= */ false,
285+
JdbcDriver.class.getClassLoader());
286+
return true;
287+
} catch (Throwable ignore) {
288+
}
290289

291-
// If we did not find the Spanner Hibernate dialect on the classpath, then do a
292-
// check if the connection is still being created by Hibernate using the built-in
293-
// Spanner dialect in Hibernate.
294-
try {
295-
StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
296-
for (StackTraceElement element : callStack) {
297-
if (element.getClassName().contains(".hibernate.")) {
298-
return true;
299-
}
290+
// If we did not find the Spanner Hibernate dialect on the classpath, then do a
291+
// check if the connection is still being created by Hibernate using the built-in
292+
// Spanner dialect in Hibernate.
293+
try {
294+
StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
295+
for (StackTraceElement element : callStack) {
296+
if (element.getClassName().contains(".hibernate.")) {
297+
return true;
300298
}
301-
} catch (Throwable ignore) {
302299
}
303-
return false;
304-
})
305-
.get();
300+
} catch (Throwable ignore) {
301+
}
302+
return false;
303+
});
304+
305+
static boolean isHibernate() {
306+
// Cache the result as the check is relatively expensive, and we also don't want to create
307+
// multiple different Spanner instances just to get the correct user-agent in every case.
308+
return isHibernate.get();
306309
}
307310

308-
private String appendPropertiesToUrl(String url, Properties info) {
311+
static String appendPropertiesToUrl(String url, Properties info) {
309312
StringBuilder res = new StringBuilder(url);
310313
for (Entry<Object, Object> entry : info.entrySet()) {
311314
if (entry.getValue() instanceof String && !"".equals(entry.getValue())) {
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.spanner.jdbc;
18+
19+
import static org.junit.Assert.assertEquals;
20+
21+
import com.google.cloud.spanner.connection.AbstractMockServerTest;
22+
import java.sql.Connection;
23+
import java.sql.SQLException;
24+
import org.junit.Test;
25+
import org.junit.runner.RunWith;
26+
import org.junit.runners.JUnit4;
27+
28+
@RunWith(JUnit4.class)
29+
public class JdbcDataSourceTest extends AbstractMockServerTest {
30+
31+
@Override
32+
protected String getBaseUrl() {
33+
return String.format(
34+
"jdbc:cloudspanner://localhost:%d/projects/p/instances/i/databases/d?usePlainText=true",
35+
getPort());
36+
}
37+
38+
@Test
39+
public void testGetConnectionFromNewDataSource() throws SQLException {
40+
for (boolean autoCommit : new boolean[] {true, false}) {
41+
JdbcDataSource dataSource = new JdbcDataSource();
42+
dataSource.setUrl(getBaseUrl());
43+
dataSource.setAutocommit(autoCommit);
44+
try (Connection connection = dataSource.getConnection()) {
45+
assertEquals(autoCommit, connection.getAutoCommit());
46+
}
47+
}
48+
}
49+
50+
@Test
51+
public void testGetConnectionFromCachedDataSource() throws SQLException {
52+
JdbcDataSource dataSource = new JdbcDataSource();
53+
dataSource.setUrl(getBaseUrl());
54+
for (boolean autoCommit : new boolean[] {true, false}) {
55+
// Changing a property on the DataSource should invalidate the internally cached
56+
// ConnectionOptions.
57+
dataSource.setAutocommit(autoCommit);
58+
try (Connection connection = dataSource.getConnection()) {
59+
assertEquals(autoCommit, connection.getAutoCommit());
60+
}
61+
}
62+
}
63+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.level=INFO
2+
.handlers=java.util.logging.ConsoleHandler
3+
java.util.logging.ConsoleHandler.level=INFO
4+
java.util.logging.Logger.useParentHandlers=true
5+
6+
# Set log level to WARN for SpannerImpl to prevent log spamming of the Spanner configuration.
7+
com.google.cloud.spanner.SpannerImpl.LEVEL=WARN

versions.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Format:
22
# module:released-version:current-version
33

4-
google-cloud-spanner-jdbc:2.35.0:2.35.1-SNAPSHOT
4+
google-cloud-spanner-jdbc:2.35.1:2.35.1

0 commit comments

Comments
 (0)