Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
18a90e7
Initial commit
Rinoyt Nov 20, 2025
a892eb0
added the pom file
Rinoyt Dec 8, 2025
16df237
v0.1 for the YDB dialect
Rinoyt Dec 8, 2025
5346933
dialect name change
Rinoyt Feb 3, 2026
6e9da30
Update .gitignore
Rinoyt Feb 3, 2026
48013f7
Update pom.xml
Rinoyt Feb 3, 2026
c0c0d54
models for tests
Rinoyt Feb 3, 2026
006155b
Classes to monitor sql requests for tests
Rinoyt Feb 3, 2026
eaf4f10
Initial integration test (pre-alpha)
Rinoyt Feb 3, 2026
88d30f9
Files to start the docker image of DB and stop it
Rinoyt Feb 3, 2026
495a77f
sql file for creating tables for models
Rinoyt Feb 3, 2026
9658e1d
fixed test initialization
Rinoyt Feb 4, 2026
0c99f2d
fixed the select test (pre-alpha)
Rinoyt Feb 4, 2026
268cd68
Added table/column names to the Group model in tests
Rinoyt Feb 4, 2026
18d732f
simple "select" test has been implemented
Rinoyt Feb 6, 2026
a0a3c31
Changed Java version from 25 to 17
Rinoyt Feb 7, 2026
5fe2f13
Transaction cache table is now supported for YDB
Rinoyt Feb 7, 2026
61c4753
Made transCacheOperatorTableDDL() look a bit nicer
Rinoyt Feb 7, 2026
2df198c
Moved classes to a different package
Rinoyt Feb 7, 2026
a08ace7
renamed a package
Rinoyt Feb 8, 2026
7ee5bd1
Added tests for YDB data types
Rinoyt Feb 10, 2026
d3094f9
added YDB JDBC type constants
Rinoyt Feb 12, 2026
db8d0fe
Removed Timestamp from tests
Rinoyt Feb 12, 2026
01a0685
Fixed sqlType()
Rinoyt Feb 12, 2026
7bd0a93
Reworked sqlType() in YdbDialect and added resolveJdbcType()
Rinoyt Feb 12, 2026
55da70c
Fixed Interval64
Rinoyt Feb 12, 2026
5f6390e
Added more tests for data types
Rinoyt Feb 12, 2026
c1ffb69
Added Json test and fixed Json in YdbDialect
Rinoyt Feb 12, 2026
bf44f1b
Moved Json model to another package
Rinoyt Feb 12, 2026
d5d9491
Added a Enum test
Rinoyt Feb 12, 2026
9397554
Removed support for arrays
Rinoyt Feb 12, 2026
48c456e
Added batchUpdate and bulkUpsert
Rinoyt Feb 12, 2026
bd53d42
Added YqlClentBuilder
Rinoyt Feb 13, 2026
ba2c097
Added a class to manage retries during transactions
Rinoyt Feb 13, 2026
617cc75
Added "insert into" tests
Rinoyt Feb 13, 2026
b31d93d
Fixed the error message about slf4j
Rinoyt Feb 16, 2026
3034bc2
Changed the structure of my ScalarProviders
Rinoyt Feb 16, 2026
828980c
Extracted adding ScalarProviders to a new function
Rinoyt Feb 16, 2026
8cd7ac2
Amend
Rinoyt Feb 17, 2026
23acf02
Revert "Amend"
Rinoyt Feb 27, 2026
c28a608
fixed sql checks in tests
Rinoyt Mar 15, 2026
3cf0e2e
merged old repo into a shared new one; conflicts resolved
Rinoyt Mar 15, 2026
085056b
renamed the directory for the jimmer dialect
Rinoyt Mar 15, 2026
d533c08
fixed upsert SQL in dialect
Rinoyt Mar 16, 2026
4c031b4
Added tests for batch operations
Rinoyt Mar 17, 2026
2e5fa29
Added a test for Streaming Result Sets
Rinoyt Mar 25, 2026
e9ab0fc
Added a template for setting isolation levels for transactions
Rinoyt Apr 3, 2026
167cd86
Added YDB supported Isolation levels
Rinoyt Apr 8, 2026
af0e0bb
ReadOnly parameter is now set together with the isolation level
Rinoyt Apr 9, 2026
7f34971
Made abstract AbstractInsertTest
Rinoyt Apr 23, 2026
db5041b
Name change
Rinoyt Apr 23, 2026
3f5a61c
Tests: added the function to drop the chosen table in SQL
Rinoyt Apr 23, 2026
8a24874
Tests: added the error message checker from SQL insert queries
Rinoyt Apr 23, 2026
08b7faa
Abstract test class for transactions
Rinoyt Apr 23, 2026
f911c32
Added tests for transactions
Rinoyt Apr 23, 2026
38aae75
Added a class for keyset pagination
Rinoyt Apr 26, 2026
135e09c
Removed unnecessary parameter from buildJsonResponse()
Rinoyt Apr 27, 2026
6e628d9
Added a simple test for the keyset pagination
Rinoyt Apr 27, 2026
3201907
Fixed select test for types
Rinoyt Apr 27, 2026
0906b61
Removed unnecessary code
Rinoyt Apr 27, 2026
8b53b91
Removed unused models from tests
Rinoyt Apr 27, 2026
6a4ebe7
Extracted repeated code to the common class in tests
Rinoyt Apr 27, 2026
96b357b
Revert "Removed unused models from tests"
Rinoyt Apr 27, 2026
7c3cb45
Renamed the simple entity from "Table" to "Entity"
Rinoyt Apr 27, 2026
29fd907
fixed BatchTest
Rinoyt Apr 27, 2026
e30968a
removed boilerplate
Rinoyt Apr 27, 2026
4c19948
Removed boilerplate
Rinoyt Apr 28, 2026
fd7c0a0
Removed space in the function arguments
Rinoyt Apr 28, 2026
0decbca
In Insert tests the crreated SQL table is dropped at the end
Rinoyt Apr 28, 2026
1e8fcbb
Extracted the test for the json insert into a new class
Rinoyt Apr 29, 2026
dbeed53
removed unnecessary imports
Rinoyt Apr 29, 2026
9945da4
Extracted all data types that are changed by Jimmer before reaching t…
Rinoyt Apr 29, 2026
9cf674e
Added a javadoc comment about the handleDateTime
Rinoyt Apr 29, 2026
a67f8d9
Removed boilerplate and unnecessary code
Rinoyt Apr 29, 2026
d57b578
Renamed the custom SqlClient
Rinoyt Apr 30, 2026
cb55a99
Added a simple README
Rinoyt Apr 30, 2026
3f26a0e
Fixed the return value for the YqlClientBuilder
Rinoyt Apr 30, 2026
83dc516
Added a simple left join test
Rinoyt May 1, 2026
9db6a08
Merge branch 'ydb-platform:main' into main
Rinoyt May 1, 2026
7b776a4
Added more join types to the simple join test
Rinoyt May 1, 2026
48be5b1
Integrated transaction cache class into the client builder
Rinoyt May 4, 2026
8434d6b
Added javadoc comments
Rinoyt May 4, 2026
827cf67
Extracted types used in handleDateTime in select test to a new test
Rinoyt May 4, 2026
ee19b1a
Added a new Executor to fix the return for UPDATE
Rinoyt May 10, 2026
1433ac0
Added a test for optimistic locking using versioning
Rinoyt May 11, 2026
1589b13
Added the new executor to the YqlClientBuilder
Rinoyt May 11, 2026
38e6d0d
Removed "--restart=always and --rm" conflict
Rinoyt May 11, 2026
2c30093
Added .setDialect(new YdbDialect()) to getIsolationClient() and fixed…
Rinoyt May 12, 2026
e8c61e3
Made the maps in YdbClassMapping unmodifiable
Rinoyt May 12, 2026
358b75d
changed "compile" to "test" for liquibase-core in XML
Rinoyt May 12, 2026
7a87811
delete calls are now separated into groups by batchSize
Rinoyt May 12, 2026
b3fbcc4
Fixed the problem where rollback() was useless in tests
Rinoyt May 13, 2026
d9d8208
Combined propagation, isolation level and retries into 1 ConnectionMa…
Rinoyt May 15, 2026
8911923
Created a method in the Client class for the user to access retry fun…
Rinoyt May 15, 2026
f658df2
Merge pull request #1 from Rinoyt/dev
Rinoyt May 15, 2026
c092fbb
Added exponential wait to the retry logic for transactions
Rinoyt May 17, 2026
c2b6ba7
extracted a constant for simplicity
Rinoyt May 18, 2026
bdd4ace
Fixed retires for transactions and cutdown on imports
Rinoyt May 18, 2026
363670e
Added chaos tests for transaction retries
Rinoyt May 18, 2026
3e2bc16
Merge pull request #2 from Rinoyt/dev
Rinoyt May 18, 2026
1b4ab64
Added micro benchmarking tests
Rinoyt May 20, 2026
d60d31d
Fixed the join benchmarking test
Rinoyt May 20, 2026
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
34 changes: 34 additions & 0 deletions jimmer-dialect/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# YDB Dialect for Jimmer

