Skip to content

Commit afb6b20

Browse files
init commit
1 parent 456abee commit afb6b20

File tree

8 files changed

+371
-5
lines changed

8 files changed

+371
-5
lines changed

hibernate-dialect/pom.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
<junit5.version>5.9.3</junit5.version>
4242
<log4j2.version>2.17.2</log4j2.version>
4343

44-
<ydb.sdk.version>2.2.6</ydb.sdk.version>
45-
<ydb.jdbc.version>2.2.4</ydb.jdbc.version>
44+
<ydb.sdk.version>2.3.4</ydb.sdk.version>
45+
<ydb.jdbc.version>2.3.2</ydb.jdbc.version>
4646
</properties>
4747

4848
<licenses>
@@ -145,6 +145,8 @@
145145
<configuration>
146146
<environmentVariables>
147147
<TESTCONTAINERS_REUSE_ENABLE>true</TESTCONTAINERS_REUSE_ENABLE>
148+
<YDB_DOCKER_FEATURE_FLAGS>enable_parameterized_decimal</YDB_DOCKER_FEATURE_FLAGS>
149+
<YDB_DOCKER_IMAGE>cr.yandex/yc/yandex-docker-local-ydb:trunk</YDB_DOCKER_IMAGE>
148150
</environmentVariables>
149151
</configuration>
150152
</plugin>

hibernate-dialect/src/main/java/tech/ydb/hibernate/dialect/YdbDialect.java

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.time.LocalDateTime;
44
import java.util.List;
5+
import java.util.concurrent.ConcurrentHashMap;
56
import org.hibernate.boot.model.FunctionContributions;
67
import org.hibernate.boot.model.TypeContributions;
78
import org.hibernate.dialect.Dialect;
@@ -51,33 +52,40 @@
5152
import static org.hibernate.type.SqlTypes.VARBINARY;
5253
import static org.hibernate.type.SqlTypes.VARCHAR;
5354
import org.hibernate.type.StandardBasicTypes;
55+
import org.hibernate.type.descriptor.jdbc.JdbcType;
56+
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
5457
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
5558
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
59+
import tech.ydb.hibernate.dialect.code.YdbJdbcCode;
5660
import tech.ydb.hibernate.dialect.exporter.EmptyExporter;
5761
import tech.ydb.hibernate.dialect.exporter.YdbIndexExporter;
5862
import tech.ydb.hibernate.dialect.hint.IndexQueryHintHandler;
5963
import tech.ydb.hibernate.dialect.hint.QueryHintHandler;
6064
import tech.ydb.hibernate.dialect.hint.ScanQueryHintHandler;
6165
import tech.ydb.hibernate.dialect.translator.YdbSqlAstTranslatorFactory;
66+
import tech.ydb.hibernate.dialect.types.BigDecimalJavaType;
67+
import tech.ydb.hibernate.dialect.types.DecimalJdbcType;
6268
import tech.ydb.hibernate.dialect.types.InstantJavaType;
6369
import tech.ydb.hibernate.dialect.types.InstantJdbcType;
6470
import tech.ydb.hibernate.dialect.types.LocalDateJavaType;
6571
import tech.ydb.hibernate.dialect.types.LocalDateJdbcType;
6672
import tech.ydb.hibernate.dialect.types.LocalDateTimeJavaType;
6773
import tech.ydb.hibernate.dialect.types.LocalDateTimeJdbcType;
6874
import static tech.ydb.hibernate.dialect.types.LocalDateTimeJdbcType.JDBC_TYPE_DATETIME_CODE;
75+
import tech.ydb.hibernate.dialect.types.Uint8JdbcType;
6976

