Skip to content

Commit 8eedd9d

Browse files
mdeinumjhoeller
authored andcommitted
Use JDBC 4 API for connection validation
With this commit use the JDBC 4.0 isValid method to validate the connection. This is favorable over a validation query.
1 parent eee0b76 commit 8eedd9d

File tree

2 files changed

+155
-8
lines changed

2 files changed

+155
-8
lines changed

spring-jdbc/src/main/java/org/springframework/jdbc/support/DatabaseStartupValidator.java

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,12 @@ public class DatabaseStartupValidator implements InitializingBean {
5959
@Nullable
6060
private DataSource dataSource;
6161

62+
/**
63+
* The query used to validate the connection
64+
* @deprecated in favor of JDBC 4.0 connection validation
65+
*/
6266
@Nullable
67+
@Deprecated
6368
private String validationQuery;
6469

6570
private int interval = DEFAULT_INTERVAL;
@@ -76,7 +81,10 @@ public void setDataSource(DataSource dataSource) {
7681

7782
/**
7883
* Set the SQL query string to use for validation.
84+
*
85+
* @deprecated in favor of the JDBC 4.0 connection validation
7986
*/
87+
@Deprecated
8088
public void setValidationQuery(String validationQuery) {
8189
this.validationQuery = validationQuery;
8290
}
@@ -108,9 +116,6 @@ public void afterPropertiesSet() {
108116
if (this.dataSource == null) {
109117
throw new IllegalArgumentException("Property 'dataSource' is required");
110118
}
111-
if (this.validationQuery == null) {
112-
throw new IllegalArgumentException("Property 'validationQuery' is required");
113-
}
114119

115120
try {
116121
boolean validated = false;
@@ -124,18 +129,29 @@ public void afterPropertiesSet() {
124129
try {
125130
con = this.dataSource.getConnection();
126131
if (con == null) {
127-
throw new CannotGetJdbcConnectionException("Failed to execute validation query: " +
132+
throw new CannotGetJdbcConnectionException("Failed to execute validation: " +
128133
"DataSource returned null from getConnection(): " + this.dataSource);
129134
}
130-
stmt = con.createStatement();
131-
stmt.execute(this.validationQuery);
132-
validated = true;
135+
if (this.validationQuery == null) {
136+
validated = con.isValid(this.interval);
137+
}
138+
else {
139+
stmt = con.createStatement();
140+
stmt.execute(this.validationQuery);
141+
validated = true;
142+
}
133143
}
134144
catch (SQLException ex) {
135145
latestEx = ex;
136146
if (logger.isDebugEnabled()) {
137-
logger.debug("Validation query [" + this.validationQuery + "] threw exception", ex);
147+
if (this.validationQuery != null) {
148+
logger.debug("Validation query [" + this.validationQuery + "] threw exception", ex);
149+
}
150+
else {
151+
logger.debug(" Validation threw exception", ex);
152+
}
138153
}
154+
139155
if (logger.isInfoEnabled()) {
140156
float rest = ((float) (deadLine - System.currentTimeMillis())) / 1000;
141157
if (rest > this.interval) {
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright 2003-2020 the original author or authors.
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+
* https://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 org.springframework.jdbc.support;
18+
19+
import java.sql.Connection;
20+
import java.sql.SQLException;
21+
import java.sql.Statement;
22+
23+
import javax.sql.DataSource;
24+
25+
import org.junit.jupiter.api.BeforeEach;
26+
import org.junit.jupiter.api.Test;
27+
28+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
29+
import static org.mockito.BDDMockito.given;
30+
import static org.mockito.Mockito.mock;
31+
import static org.mockito.Mockito.times;
32+
import static org.mockito.Mockito.verify;
33+
34+
/**
35+
* Mock object based test for {@code DatabaseStartupValidator}.
36+
*
37+
* @author Marten Deinum,
38+
*/
39+
class DatabaseStartupValidatorTests {
40+
41+
private Connection connection;
42+
private DataSource dataSource;
43+
44+
@BeforeEach
45+
public void setUp() throws Exception {
46+
connection = mock(Connection.class);
47+
dataSource = mock(DataSource.class);
48+
given(dataSource.getConnection()).willReturn(connection);
49+
}
50+
51+
@Test
52+
public void properSetupForDataSource() {
53+
DatabaseStartupValidator validator = new DatabaseStartupValidator();
54+
assertThatThrownBy(validator::afterPropertiesSet)
55+
.isInstanceOf(IllegalArgumentException.class);
56+
}
57+
58+
@Test
59+
public void shouldUseJdbc4IsValidByDefault() throws Exception {
60+
given(connection.isValid(1)).willReturn(true);
61+
DatabaseStartupValidator validator = new DatabaseStartupValidator();
62+
validator.setDataSource(dataSource);
63+
validator.afterPropertiesSet();
64+
65+
verify(connection, times(1)).isValid(1);
66+
verify(connection, times(1)).close();
67+
68+
}
69+
70+
@Test
71+
public void shouldCallValidatonTwiceWhenNotValid() throws Exception {
72+
given(connection.isValid(1)).willReturn(false, true);
73+
DatabaseStartupValidator validator = new DatabaseStartupValidator();
74+
validator.setDataSource(dataSource);
75+
validator.afterPropertiesSet();
76+
77+
verify(connection, times(2)).isValid(1);
78+
verify(connection, times(2)).close();
79+
80+
}
81+
82+
@Test
83+
public void shouldCallValidatonTwiceInCaseOfException() throws Exception {
84+
given(connection.isValid(1)).willThrow(new SQLException("Test")).willReturn(true);
85+
DatabaseStartupValidator validator = new DatabaseStartupValidator();
86+
validator.setDataSource(dataSource);
87+
validator.afterPropertiesSet();
88+
89+
verify(connection, times(2)).isValid(1);
90+
verify(connection, times(2)).close();
91+
92+
}
93+
94+
@Test
95+
public void useValidationQueryInsteadOfIsValid() throws Exception {
96+
String validationQuery = "SELECT NOW() FROM DUAL";
97+
Statement statement = mock(Statement.class);
98+
given(connection.createStatement()).willReturn(statement);
99+
given(statement.execute(validationQuery)).willReturn(true);
100+
101+
DatabaseStartupValidator validator = new DatabaseStartupValidator();
102+
validator.setDataSource(dataSource);
103+
validator.setValidationQuery(validationQuery);
104+
validator.afterPropertiesSet();
105+
106+
verify(connection, times(1)).createStatement();
107+
verify(statement, times(1)).execute(validationQuery);
108+
verify(connection, times(1)).close();
109+
verify(statement, times(1)).close();
110+
}
111+
112+
@Test
113+
public void shouldExecuteValidatonTwiceOnError() throws Exception {
114+
String validationQuery = "SELECT NOW() FROM DUAL";
115+
Statement statement = mock(Statement.class);
116+
given(connection.createStatement()).willReturn(statement);
117+
given(statement.execute(validationQuery))
118+
.willThrow(new SQLException("Test"))
119+
.willReturn(true);
120+
121+
DatabaseStartupValidator validator = new DatabaseStartupValidator();
122+
validator.setDataSource(dataSource);
123+
validator.setValidationQuery(validationQuery);
124+
validator.afterPropertiesSet();
125+
126+
verify(connection, times(2)).createStatement();
127+
verify(statement, times(2)).execute(validationQuery);
128+
verify(connection, times(2)).close();
129+
verify(statement, times(2)).close();
130+
}
131+
}

0 commit comments

Comments
 (0)