Skip to content

Commit e4482a2

Browse files
authored
Added support of Spring Boot 4 to YDB Spring JDBC Dialect (#197)
2 parents b1d4171 + f7714ea commit e4482a2

File tree

13 files changed

+167
-38
lines changed

13 files changed

+167
-38
lines changed

.github/workflows/ci-spring-data-jdbc-ydb.yaml

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,44 @@ env:
1414
MAVEN_ARGS: --batch-mode --update-snapshots -Dstyle.color=always
1515

1616
jobs:
17+
prepare:
18+
name: Prepare Maven cache
19+
runs-on: ubuntu-24.04
20+
21+
env:
22+
MAVEN_ARGS: --batch-mode -Dstyle.color=always
23+
24+
steps:
25+
- name: Checkout
26+
uses: actions/checkout@v4
27+
28+
- name: Set up JDK
29+
uses: actions/setup-java@v4
30+
with:
31+
java-version: '8'
32+
distribution: 'temurin'
33+
cache: 'maven'
34+
35+
- name: Download dependencies (Default)
36+
working-directory: ./spring-data-jdbc-ydb
37+
run: mvn $MAVEN_ARGS dependency:resolve-plugins dependency:go-offline
38+
39+
- name: Download dependencies (Spring Boot 3)
40+
working-directory: ./spring-data-jdbc-ydb
41+
run: mvn $MAVEN_ARGS -Pspring-boot3 dependency:resolve-plugins dependency:go-offline
42+
43+
- name: Download dependencies (Spring Boot 4)
44+
working-directory: ./spring-data-jdbc-ydb
45+
run: mvn $MAVEN_ARGS -Pspring-boot4 dependency:resolve-plugins dependency:go-offline
46+
1747
build:
18-
name: Spring Data JDBC YDB Dialect
19-
runs-on: ubuntu-latest
48+
name: Spring Data JDBC YDB Dialect build & tests
49+
runs-on: ubuntu-24.04
50+
needs: prepare
2051

2152
strategy:
2253
matrix:
23-
java: [ '17', '21' ]
54+
java: [ '17', '21', '24' ]
2455

2556
steps:
2657
- uses: actions/checkout@v4
@@ -32,16 +63,39 @@ jobs:
3263
distribution: 'temurin'
3364
cache: maven
3465

66+
- name: Build spring-data-jdbc YDB dialect
67+
working-directory: ./spring-data-jdbc-ydb
68+
run: mvn $MAVEN_ARGS package
69+
70+
- name: Tests with Spring Boot 3
71+
working-directory: ./spring-data-jdbc-ydb
72+
run: mvn $MAVEN_ARGS -Pspring-boot3 test
73+
74+
- name: Tests with Spring Boot 4
75+
working-directory: ./spring-data-jdbc-ydb
76+
run: mvn $MAVEN_ARGS -Pspring-boot4 test
77+
78+
examples:
79+
name: Spring Data JDBC YDB Dialect Examples
80+
runs-on: ubuntu-24.04
81+
needs: build
82+
83+
steps:
84+
- uses: actions/checkout@v4
85+
86+
- name: Set up JDK
87+
uses: actions/setup-java@v4
88+
with:
89+
java-version: 17
90+
distribution: 'temurin'
91+
cache: maven
92+
3593
- name: Extract spring-data-jdbc YDB dialect version
3694
working-directory: ./spring-data-jdbc-ydb
3795
run: |
3896
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
3997
echo "SPRING_DATA_JDBC_DIALECT_VERSION=$VERSION" >> "$GITHUB_ENV"
4098
41-
- name: Download spring-data-jdbc YDB dialect dependencies
42-
working-directory: ./spring-data-jdbc-ydb
43-
run: mvn $MAVEN_ARGS dependency:go-offline
44-
4599
- name: Build spring-data-jdbc YDB dialect
46100
working-directory: ./spring-data-jdbc-ydb
47101
run: mvn $MAVEN_ARGS install

.github/workflows/publish-spring-data-jdbc-ydb.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ env:
1111
jobs:
1212
validate:
1313
name: Validate Spring Data JDBC YDB Dialect
14-
runs-on: ubuntu-latest
14+
runs-on: ubuntu-24.04
1515

1616
steps:
1717
- uses: actions/checkout@v4

spring-data-jdbc-ydb/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66

77
## Overview
88

9-
This project is an extension for Spring Data JDBC
9+
This project is an extension for Spring Data JDBC
1010
that provides support for working with [YDB](https://ydb.tech).
1111

1212
### Features
1313

14-
- Full support for basic operations with Spring Data JDBC
14+
- Full support for basic operations with Spring Data JDBC
1515
- Supported VIEW INDEX statement from @ViewIndex annotation on method your Repository
1616
- @YdbType explicitly specifies the YDB data type (Json example in String type)
1717

@@ -22,7 +22,7 @@ that provides support for working with [YDB](https://ydb.tech).
2222
To use this Spring Data JDBC YDB Dialect, you'll need:
2323

2424
- Java 17 or above.
25-
- Spring Data JDBC 3+
25+
- Spring Data JDBC 3.4+
2626
- [YDB JDBC Driver](https://github.com/ydb-platform/ydb-jdbc-driver)
2727
- Access to a YDB Database instance
2828

@@ -35,7 +35,7 @@ For Maven, add the following dependency to your pom.xml:
3535
<groupId>tech.ydb.dialects</groupId>
3636
<artifactId>spring-data-jdbc-ydb</artifactId>
3737
<!-- Set actual version -->
38-
<version>${spring.data.jdbc.ydb.version}</version>
38+
<version>${spring.data.jdbc.ydb.version}</version>
3939
</dependency>
4040
```
4141

spring-data-jdbc-ydb/pom.xml

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050
<maven.compiler.source>17</maven.compiler.source>
5151

5252
<junit5.version>5.10.2</junit5.version>
53-
<spring.version>3.4.0</spring.version>
5453
<liquibase.version>4.24.0</liquibase.version>
5554

5655
<ydb.sdk.version>2.3.13</ydb.sdk.version>
@@ -166,6 +165,34 @@
166165
</plugins>
167166
</build>
168167
<profiles>
168+
<profile>
169+
<id>spring-boot-minimal</id>
170+
<activation>
171+
<activeByDefault>true</activeByDefault>
172+
</activation>
173+
<properties>
174+
<spring.version>3.4.0</spring.version>
175+
</properties>
176+
</profile>
177+
<profile>
178+
<id>spring-boot3</id>
179+
<properties>
180+
<spring.version>3.5.7</spring.version>
181+
</properties>
182+
</profile>
183+
<profile>
184+
<id>spring-boot4</id>
185+
<properties>
186+
<spring.version>4.0.0</spring.version>
187+
</properties>
188+
<dependencies>
189+
<dependency>
190+
<groupId>org.springframework.boot</groupId>
191+
<artifactId>spring-boot-starter-liquibase</artifactId>
192+
<scope>test</scope>
193+
</dependency>
194+
</dependencies>
195+
</profile>
169196
<profile>
170197
<id>ossrh-s01</id>
171198
<activation>

spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/dialect/YdbDialect.java

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import java.util.function.Function;
55

66
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
7+
import org.springframework.data.jdbc.core.convert.JdbcArrayColumns;
8+
import org.springframework.data.jdbc.core.dialect.JdbcDialect;
79
import org.springframework.data.relational.core.dialect.AbstractDialect;
810
import org.springframework.data.relational.core.dialect.InsertRenderContext;
911
import org.springframework.data.relational.core.dialect.LimitClause;
@@ -19,9 +21,11 @@
1921
* @author Madiyar Nurgazin
2022
* @author Mikhail Polivakha
2123
*/
22-
public class YdbDialect extends AbstractDialect {
24+
public class YdbDialect extends AbstractDialect implements JdbcDialect {
2325
public static final YdbDialect INSTANCE = new YdbDialect();
2426

27+
private static final IdentifierProcessing.Quoting QUOTING = new IdentifierProcessing.Quoting("`");
28+
2529
private static final LimitClause LIMIT_CLAUSE = new LimitClause() {
2630

2731
@Override
@@ -46,10 +50,12 @@ public Position getClausePosition() {
4650
};
4751

4852
private static final LockClause LOCK_CLAUSE = new LockClause() {
53+
@Override
4954
public String getLock(LockOptions lockOptions) {
5055
throw new UnsupportedOperationException("YDB does not support pessimistic locks");
5156
}
5257

58+
@Override
5359
public LockClause.Position getClausePosition() {
5460
return null;
5561
}
@@ -99,10 +105,17 @@ public LockClause lock() {
99105

100106
@Override
101107
public IdentifierProcessing getIdentifierProcessing() {
102-
return IdentifierProcessing.create(
103-
new IdentifierProcessing.Quoting("`"),
104-
IdentifierProcessing.LetterCasing.AS_IS
105-
);
108+
return new IdentifierProcessing() {
109+
@Override
110+
public String quote(String identifier) {
111+
return QUOTING.apply(identifier);
112+
}
113+
114+
@Override
115+
public String standardizeLetterCase(String identifier) {
116+
return identifier;
117+
}
118+
};
106119
}
107120

108121
@Override
@@ -116,4 +129,10 @@ public InsertRenderContext getInsertRenderContext() {
116129
public OrderByNullPrecedence orderByNullHandling() {
117130
return OrderByNullPrecedence.NONE;
118131
}
132+
133+
@Override
134+
@SuppressWarnings("removal")
135+
public JdbcArrayColumns getArraySupport() {
136+
return JdbcArrayColumns.Unsupported.INSTANCE;
137+
}
119138
}

spring-data-jdbc-ydb/src/main/java/tech/ydb/data/repository/config/AbstractYdbJdbcConfiguration.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
import org.springframework.data.jdbc.core.convert.JdbcConverter;
99
import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
1010
import org.springframework.data.jdbc.core.convert.RelationResolver;
11+
import org.springframework.data.jdbc.core.dialect.JdbcDialect;
1112
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
1213
import org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration;
1314
import org.springframework.data.relational.core.dialect.Dialect;
1415
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
16+
1517
import tech.ydb.data.core.convert.YdbMappingJdbcConverter;
1618

1719
/**
@@ -22,7 +24,24 @@
2224
@Import(JdbcRepositoryBeanPostProcessor.class)
2325
public class AbstractYdbJdbcConfiguration extends AbstractJdbcConfiguration {
2426

25-
@Override
27+
// Spring Boot 4 support
28+
@SuppressWarnings({"override", "removal"})
29+
public JdbcConverter jdbcConverter(
30+
JdbcMappingContext mappingContext,
31+
NamedParameterJdbcOperations operations,
32+
@Lazy RelationResolver relationResolver,
33+
JdbcCustomConversions conversions,
34+
JdbcDialect dialect
35+
) {
36+
DefaultJdbcTypeFactory jdbcTypeFactory = new DefaultJdbcTypeFactory(
37+
operations.getJdbcOperations(), JdbcArrayColumns.Unsupported.INSTANCE
38+
);
39+
40+
return new YdbMappingJdbcConverter(mappingContext, relationResolver, conversions, jdbcTypeFactory);
41+
}
42+
43+
// Spring Boot 3 support
44+
@SuppressWarnings({"override", "removal"})
2645
public JdbcConverter jdbcConverter(
2746
JdbcMappingContext mappingContext,
2847
NamedParameterJdbcOperations operations,

spring-data-jdbc-ydb/src/main/java/tech/ydb/data/repository/config/YdbDialectProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@
99
import org.springframework.data.relational.core.dialect.Dialect;
1010
import org.springframework.jdbc.core.ConnectionCallback;
1111
import org.springframework.jdbc.core.JdbcOperations;
12-
import org.springframework.lang.Nullable;
12+
1313
import tech.ydb.data.core.dialect.YdbDialect;
1414

1515
/**
1616
* @author Madiyar Nurgazin
1717
*/
18+
@SuppressWarnings("removal") // Spring Boot 3 support
1819
public class YdbDialectProvider extends DialectResolver.DefaultDialectProvider {
1920
@Override
2021
public Optional<Dialect> getDialect(JdbcOperations operations) {
@@ -29,7 +30,6 @@ public Optional<Dialect> getDialect(JdbcOperations operations) {
2930
return super.getDialect(operations);
3031
}
3132

32-
@Nullable
3333
private static Dialect getDialect(Connection connection) throws SQLException {
3434
if ("ydb".contains(connection.getMetaData().getDatabaseProductName().toLowerCase(Locale.ENGLISH))) {
3535
return YdbDialect.INSTANCE;

spring-data-jdbc-ydb/src/test/java/tech/ydb/data/YdbBaseTest.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,24 @@
11
package tech.ydb.data;
22

33
import org.junit.jupiter.api.extension.RegisterExtension;
4-
import org.springframework.boot.test.autoconfigure.data.jdbc.AutoConfigureDataJdbc;
54
import org.springframework.boot.test.context.SpringBootTest;
65
import org.springframework.test.context.DynamicPropertyRegistry;
76
import org.springframework.test.context.DynamicPropertySource;
87
import org.springframework.transaction.annotation.Transactional;
8+
99
import tech.ydb.test.junit5.YdbHelperExtension;
1010

1111
/**
1212
* @author Madiyar Nurgazin
1313
*/
1414
@SpringBootTest(classes = YdbJdbcConfiguration.class)
15-
@AutoConfigureDataJdbc
1615
@Transactional
1716
public abstract class YdbBaseTest {
1817
@RegisterExtension
1918
private static final YdbHelperExtension ydb = new YdbHelperExtension();
2019

2120
@DynamicPropertySource
22-
private static void propertySource(DynamicPropertyRegistry registry) {
21+
public static void propertySource(DynamicPropertyRegistry registry) {
2322
registry.add("spring.datasource.url", YdbBaseTest::jdbcUrl);
2423
}
2524

spring-data-jdbc-ydb/src/test/java/tech/ydb/data/YdbJdbcConfiguration.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package tech.ydb.data;
22

3+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
34
import org.springframework.context.annotation.Configuration;
4-
import org.springframework.context.annotation.Import;
55
import org.springframework.data.jdbc.repository.config.EnableJdbcAuditing;
66
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;
7+
78
import tech.ydb.data.repository.config.AbstractYdbJdbcConfiguration;
89

910
/**
@@ -16,5 +17,5 @@
1617
basePackages = "tech.ydb.data"
1718
)
1819
@EnableJdbcAuditing
19-
@Import(AbstractYdbJdbcConfiguration.class)
20-
public class YdbJdbcConfiguration {}
20+
@EnableAutoConfiguration
21+
public class YdbJdbcConfiguration extends AbstractYdbJdbcConfiguration {}

spring-data-jdbc-ydb/src/test/java/tech/ydb/data/all_types_table/AllTypesTableTest.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55
import java.time.LocalDate;
66
import java.time.LocalDateTime;
77
import java.time.temporal.ChronoUnit;
8+
import java.util.ArrayList;
89
import java.util.List;
910
import java.util.Optional;
1011

1112
import org.junit.jupiter.api.Assertions;
1213
import org.junit.jupiter.api.Test;
1314
import org.springframework.beans.factory.annotation.Autowired;
1415
import org.springframework.data.jdbc.core.JdbcAggregateOperations;
15-
import org.springframework.data.relational.core.conversion.DbActionExecutionException;
16+
import org.springframework.jdbc.UncategorizedSQLException;
1617

1718
import tech.ydb.data.YdbBaseTest;
1819
import tech.ydb.data.all_types_table.entity.AllTypesEntity;
@@ -57,7 +58,8 @@ public void allTypesTableCrudTest() {
5758
aggregateOperations.insert(entity2);
5859
Assertions.assertEquals(2, repository.countDistinctTextColumn());
5960

60-
List<AllTypesEntity> entities = repository.findAll();
61+
List<AllTypesEntity> entities = new ArrayList<>();
62+
repository.findAll().forEach(entities::add);
6163
Assertions.assertEquals(4, entities.size());
6264

6365
repository.deleteById(1);
@@ -75,8 +77,12 @@ public void allTypesTableCrudTest() {
7577
Assertions.assertEquals(Integer.valueOf(4), entities.get(0).getId());
7678

7779
entity3.setJsonColumn("Not json");
78-
var ex = Assertions.assertThrows(DbActionExecutionException.class, () -> repository.save(entity3));
79-
Assertions.assertTrue(ex.getMessage().startsWith("Failed to execute DbAction.UpdateRoot"));
80+
Throwable ex = Assertions.assertThrows(Exception.class, () -> repository.save(entity3));
81+
if (ex != null && !(ex instanceof UncategorizedSQLException)) {
82+
ex = ex.getCause();
83+
}
84+
Assertions.assertTrue(ex instanceof UncategorizedSQLException);
85+
Assertions.assertTrue(ex != null && ex.getMessage().contains("Invalid Json value (S_ERROR)]"));
8086

8187
entity3.setJsonColumn("{\"values\": [1, 2, 3]}");
8288
AllTypesEntity updated = repository.save(entity3);

0 commit comments

Comments
 (0)