Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 61 additions & 7 deletions .github/workflows/ci-spring-data-jdbc-ydb.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,44 @@ env:
MAVEN_ARGS: --batch-mode --update-snapshots -Dstyle.color=always

jobs:
prepare:
name: Prepare Maven cache
runs-on: ubuntu-24.04

env:
MAVEN_ARGS: --batch-mode -Dstyle.color=always

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: '8'
distribution: 'temurin'
cache: 'maven'

- name: Download dependencies (Default)
working-directory: ./spring-data-jdbc-ydb
run: mvn $MAVEN_ARGS dependency:resolve-plugins dependency:go-offline

- name: Download dependencies (Spring Boot 3)
working-directory: ./spring-data-jdbc-ydb
run: mvn $MAVEN_ARGS -Pspring-boot3 dependency:resolve-plugins dependency:go-offline

- name: Download dependencies (Spring Boot 4)
working-directory: ./spring-data-jdbc-ydb
run: mvn $MAVEN_ARGS -Pspring-boot4 dependency:resolve-plugins dependency:go-offline

build:
name: Spring Data JDBC YDB Dialect
runs-on: ubuntu-latest
name: Spring Data JDBC YDB Dialect build & tests
runs-on: ubuntu-24.04
needs: prepare

strategy:
matrix:
java: [ '17', '21' ]
java: [ '17', '21', '24' ]

steps:
- uses: actions/checkout@v4
Expand All @@ -32,16 +63,39 @@ jobs:
distribution: 'temurin'
cache: maven

- name: Build spring-data-jdbc YDB dialect
working-directory: ./spring-data-jdbc-ydb
run: mvn $MAVEN_ARGS package

- name: Tests with Spring Boot 3
working-directory: ./spring-data-jdbc-ydb
run: mvn $MAVEN_ARGS -Pspring-boot3 test

- name: Tests with Spring Boot 4
working-directory: ./spring-data-jdbc-ydb
run: mvn $MAVEN_ARGS -Pspring-boot4 test

examples:
name: Spring Data JDBC YDB Dialect Examples
runs-on: ubuntu-24.04
needs: build

steps:
- uses: actions/checkout@v4

- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'temurin'
cache: maven

- name: Extract spring-data-jdbc YDB dialect version
working-directory: ./spring-data-jdbc-ydb
run: |
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
echo "SPRING_DATA_JDBC_DIALECT_VERSION=$VERSION" >> "$GITHUB_ENV"

- name: Download spring-data-jdbc YDB dialect dependencies
working-directory: ./spring-data-jdbc-ydb
run: mvn $MAVEN_ARGS dependency:go-offline

- name: Build spring-data-jdbc YDB dialect
working-directory: ./spring-data-jdbc-ydb
run: mvn $MAVEN_ARGS install
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-spring-data-jdbc-ydb.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ env:
jobs:
validate:
name: Validate Spring Data JDBC YDB Dialect
runs-on: ubuntu-latest
runs-on: ubuntu-24.04

steps:
- uses: actions/checkout@v4
Expand Down
8 changes: 4 additions & 4 deletions spring-data-jdbc-ydb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@

## Overview

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

### Features

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

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

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

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

Expand Down
29 changes: 28 additions & 1 deletion spring-data-jdbc-ydb/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
<maven.compiler.source>17</maven.compiler.source>

<junit5.version>5.10.2</junit5.version>
<spring.version>3.4.0</spring.version>
<liquibase.version>4.24.0</liquibase.version>

<ydb.sdk.version>2.3.13</ydb.sdk.version>
Expand Down Expand Up @@ -166,6 +165,34 @@
</plugins>
</build>
<profiles>
<profile>
<id>spring-boot-minimal</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<spring.version>3.4.0</spring.version>
</properties>
</profile>
<profile>
<id>spring-boot3</id>
<properties>
<spring.version>3.5.7</spring.version>
</properties>
</profile>
<profile>
<id>spring-boot4</id>
<properties>
<spring.version>4.0.0</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-liquibase</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
<profile>
<id>ossrh-s01</id>
<activation>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import java.util.function.Function;