## Overview

This project contains a custom Jimmer dialect
for a simple integration between Jimmer ORM and Yandex Database (YDB).
For more thorough integration it is recommended to use the YqlClientBuilder class
for a custom JSqlClient.


### Features

- Custom type mappings to utilize YDB's data types.
- Support for YDB-specific features and functions.
- Transaction modes and isolation levels.
- YDB keyset pagination.

## Getting Started

### Requirements

To use this Hibernate YDB Dialect, you'll need:

- Java 17 or above.
- Jimmer version 0.9.117
- [YDB JDBC Driver](https://github.com/ydb-platform/ydb-jdbc-driver)
- Access to a YDB Database instance

## Usage

```java
DataSource dataSource = new DriverManagerDataSource("jdbc:ydb:grpc://localhost:2136/local");
YqlClient yqlClient = getYqlClient(dataSource);
```
111 changes: 111 additions & 0 deletions jimmer-dialect/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>jimmer.ydb</groupId>
<artifactId>jimmer-ydb-dialect</artifactId>
<version>1.0-SNAPSHOT</version>
<name>YDB dialect for Jimmer ORM</name>

<properties>
<jimmer.version>0.9.117</jimmer.version>
<spring.version>7.0.2</spring.version>
<jmh.version>1.37</jmh.version>
<maven.compiler.release>17</maven.compiler.release>
</properties>

<dependencies>
<!-- https://mvnrepository.com/artifact/org.babyfish.jimmer/jimmer-sql -->
<dependency>
<groupId>org.babyfish.jimmer</groupId>
<artifactId>jimmer-sql</artifactId>
<version>${jimmer.version}</version>
</dependency>
<!-- Source: https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.21.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.11.4</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/tech.ydb.jdbc/ydb-jdbc-driver -->
<dependency>
<groupId>tech.ydb.jdbc</groupId>
<artifactId>ydb-jdbc-driver</artifactId>
<version>2.3.20</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<!-- Source: https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.17</version>
<scope>compile</scope>
</dependency>
<!-- Source: https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j2-impl -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.25.3</version>
<scope>test</scope>
</dependency>
<!-- Source: https://mvnrepository.com/artifact/tech.ydb.test/ydb-junit5-support -->
<dependency>
<groupId>tech.ydb.test</groupId>
<artifactId>ydb-junit5-support</artifactId>
<version>2.3.29</version>
<scope>test</scope>
</dependency>
<!-- Source: https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core -->
<dependency>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Severity: Minor
Confidence: Medium

liquibase-core is declared at compile scope but is not imported or used anywhere in the main source code (src/main/java). This adds a significant transitive dependency tree (~3 MB+) to the artifact for no functional benefit.

If Liquibase is intended for future use, consider changing the scope to provided or test. If it's not needed, remove it.

<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
<scope>test</scope>
</dependency>
<!-- Source: https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-generator-annprocess -->
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.babyfish.jimmer</groupId>
<artifactId>jimmer-apt</artifactId>
<version>${jimmer.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package ydb.jimmer.dialect;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.babyfish.jimmer.jackson.ImmutableModule;
import org.babyfish.jimmer.meta.ImmutableProp;
import org.babyfish.jimmer.meta.ImmutableType;
import org.babyfish.jimmer.sql.cache.TransactionCacheOperator;
import org.babyfish.jimmer.sql.cache.UsedCache;
import org.babyfish.jimmer.sql.exception.ExecutionException;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Collections;
import java.util.UUID;

/**
* Uses UUID for the ids in the {@link #TABLE_NAME} SQL table.
*/
public class UuidTransactionCacheOperator extends TransactionCacheOperator {
public static final String TABLE_NAME = "JIMMER_TRANS_CACHE_OPERATOR";

private static final String ID = "ID";

private static final String IMMUTABLE_TYPE = "IMMUTABLE_TYPE";

private static final String IMMUTABLE_PROP = "IMMUTABLE_PROP";

private static final String CACHE_KEY = "CACHE_KEY";

private static final String REASON = "REASON";

private static final String INSERT_WITH_UUID =
"insert into " +
TABLE_NAME + "(" +
ID +
", " +
IMMUTABLE_TYPE +
", " +
IMMUTABLE_PROP +
", " +
CACHE_KEY +
", " +
REASON +
") values(?, ?, ?, ?, ?)";

private final ObjectMapper mapper;

private final int batchSize;

public UuidTransactionCacheOperator() {
this(null, 32);
}

public UuidTransactionCacheOperator(int batchSize) {
this(null, batchSize);
}

public UuidTransactionCacheOperator(ObjectMapper mapper) {
this(mapper, 32);
}

public UuidTransactionCacheOperator(ObjectMapper mapper, int batchSize) {
super(mapper, batchSize);

if (batchSize < 1) {
throw new IllegalArgumentException("`batchSize` cannot be less than 1");
}
this.mapper = mapper != null ?
mapper :
new ObjectMapper()
.registerModule(new JavaTimeModule())
.registerModule(new ImmutableModule());
this.batchSize = batchSize;
}

@Override
public void delete(UsedCache<Object, ?> cache, Object key, Object reason) {
if (reason != null && !(reason instanceof String)) {
throw new IllegalArgumentException(
"The cache deletion reason can only be null or string when trigger type is `TRANSACTION_ONLY`"
);
}
save(cache.type(), cache.prop(), Collections.singleton(key), (String) reason);
}

@Override
public void deleteAll(UsedCache<Object, ?> cache, Collection<Object> keys, Object reason) {
if (keys.isEmpty()) {
return;
}
if (reason != null && !(reason instanceof String)) {
throw new IllegalArgumentException(
"The cache deletion reason can only be null or string when trigger type is `TRANSACTION_ONLY`"
);
}
save(cache.type(), cache.prop(), keys, (String) reason);
}

private void save(
ImmutableType type,
ImmutableProp prop,
Collection<Object> keys,
String reason
) {
sqlClient().getConnectionManager().execute(con -> {
try {
try (PreparedStatement stmt = con.prepareStatement(INSERT_WITH_UUID)) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Severity: Major
Confidence: Medium

The batchSize parameter is validated in the constructor (batchSize < 1 throws) and passed to the superclass, but the save() method here batches all keys in a single executeBatch() call regardless of batchSize. Since both delete() and deleteAll() are overridden, the superclass's chunking logic (which would split work into batchSize-sized batches) is bypassed.

For a large collection of cache keys, this could cause memory pressure or exceed YDB's batch statement limits.

Suggested fix: chunk the keys collection into batchSize-sized groups and call executeBatch() for each chunk:

int count = 0;
for (Object key : keys) {
    // ... setString calls ...
    stmt.addBatch();
    if (++count % batchSize == 0) {
        stmt.executeBatch();
    }
}
if (count % batchSize != 0) {
    stmt.executeBatch();
}

(where batchSize needs to be stored as an instance field)

int count = 0;
for (Object key : keys) {
stmt.setString(1, UUID.randomUUID().toString());
stmt.setString(2, type != null ? type.toString() : null);
stmt.setString(3, prop != null ? prop.toString() : null);
stmt.setString(4, mapper.writeValueAsString(key));
stmt.setString(5, reason);
stmt.addBatch();

if (++count % batchSize == 0) {
stmt.executeBatch();
}
}

if (count % batchSize != 0) {
stmt.executeBatch();
}
}
} catch (SQLException | JsonProcessingException ex) {
throw new ExecutionException("Failed to save delayed cache deletion", ex);
}
return null;
});
}
}
Loading