7077
/**
7178
* @author Kirill Kurdyukov
7279
*/
7380
public class YdbDialect extends Dialect {
74-
81+
private static final int SHIFT_DECIMAL_CODE = 11000;
7582
private static final Exporter<ForeignKey> FOREIGN_KEY_EMPTY_EXPORTER = new EmptyExporter<>();
7683
private static final Exporter<Constraint> UNIQUE_KEY_EMPTY_EXPORTER = new EmptyExporter<>();
7784
private static final List<QueryHintHandler> QUERY_HINT_HANDLERS = List.of(
7885
IndexQueryHintHandler.INSTANCE,
7986
ScanQueryHintHandler.INSTANCE
8087
);
88+
private static final ConcurrentHashMap<Integer, DecimalJdbcType> DECIMAL_JDBC_TYPE_CACHE = new ConcurrentHashMap<>();
8189

8290
public YdbDialect(DialectResolutionInfo dialectResolutionInfo) {
8391
super(dialectResolutionInfo);
@@ -93,7 +101,7 @@ protected String columnType(int sqlTypeCode) {
93101
case BIGINT -> "Int64";
94102
case REAL, FLOAT -> "Float";
95103
case DOUBLE -> "Double";
96-
case NUMERIC, DECIMAL -> "Decimal (22,9)"; // Fixed
104+
case NUMERIC, DECIMAL -> "Decimal($p, $s)";
97105
case DATE -> "Date";
98106
case JDBC_TYPE_DATETIME_CODE -> "Datetime";
99107
case TIME_WITH_TIMEZONE -> "TzDateTime";
@@ -117,6 +125,11 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
117125
typeContributions.contributeJdbcType(LocalDateJdbcType.INSTANCE);
118126
typeContributions.contributeJavaType(InstantJavaType.INSTANCE);
119127
typeContributions.contributeJdbcType(InstantJdbcType.INSTANCE);
128+
129+
// custom jdbc codec
130+
typeContributions.contributeJdbcType(Uint8JdbcType.INSTANCE);
131+
typeContributions.contributeJavaType(BigDecimalJavaType.INSTANCE_22_9);
132+
120133
}
121134

122135
@Override
@@ -125,7 +138,29 @@ protected void registerColumnTypes(TypeContributions typeContributions, ServiceR
125138

126139
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
127140

128-
ddlTypeRegistry.addDescriptor(new DdlTypeImpl(JDBC_TYPE_DATETIME_CODE, "Datetime", "Datetime", this));
141+
ddlTypeRegistry.addDescriptor(new DdlTypeImpl(YdbJdbcCode.DATETIME, "Datetime", "Datetime", this));
142+
ddlTypeRegistry.addDescriptor(new DdlTypeImpl(YdbJdbcCode.UINT8, "Uint8", "Uint8", this));
143+
}
144+
145+
@Override
146+
public JdbcType resolveSqlTypeDescriptor(
147+
String columnTypeName,
148+
int jdbcTypeCode,
149+
int precision,
150+
int scale,
151+
JdbcTypeRegistry jdbcTypeRegistry) {
152+
if ((jdbcTypeCode == NUMERIC || jdbcTypeCode == DECIMAL) && (precision != 0 || scale != 0)) {
153+
int sqlCode = SHIFT_DECIMAL_CODE + (precision + scale) * (precision + scale + 1) / 2 + precision;
154+
155+
return DECIMAL_JDBC_TYPE_CACHE.computeIfAbsent(sqlCode, DecimalJdbcType::new);
156+
}
157+
158+
return super.resolveSqlTypeDescriptor(columnTypeName, jdbcTypeCode, precision, scale, jdbcTypeRegistry);
159+
}
160+
161+
@Override
162+
public int getDefaultDecimalPrecision() {
163+
return 22;
129164
}
130165

131166
@Override
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package tech.ydb.hibernate.dialect.code;
2+
3+
/**
4+
* @author Kirill Kurdyukov
5+
*/
6+
public final class YdbJdbcCode {
7+
8+
/**
9+
* Boolean value.
10+
*/
11+
public static final int BOOL = 10000;
12+
13+
/**
14+
* A signed integer. Acceptable values: from -2^7 to 2^7–1. Not supported for table columns
15+
*/
16+
public static final int INT8 = 10001;
17+
18+
/**
19+
* An unsigned integer. Acceptable values: from 0 to 2^8–1.
20+
*/
21+
public static final int UINT8 = 10002;
22+
23+
/**
24+
* A signed integer. Acceptable values: from –2^15 to 2^15–1. Not supported for table columns
25+
*/
26+
public static final int INT16 = 10003;
27+
28+
/**
29+
* An unsigned integer. Acceptable values: from 0 to 2^16–1. Not supported for table columns
30+
*/
31+
public static final int UINT16 = 10004;
32+
33+
/**
34+
* A signed integer. Acceptable values: from –2^31 to 2^31–1.
35+
*/
36+
public static final int INT32 = 10005;
37+
38+
/**
39+
* An unsigned integer. Acceptable values: from 0 to 2^32–1.
40+
*/
41+
public static final int UINT32 = 10006;
42+
43+
/**
44+
* A signed integer. Acceptable values: from –2^63 to 2^63–1.
45+
*/
46+
public static final int INT64 = 10007;
47+
48+
/**
49+
* An unsigned integer. Acceptable values: from 0 to 2^64–1.
50+
*/
51+
public static final int UINT64 = 10008;
52+
53+
/**
54+
* A real number with variable precision, 4 bytes in size. Can't be used in the primary key
55+
*/
56+
public static final int FLOAT = 10009;
57+
58+
/**
59+
* A real number with variable precision, 8 bytes in size. Can't be used in the primary key
60+
*/
61+
public static final int DOUBLE = 10010;
62+
63+
/**
64+
* A binary data, synonym for YDB type String
65+
*/
66+
public static final int BYTES = 10011;
67+
68+
/**
69+
* Text encoded in UTF-8, synonym for YDB type Utf8
70+
*/
71+
public static final int TEXT = 10012;
72+
73+
/**
74+
* YSON in a textual or binary representation. Doesn't support matching, can't be used in the primary key
75+
*/
76+
public static final int YSON = 10013;
77+
78+
/**
79+
* JSON represented as text. Doesn't support matching, can't be used in the primary key
80+
*/
81+
public static final int JSON = 10014;
82+
83+
/**
84+
* Universally unique identifier UUID. Not supported for table columns
85+
*/
86+
public static final int UUID = 10015;
87+
88+
/**
89+
* Date, precision to the day
90+
*/
91+
public static final int DATE = 10016;
92+
93+
/**
94+
* Date/time, precision to the second
95+
*/
96+
public static final int DATETIME = 10017;
97+
98+
/**
99+
* Date/time, precision to the microsecond
100+
*/
101+
public static final int TIMESTAMP = 10018;
102+
103+
/**
104+
* Time interval (signed), precision to microseconds
105+
*/
106+
public static final int INTERVAL = 10019;
107+
108+
/**
109+
* Date with time zone label, precision to the day
110+
*/
111+
public static final int TZ_DATE = 10020;
112+
113+
/**
114+
* Date/time with time zone label, precision to the second
115+
*/
116+
public static final int TZ_DATETIME = 10021;
117+
118+
/**
119+
* Date/time with time zone label, precision to the microsecond
120+
*/
121+
public static final int TZ_TIMESTAMP = 10022;
122+
123+
/**
124+
* JSON in an indexed binary representation. Doesn't support matching, can't be used in the primary key
125+
*/
126+
public static final int JSON_DOCUMENT = 10023;
127+
128+
public static final int DECIMAL_22_9 = 10024;
129+
130+
public static final int DECIMAL_31_9 = 10025;
131+
132+
public static final int DECIMAL_35_9 = 10026;
133+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package tech.ydb.hibernate.dialect.types;
2+
3+
import org.hibernate.dialect.Dialect;
4+
import org.hibernate.type.descriptor.jdbc.JdbcType;
5+
6+
/**
7+
* @author Kirill Kurdyukov
8+
*/
9+
public final class BigDecimalJavaType extends org.hibernate.type.descriptor.java.BigDecimalJavaType {
10+
11+
public static final BigDecimalJavaType INSTANCE_22_9 = new BigDecimalJavaType();
12+
13+
@Override
14+
public int getDefaultSqlScale(Dialect dialect, JdbcType jdbcType) {
15+
return 9;
16+
}
17+
18+
@Override
19+
public int getDefaultSqlPrecision(Dialect dialect, JdbcType jdbcType) {
20+
return 22;
21+
}
22+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package tech.ydb.hibernate.dialect.types;
2+
3+
import java.math.BigDecimal;
4+
import java.sql.CallableStatement;
5+
import java.sql.PreparedStatement;
6+
import java.sql.SQLException;
7+
import org.hibernate.type.descriptor.ValueBinder;
8+
import org.hibernate.type.descriptor.WrapperOptions;
9+
import org.hibernate.type.descriptor.java.JavaType;
10+
11+
/**
12+
* @author Kirill Kurdyukov
13+
*/
14+
public class DecimalJdbcType extends org.hibernate.type.descriptor.jdbc.DecimalJdbcType {
15+
private final int sqlCode;
16+
17+
public DecimalJdbcType(int sqlCode) {
18+
this.sqlCode = sqlCode;
19+
}
20+
21+
@Override
22+
public <X> ValueBinder<X> getBinder(final JavaType<X> javaType) {
23+
return new ValueBinder<>() {
24+
@Override
25+
public void bind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
26+
st.setObject(index, javaType.unwrap(value, BigDecimal.class, options), sqlCode);
27+
}
28+
29+
@Override
30+
public void bind(CallableStatement st, X value, String name, WrapperOptions options) throws SQLException {
31+
st.setObject(name, javaType.unwrap(value, BigDecimal.class, options), sqlCode);
32+
}
33+
};
34+
}
35+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package tech.ydb.hibernate.dialect.types;
2+
3+
4+
import java.sql.CallableStatement;
5+
import java.sql.PreparedStatement;
6+
import java.sql.ResultSet;
7+
import java.sql.SQLException;
8+
import org.hibernate.type.descriptor.ValueBinder;
9+
import org.hibernate.type.descriptor.ValueExtractor;
10+
import org.hibernate.type.descriptor.WrapperOptions;
11+
import org.hibernate.type.descriptor.java.JavaType;
12+
import org.hibernate.type.descriptor.jdbc.JdbcType;
13+
import org.hibernate.type.spi.TypeConfiguration;
14+
import tech.ydb.hibernate.dialect.code.YdbJdbcCode;
15+
16+
/**
17+
* @author Kirill Kurdyukov
18+
*/
19+
public class Uint8JdbcType implements JdbcType {
20+
public static final Uint8JdbcType INSTANCE = new Uint8JdbcType();
21+
22+
@Override
23+
public int getJdbcTypeCode() {
24+
return YdbJdbcCode.UINT8;
25+
}
26+
27+
@Override
28+
public <T> JavaType<T> getJdbcRecommendedJavaTypeMapping(
29+
Integer precision,
30+
Integer scale,
31+
TypeConfiguration typeConfiguration) {
32+
return typeConfiguration.getJavaTypeRegistry().getDescriptor(Integer.class);
33+
}
34+
35+
@Override
36+
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
37+
return new ValueBinder<>() {
38+
@Override
39+
public void bind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
40+
st.setObject(index, javaType.unwrap(value, Integer.class, options), getJdbcTypeCode());
41+
}
42+
43+
@Override
44+
public void bind(CallableStatement st, X value, String name, WrapperOptions options) throws SQLException {
45+
st.setObject(name, javaType.unwrap(value, Integer.class, options), getJdbcTypeCode());
46+
}
47+
};
48+
}
49+
50+
@Override
51+
public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
52+
return new ValueExtractor<>() {
53+
@Override
54+
public X extract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
55+
return javaType.wrap(rs.getObject(paramIndex), options);
56+
}
57+
58+
@Override
59+
public X extract(CallableStatement statement, int paramIndex, WrapperOptions options) throws SQLException {
60+
return javaType.wrap(statement.getObject(paramIndex), options);
61+
}
62+
63+
@Override
64+
public X extract(CallableStatement statement, String paramName, WrapperOptions options) throws SQLException {
65+
return javaType.wrap(statement.getObject(paramName), options);
66+
}
67+
};
68+
}
69+
}

0 commit comments

Comments
 (0)