import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.data.jdbc.core.convert.JdbcArrayColumns;
import org.springframework.data.jdbc.core.dialect.JdbcDialect;
import org.springframework.data.relational.core.dialect.AbstractDialect;
import org.springframework.data.relational.core.dialect.InsertRenderContext;
import org.springframework.data.relational.core.dialect.LimitClause;
Expand All @@ -19,9 +21,11 @@
* @author Madiyar Nurgazin
* @author Mikhail Polivakha
*/
public class YdbDialect extends AbstractDialect {
public class YdbDialect extends AbstractDialect implements JdbcDialect {
public static final YdbDialect INSTANCE = new YdbDialect();

private static final IdentifierProcessing.Quoting QUOTING = new IdentifierProcessing.Quoting("`");

private static final LimitClause LIMIT_CLAUSE = new LimitClause() {

@Override
Expand All @@ -46,10 +50,12 @@ public Position getClausePosition() {
};

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

@Override
public LockClause.Position getClausePosition() {
return null;
}
Expand Down Expand Up @@ -99,10 +105,17 @@ public LockClause lock() {

@Override
public IdentifierProcessing getIdentifierProcessing() {
return IdentifierProcessing.create(
new IdentifierProcessing.Quoting("`"),
IdentifierProcessing.LetterCasing.AS_IS
);
return new IdentifierProcessing() {
@Override
public String quote(String identifier) {
return QUOTING.apply(identifier);
}

@Override
public String standardizeLetterCase(String identifier) {
return identifier;
}
};
}

@Override
Expand All @@ -116,4 +129,10 @@ public InsertRenderContext getInsertRenderContext() {
public OrderByNullPrecedence orderByNullHandling() {
return OrderByNullPrecedence.NONE;
}

@Override
@SuppressWarnings("removal")
public JdbcArrayColumns getArraySupport() {
return JdbcArrayColumns.Unsupported.INSTANCE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
import org.springframework.data.jdbc.core.convert.RelationResolver;
import org.springframework.data.jdbc.core.dialect.JdbcDialect;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;

import tech.ydb.data.core.convert.YdbMappingJdbcConverter;

/**
Expand All @@ -22,7 +24,24 @@
@Import(JdbcRepositoryBeanPostProcessor.class)
public class AbstractYdbJdbcConfiguration extends AbstractJdbcConfiguration {

@Override
// Spring Boot 4 support
@SuppressWarnings({"override", "removal"})
public JdbcConverter jdbcConverter(
JdbcMappingContext mappingContext,
NamedParameterJdbcOperations operations,
@Lazy RelationResolver relationResolver,
JdbcCustomConversions conversions,
JdbcDialect dialect
) {
DefaultJdbcTypeFactory jdbcTypeFactory = new DefaultJdbcTypeFactory(
operations.getJdbcOperations(), JdbcArrayColumns.Unsupported.INSTANCE
);

return new YdbMappingJdbcConverter(mappingContext, relationResolver, conversions, jdbcTypeFactory);
}

// Spring Boot 3 support
@SuppressWarnings({"override", "removal"})
public JdbcConverter jdbcConverter(
JdbcMappingContext mappingContext,
NamedParameterJdbcOperations operations,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.lang.Nullable;

import tech.ydb.data.core.dialect.YdbDialect;

/**
* @author Madiyar Nurgazin
*/
@SuppressWarnings("removal") // Spring Boot 3 support
public class YdbDialectProvider extends DialectResolver.DefaultDialectProvider {
@Override
public Optional<Dialect> getDialect(JdbcOperations operations) {
Expand All @@ -29,7 +30,6 @@ public Optional<Dialect> getDialect(JdbcOperations operations) {
return super.getDialect(operations);
}

@Nullable
private static Dialect getDialect(Connection connection) throws SQLException {
if ("ydb".contains(connection.getMetaData().getDatabaseProductName().toLowerCase(Locale.ENGLISH))) {
return YdbDialect.INSTANCE;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
package tech.ydb.data;

import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.boot.test.autoconfigure.data.jdbc.AutoConfigureDataJdbc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.transaction.annotation.Transactional;

import tech.ydb.test.junit5.YdbHelperExtension;

/**
* @author Madiyar Nurgazin
*/
@SpringBootTest(classes = YdbJdbcConfiguration.class)
@AutoConfigureDataJdbc
@Transactional
public abstract class YdbBaseTest {
@RegisterExtension
private static final YdbHelperExtension ydb = new YdbHelperExtension();

@DynamicPropertySource
private static void propertySource(DynamicPropertyRegistry registry) {
public static void propertySource(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", YdbBaseTest::jdbcUrl);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package tech.ydb.data;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jdbc.repository.config.EnableJdbcAuditing;
import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories;

import tech.ydb.data.repository.config.AbstractYdbJdbcConfiguration;

/**
Expand All @@ -16,5 +17,5 @@
basePackages = "tech.ydb.data"
)
@EnableJdbcAuditing
@Import(AbstractYdbJdbcConfiguration.class)
public class YdbJdbcConfiguration {}
@EnableAutoConfiguration
public class YdbJdbcConfiguration extends AbstractYdbJdbcConfiguration {}
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jdbc.core.JdbcAggregateOperations;
import org.springframework.data.relational.core.conversion.DbActionExecutionException;
import org.springframework.jdbc.UncategorizedSQLException;

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

List<AllTypesEntity> entities = repository.findAll();
List<AllTypesEntity> entities = new ArrayList<>();
repository.findAll().forEach(entities::add);
Assertions.assertEquals(4, entities.size());

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

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

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