Skip to content

Commit 4bf2b8d

Browse files
schaudermp911de
authored andcommitted
DATAJDBC-257 - Adds DB2 support.
Db2Dialect added in order to support DB2. Added test configuration files. Adapted some tests to make them properly work with DB2 QueryAnnotationIntegrationTests converted into an Hsqldb only test since it is next to impossible to make it work across databases since it heavily depends on database and driver specifics. Removed license acceptance file from the repository in order to not accept a license in the name of someone forking the repository. For the CI build an appropriate file gets created on the fly. Original pull request: #213.
1 parent b0d37bd commit 4bf2b8d

File tree

39 files changed

+625
-74
lines changed

39 files changed

+625
-74
lines changed

Jenkinsfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pipeline {
2929
}
3030
options { timeout(time: 30, unit: 'MINUTES') }
3131
steps {
32+
sh './accept-third-party-license.sh'
3233
sh 'mkdir -p /tmp/jenkins-home'
3334
sh 'chown -R 1001:1001 .'
3435
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,all-dbs clean dependency:list test -Dsort -U -B'
@@ -55,6 +56,7 @@ pipeline {
5556
}
5657
options { timeout(time: 30, unit: 'MINUTES') }
5758
steps {
59+
sh './accept-third-party-license.sh'
5860
sh 'mkdir -p /tmp/jenkins-home'
5961
sh 'chown -R 1001:1001 .'
6062
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,all-dbs,java11 clean dependency:list test -Dsort -U -B'
@@ -73,7 +75,8 @@ pipeline {
7375
}
7476
options { timeout(time: 30, unit: 'MINUTES') }
7577
steps {
76-
sh 'mkdir -p /tmp/jenkins-home'
78+
sh './accept-third-party-license.sh'
79+
sh 'mkdir -p /tmp/jenkins-home'
7780
sh 'chown -R 1001:1001 .'
7881
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,all-dbs,java11 clean dependency:list test -Dsort -U -B'
7982
sh 'chown -R 1001:1001 .'

accept-third-party-license.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/sh
2+
3+
{
4+
echo "mcr.microsoft.com/mssql/server:2017-CU12"
5+
echo "ibmcom/db2:11.5.0.0a"
6+
} > spring-data-jdbc/src/test/resources/container-license-acceptance.txt

pom.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
<degraph-check.version>0.1.4</degraph-check.version>
2727

28+
<db2.version>11.5.0.0</db2.version>
2829
<h2.version>1.4.200</h2.version>
2930
<hsqldb.version>2.2.8</hsqldb.version>
3031
<mssql.version>7.0.0.jre8</mssql.version>
@@ -170,6 +171,24 @@
170171
</systemPropertyVariables>
171172
</configuration>
172173
</execution>
174+
<execution>
175+
<id>db2-test</id>
176+
<phase>test</phase>
177+
<goals>
178+
<goal>test</goal>
179+
</goals>
180+
<configuration>
181+
<includes>
182+
<include>**/*IntegrationTests.java</include>
183+
</includes>
184+
<excludes>
185+
<exclude>**/*HsqlIntegrationTests.java</exclude>
186+
</excludes>
187+
<systemPropertyVariables>
188+
<spring.profiles.active>db2</spring.profiles.active>
189+
</systemPropertyVariables>
190+
</configuration>
191+
</execution>
173192
<!--<execution>-->
174193
<!--<id>mssql-test</id>-->
175194
<!--<phase>test</phase>-->

spring-data-jdbc/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,12 @@
186186
<scope>test</scope>
187187
</dependency>
188188

189+
<dependency>
190+
<groupId>com.ibm.db2</groupId>
191+
<artifactId>jcc</artifactId>
192+
<version>11.1.4.4</version>
193+
</dependency>
194+
189195
<dependency>
190196
<groupId>de.schauderhaft.degraph</groupId>
191197
<artifactId>degraph-check</artifactId>
@@ -223,6 +229,12 @@
223229
<scope>test</scope>
224230
</dependency>
225231

232+
<dependency>
233+
<groupId>org.testcontainers</groupId>
234+
<artifactId>db2</artifactId>
235+
<scope>test</scope>
236+
</dependency>
237+
226238
</dependencies>
227239

228240
</project>

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/DialectResolver.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import org.springframework.core.io.support.SpringFactoriesLoader;
2828
import org.springframework.dao.NonTransientDataAccessException;
29+
import org.springframework.data.relational.core.dialect.Db2Dialect;
2930
import org.springframework.data.relational.core.dialect.Dialect;
3031
import org.springframework.data.relational.core.dialect.H2Dialect;
3132
import org.springframework.data.relational.core.dialect.HsqlDbDialect;
@@ -123,7 +124,9 @@ private static Dialect getDialect(Connection connection) throws SQLException {
123124
if (name.contains("microsoft")) {
124125
return SqlServerDialect.INSTANCE;
125126
}
126-
127+
if (name.contains("db2")) {
128+
return Db2Dialect.INSTANCE;
129+
}
127130
return null;
128131
}
129132

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ public void saveAndLoadAnEntityWithArray() {
514514
assumeNot("mysql");
515515
assumeNot("mariadb");
516516
assumeNot("mssql");
517+
assumeNot("db2");
517518

518519
ArrayOwner arrayOwner = new ArrayOwner();
519520
arrayOwner.digits = new String[] { "one", "two", "three" };
@@ -539,6 +540,7 @@ public void saveAndLoadAnEntityWithMultidimensionalArray() {
539540
assumeNot("mariadb");
540541
assumeNot("mssql");
541542
assumeNot("hsqldb");
543+
assumeNot("db2");
542544

543545
ArrayOwner arrayOwner = new ArrayOwner();
544546
arrayOwner.multidimensional = new String[][] { { "one-a", "two-a", "three-a" }, { "one-b", "two-b", "three-b" } };
@@ -563,6 +565,7 @@ public void saveAndLoadAnEntityWithList() {
563565
assumeNot("mysql");
564566
assumeNot("mariadb");
565567
assumeNot("mssql");
568+
assumeNot("db2");
566569

567570
ListOwner arrayOwner = new ListOwner();
568571
arrayOwner.digits.addAll(Arrays.asList("one", "two", "three"));
@@ -586,6 +589,7 @@ public void saveAndLoadAnEntityWithSet() {
586589
assumeNot("mysql");
587590
assumeNot("mariadb");
588591
assumeNot("mssql");
592+
assumeNot("db2");
589593

590594
SetOwner setOwner = new SetOwner();
591595
setOwner.digits.addAll(Arrays.asList("one", "two", "three"));
Lines changed: 17 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,16 @@
1919

2020
import lombok.Value;
2121

22-
import java.sql.Timestamp;
2322
import java.time.LocalDateTime;
2423
import java.util.Date;
2524
import java.util.List;
2625
import java.util.Optional;
2726
import java.util.stream.Stream;
2827

29-
import org.junit.Assume;
28+
import org.assertj.core.api.SoftAssertions;
3029
import org.junit.ClassRule;
3130
import org.junit.Rule;
3231
import org.junit.Test;
33-
3432
import org.springframework.beans.factory.annotation.Autowired;
3533
import org.springframework.context.annotation.Bean;
3634
import org.springframework.context.annotation.Configuration;
@@ -42,7 +40,7 @@
4240
import org.springframework.data.repository.CrudRepository;
4341
import org.springframework.data.repository.query.Param;
4442
import org.springframework.lang.Nullable;
45-
import org.springframework.test.annotation.ProfileValueUtils;
43+
import org.springframework.test.context.ActiveProfiles;
4644
import org.springframework.test.context.junit4.rules.SpringClassRule;
4745
import org.springframework.test.context.junit4.rules.SpringMethodRule;
4846
import org.springframework.transaction.annotation.Transactional;
@@ -55,7 +53,8 @@
5553
* @author Mark Paluch
5654
*/
5755
@Transactional
58-
public class QueryAnnotationIntegrationTests {
56+
@ActiveProfiles("hsql")
57+
public class QueryAnnotationHsqlIntegrationTests {
5958

6059
@Configuration
6160
@Import(TestConfiguration.class)
@@ -64,7 +63,7 @@ static class Config {
6463

6564
@Bean
6665
Class<?> testClass() {
67-
return QueryAnnotationIntegrationTests.class;
66+
return QueryAnnotationHsqlIntegrationTests.class;
6867
}
6968
}
7069

@@ -76,8 +75,6 @@ Class<?> testClass() {
7675
@Test // DATAJDBC-164
7776
public void executeCustomQueryWithoutParameter() {
7877

79-
assumeNot("mysql");
80-
8178
repository.save(dummyEntity("Example"));
8279
repository.save(dummyEntity("example"));
8380
repository.save(dummyEntity("EXAMPLE"));
@@ -180,8 +177,6 @@ public void executeCustomQueryWithReturnTypeIsStream() {
180177
@Test // DATAJDBC-175
181178
public void executeCustomQueryWithReturnTypeIsNumber() {
182179

183-
assumeNot("mysql");
184-
185180
repository.save(dummyEntity("aaa"));
186181
repository.save(dummyEntity("bbb"));
187182
repository.save(dummyEntity("cac"));
@@ -194,40 +189,29 @@ public void executeCustomQueryWithReturnTypeIsNumber() {
194189
@Test // DATAJDBC-175
195190
public void executeCustomQueryWithReturnTypeIsBoolean() {
196191

197-
assumeNot("mysql");
198-
199192
repository.save(dummyEntity("aaa"));
200193
repository.save(dummyEntity("bbb"));
201194
repository.save(dummyEntity("cac"));
202195

203-
assertThat(repository.existsByNameContaining("a")).isTrue();
204-
assertThat(repository.existsByNameContaining("d")).isFalse();
196+
SoftAssertions.assertSoftly(softly -> {
197+
198+
softly.assertThat(repository.existsByNameContaining("a")).describedAs("entities with A in the name").isTrue();
199+
softly.assertThat(repository.existsByNameContaining("d")).describedAs("entities with D in the name").isFalse();
200+
});
205201
}
206202

207203
@Test // DATAJDBC-175
208204
public void executeCustomQueryWithReturnTypeIsDate() {
209205

210-
assumeNot("mysql");
211-
212-
// Since Timestamp extends Date the repository returns the Timestamp as it comes from the database.
213-
// Trying to compare that to an actual Date results in non deterministic results, so we have to use an actual
214-
// Timestamp.
215-
Date now = new Timestamp(System.currentTimeMillis());
216-
assertThat(repository.nowWithDate()).isAfterOrEqualsTo(now);
217-
206+
assertThat(repository.nowWithDate()).isInstanceOf(Date.class);
218207
}
219208

220209
@Test // DATAJDBC-175
221210
public void executeCustomQueryWithReturnTypeIsLocalDateTimeList() {
222211

223-
// mysql does not support plain VALUES(…)
224-
assumeNot("mysql");
225-
226-
LocalDateTime preciseNow = LocalDateTime.now();
227-
LocalDateTime truncatedNow = truncateSubmillis(preciseNow);
228-
229-
repository.nowWithLocalDateTimeList() //
230-
.forEach(d -> assertThat(d).isAfterOrEqualTo(truncatedNow));
212+
assertThat(repository.nowWithLocalDateTimeList()) //
213+
.hasSize(2) //
214+
.allSatisfy(d -> assertThat(d).isInstanceOf(LocalDateTime.class));
231215
}
232216

233217
@Test // DATAJDBC-182
@@ -270,33 +254,16 @@ public void executeCustomModifyingQueryWithReturnTypeVoid() {
270254
@Test // DATAJDBC-175
271255
public void executeCustomQueryWithImmutableResultType() {
272256

273-
// mysql does not support plain VALUES(…)
274-
275-
assumeNot("mysql");
276-
277257
assertThat(repository.immutableTuple()).isEqualTo(new DummyEntityRepository.ImmutableTuple("one", "two", 3));
278258
}
279259

280-
private static LocalDateTime truncateSubmillis(LocalDateTime now) {
281-
282-
int NANOS_IN_MILLIS = 1_000_000;
283-
return now.withNano((now.getNano() / NANOS_IN_MILLIS) * 1_000_000);
284-
}
285-
286260
private DummyEntity dummyEntity(String name) {
287261

288262
DummyEntity entity = new DummyEntity();
289263
entity.name = name;
290264
return entity;
291265
}
292266

293-
private static void assumeNot(String dbProfileName) {
294-
295-
Assume.assumeTrue(
296-
"true".equalsIgnoreCase(ProfileValueUtils.retrieveProfileValueSource(QueryAnnotationIntegrationTests.class)
297-
.get("current.database.is.not." + dbProfileName)));
298-
}
299-
300267
private static class DummyEntity {
301268

302269
@Id Long id;
@@ -327,11 +294,11 @@ private interface DummyEntityRepository extends CrudRepository<DummyEntity, Long
327294
Stream<DummyEntity> findAllWithReturnTypeIsStream();
328295

329296
// DATAJDBC-175
330-
@Query("SELECT count(*) FROM DUMMY_ENTITY WHERE name like '%' || :name || '%'")
297+
@Query("SELECT count(*) FROM DUMMY_ENTITY WHERE name like concat('%', :name, '%')")
331298
int countByNameContaining(@Param("name") String name);
332299

333300
// DATAJDBC-175
334-
@Query("SELECT count(*) FROM DUMMY_ENTITY WHERE name like '%' || :name || '%'")
301+
@Query("SELECT case when count(*) > 0 THEN 'true' ELSE 'false' END FROM DUMMY_ENTITY WHERE name like '%' || :name || '%'")
335302
boolean existsByNameContaining(@Param("name") String name);
336303

337304
// DATAJDBC-175
@@ -358,7 +325,7 @@ private interface DummyEntityRepository extends CrudRepository<DummyEntity, Long
358325
void insert(@Param("name") String name);
359326

360327
// DATAJDBC-252
361-
@Query("SELECT 'one' one, 'two' two, 3 three FROM (VALUES (0))")
328+
@Query("SELECT 'one' one, 'two' two, 3 three FROM (VALUES (0)) as tableName")
362329
ImmutableTuple immutableTuple();
363330

364331
@Value
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright 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+
package org.springframework.data.jdbc.testing;
17+
18+
import javax.sql.DataSource;
19+
20+
import org.springframework.context.annotation.Configuration;
21+
import org.springframework.context.annotation.Profile;
22+
import org.springframework.jdbc.datasource.DriverManagerDataSource;
23+
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
24+
import org.testcontainers.containers.Db2Container;
25+
26+
/**
27+
* {@link DataSource} setup for DB2.
28+
*
29+
* @author Jens Schauder
30+
* @author Oliver Gierke
31+
*/
32+
@Configuration
33+
@Profile("db2")
34+
class Db2DataSourceConfiguration extends DataSourceConfiguration {
35+
36+
private static final Db2Container DB_2_CONTAINER = new Db2Container();
37+
38+
static {
39+
DB_2_CONTAINER.start();
40+
}
41+
42+
/*
43+
* (non-Javadoc)
44+
* @see org.springframework.data.jdbc.testing.DataSourceConfiguration#createDataSource()
45+
*/
46+
@Override
47+
protected DataSource createDataSource() {
48+
49+
DriverManagerDataSource dataSource = new DriverManagerDataSource(DB_2_CONTAINER.getJdbcUrl(),
50+
DB_2_CONTAINER.getUsername(), DB_2_CONTAINER.getPassword());
51+
52+
return dataSource;
53+
}
54+
55+
@Override
56+
protected void customizePopulator(ResourceDatabasePopulator populator) {
57+
populator.setIgnoreFailedDrops(true);
58+
}
59+
}

spring-data-jdbc/src/test/resources/container-license-acceptance.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)