diff --git a/hibernate-dialect/pom.xml b/hibernate-dialect/pom.xml
index 617e64f..c9a702c 100644
--- a/hibernate-dialect/pom.xml
+++ b/hibernate-dialect/pom.xml
@@ -41,8 +41,8 @@
5.9.3
2.17.2
- 2.3.4
- 2.3.3
+ 2.3.13
+ 2.3.10
@@ -146,7 +146,6 @@
true
enable_parameterized_decimal
- cr.yandex/yc/yandex-docker-local-ydb:trunk
diff --git a/spring-data-jdbc-ydb/pom.xml b/spring-data-jdbc-ydb/pom.xml
index aaa9f63..98eec66 100644
--- a/spring-data-jdbc-ydb/pom.xml
+++ b/spring-data-jdbc-ydb/pom.xml
@@ -50,13 +50,12 @@
17
5.10.2
- 1.18.30
3.4.0
4.24.0
- 2.2.9
- 2.2.3
- 0.9.7
+ 2.3.13
+ 2.3.10
+ 1.1.1
@@ -84,12 +83,6 @@
spring-data-jdbc
provided
-
- tech.ydb.jdbc
- ydb-jdbc-driver
- ${ydb.jdbc.version}
- provided
-
tech.ydb.test
@@ -102,9 +95,9 @@
test
- org.projectlombok
- lombok
- ${lombok.version}
+ tech.ydb.jdbc
+ ydb-jdbc-driver
+ ${ydb.jdbc.version}
test
@@ -126,7 +119,6 @@
org.liquibase
liquibase-core
- ${liquibase.version}
test
diff --git a/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YQLType.java b/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YQLType.java
index 778d597..73559ae 100644
--- a/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YQLType.java
+++ b/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YQLType.java
@@ -1,25 +1,75 @@
package tech.ydb.data.core.convert;
-import java.sql.SQLType;
-import tech.ydb.jdbc.YdbConst;
-import tech.ydb.table.values.PrimitiveType;
-
-/**
- * @author Madiyar Nurgazin
- */
-public record YQLType(PrimitiveType type) implements SQLType {
- @Override
- public String getName() {
- return type.name();
- }
- @Override
- public String getVendor() {
- return "YDB";
+public enum YQLType {
+ /** Boolean value. */
+ Bool(YdbConst.SQL_KIND_PRIMITIVE + 0),
+ /** A signed integer. Acceptable values: from -2^7 to 2^7–1. Not supported for table columns */
+ Int8(YdbConst.SQL_KIND_PRIMITIVE + 1),
+ /** An unsigned integer. Acceptable values: from 0 to 2^8–1. */
+ Uint8(YdbConst.SQL_KIND_PRIMITIVE + 2),
+ /** A signed integer. Acceptable values: from –2^15 to 2^15–1. Not supported for table columns */
+ Int16(YdbConst.SQL_KIND_PRIMITIVE + 3),
+ /** An unsigned integer. Acceptable values: from 0 to 2^16–1. Not supported for table columns */
+ Uint16(YdbConst.SQL_KIND_PRIMITIVE + 4),
+ /** A signed integer. Acceptable values: from –2^31 to 2^31–1. */
+ Int32(YdbConst.SQL_KIND_PRIMITIVE + 5),
+ /** An unsigned integer. Acceptable values: from 0 to 2^32–1. */
+ Uint32(YdbConst.SQL_KIND_PRIMITIVE + 6),
+ /** A signed integer. Acceptable values: from –2^63 to 2^63–1. */
+ Int64(YdbConst.SQL_KIND_PRIMITIVE + 7),
+ /** An unsigned integer. Acceptable values: from 0 to 2^64–1. */
+ Uint64(YdbConst.SQL_KIND_PRIMITIVE + 8),
+ /** A real number with variable precision, 4 bytes in size. Can't be used in the primary key */
+ Float(YdbConst.SQL_KIND_PRIMITIVE + 9),
+ /** A real number with variable precision, 8 bytes in size. Can't be used in the primary key */
+ Double(YdbConst.SQL_KIND_PRIMITIVE + 10),
+ /** A binary data, synonym for YDB type String */
+ Bytes(YdbConst.SQL_KIND_PRIMITIVE + 11),
+ /** Text encoded in UTF-8, synonym for YDB type Utf8 */
+ Text(YdbConst.SQL_KIND_PRIMITIVE + 12),
+ /** YSON in a textual or binary representation. Doesn't support matching, can't be used in the primary key */
+ Yson(YdbConst.SQL_KIND_PRIMITIVE + 13),
+ /** JSON represented as text. Doesn't support matching, can't be used in the primary key */
+ Json(YdbConst.SQL_KIND_PRIMITIVE + 14),
+ /** Universally unique identifier UUID. Not supported for table columns */
+ Uuid(YdbConst.SQL_KIND_PRIMITIVE + 15),
+ /** Date, precision to the day */
+ Date(YdbConst.SQL_KIND_PRIMITIVE + 16),
+ /** Date/time, precision to the second */
+ Datetime(YdbConst.SQL_KIND_PRIMITIVE + 17),
+ /** Date/time, precision to the microsecond */
+ Timestamp(YdbConst.SQL_KIND_PRIMITIVE + 18),
+ /** Time interval (signed), precision to microseconds */
+ Interval(YdbConst.SQL_KIND_PRIMITIVE + 19),
+ /** Date with time zone label, precision to the day */
+ TzDate(YdbConst.SQL_KIND_PRIMITIVE + 20),
+ /** Date/time with time zone label, precision to the second */
+ TzDatetime(YdbConst.SQL_KIND_PRIMITIVE + 21),
+ /** Date/time with time zone label, precision to the microsecond */
+ TzTimestamp(YdbConst.SQL_KIND_PRIMITIVE + 22),
+ /** JSON in an indexed binary representation. Doesn't support matching, can't be used in the primary key */
+ JsonDocument(YdbConst.SQL_KIND_PRIMITIVE + 23),
+
+ // DyNumber(YdbConst.SQL_KIND_PRIMITIVE + 24), -- not supported by JDBC Driver
+
+ Date32(YdbConst.SQL_KIND_PRIMITIVE + 25),
+
+ Datetime64(YdbConst.SQL_KIND_PRIMITIVE + 26),
+
+ Timestamp64(YdbConst.SQL_KIND_PRIMITIVE + 27),
+
+ Interval64(YdbConst.SQL_KIND_PRIMITIVE + 28),
+
+ Decimal(YdbConst.SQL_DEFAULT_DECIMAL); // special case
+
+ private final int sqlType;
+
+ private YQLType(int sqlType) {
+ this.sqlType = sqlType;
}
- @Override
- public Integer getVendorTypeNumber() {
- return YdbConst.SQL_KIND_PRIMITIVE + type.ordinal();
+ public int getSqlType() {
+ return this.sqlType;
}
}
diff --git a/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YdbConst.java b/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YdbConst.java
new file mode 100644
index 0000000..e7633ad
--- /dev/null
+++ b/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YdbConst.java
@@ -0,0 +1,21 @@
+package tech.ydb.data.core.convert;
+
+/**
+ * That class contain custom YDB type codes
+ * @see JDBC Driver constants
+ * @see Primitive types
+ * @see Decimal type
+ *
+ * @author Aleksandr Gorshenin
+ */
+final class YdbConst {
+ public static final int SQL_KIND_PRIMITIVE = 10000;
+ public static final int SQL_DEFAULT_DECIMAL = ydbDecimal(22, 9);
+ private static final int SQL_KIND_DECIMAL = 1 << 14; // 16384
+
+ public static int ydbDecimal(int precision, int scale) {
+ return SQL_KIND_DECIMAL + (precision << 6) + (scale & 0x111111);
+ }
+
+ private YdbConst() { };
+}
diff --git a/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YdbMappingJdbcConverter.java b/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YdbMappingJdbcConverter.java
index 7d8c8ea..57f44ec 100644
--- a/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YdbMappingJdbcConverter.java
+++ b/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YdbMappingJdbcConverter.java
@@ -1,18 +1,30 @@
package tech.ydb.data.core.convert;
import java.sql.SQLType;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.jdbc.core.convert.JdbcTypeFactory;
import org.springframework.data.jdbc.core.convert.MappingJdbcConverter;
import org.springframework.data.jdbc.core.convert.RelationResolver;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
-import tech.ydb.table.values.PrimitiveType;
+
+import tech.ydb.data.core.convert.annotation.YdbType;
+
/**
* @author Madiyar Nurgazin
+ * @author Mikhail Polivakha
*/
+@SuppressWarnings("removal")
public class YdbMappingJdbcConverter extends MappingJdbcConverter {
+ private final static Class ANNOTATION = YdbType.class;
+ private final static Class OLD_TYPE = tech.ydb.data.core.convert.YdbType.class;
+
+ private final ConcurrentMap typesCache = new ConcurrentHashMap<>();
+
public YdbMappingJdbcConverter(RelationalMappingContext context, RelationResolver relationResolver,
CustomConversions conversions, JdbcTypeFactory typeFactory) {
super(context, relationResolver, conversions, typeFactory);
@@ -20,8 +32,26 @@ public YdbMappingJdbcConverter(RelationalMappingContext context, RelationResolve
@Override
public SQLType getTargetSqlType(RelationalPersistentProperty property) {
- return property.isAnnotationPresent(YdbType.class) ?
- new YQLType(PrimitiveType.valueOf(property.getRequiredAnnotation(YdbType.class).value())) :
- super.getTargetSqlType(property);
+ return typesCache.computeIfAbsent(property, this::resolveSqlType);
+ }
+
+ private SQLType resolveSqlType(RelationalPersistentProperty property) {
+ if (property.isAnnotationPresent(ANNOTATION)) {
+ tech.ydb.data.core.convert.annotation.YdbType type = property.getRequiredAnnotation(ANNOTATION);
+ YQLType yql = type.value();
+ if (yql == YQLType.Decimal) {
+ int precision = type.decimalPrecision();
+ int scale = type.decimalScale();
+ return new YdbSqlType(precision, scale);
+ }
+ return new YdbSqlType(yql);
+ }
+
+ if (property.isAnnotationPresent(OLD_TYPE)) {
+ String typeName = property.getRequiredAnnotation(OLD_TYPE).value();
+ return new YdbSqlType(YQLType.valueOf(typeName));
+ }
+
+ return super.getTargetSqlType(property);
}
}
diff --git a/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YdbSqlType.java b/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YdbSqlType.java
new file mode 100644
index 0000000..8bc19be
--- /dev/null
+++ b/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YdbSqlType.java
@@ -0,0 +1,40 @@
+package tech.ydb.data.core.convert;
+
+import java.io.Serializable;
+import java.sql.SQLType;
+
+/**
+ *
+ * @author Aleksandr Gorshenin
+ */
+class YdbSqlType implements SQLType, Serializable {
+ private static final long serialVersionUID = -5722445668088782880L;
+
+ private final String name;
+ private final int vendorCode;
+
+ public YdbSqlType(YQLType type) {
+ this.name = type.name();
+ this.vendorCode = type.getSqlType();
+ }
+
+ public YdbSqlType(int decimalPrecision, int decimalScale) {
+ this.name = "Decimal(" + decimalPrecision + "," + decimalScale + ")";
+ this.vendorCode = YdbConst.ydbDecimal(decimalPrecision, decimalScale);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getVendor() {
+ return "YDB";
+ }
+
+ @Override
+ public Integer getVendorTypeNumber() {
+ return vendorCode;
+ }
+}
diff --git a/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YdbType.java b/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YdbType.java
index 1bdeafc..23c76c9 100644
--- a/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YdbType.java
+++ b/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/YdbType.java
@@ -1,15 +1,26 @@
package tech.ydb.data.core.convert;
+import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
+ * The annotation for qualification of the target YDB data type.
+ *
* @author Madiyar Nurgazin
+ * @author Mikhail Polivakha
+ * @deprecated Please, use {@link tech.ydb.data.core.convert.annotation.YdbType} instead because of type safety considerations.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
+@Deprecated(forRemoval = true)
+@Documented
public @interface YdbType {
+ /**
+ * The target YDB data type.
+ * @return name of YDB data type
+ */
String value();
}
diff --git a/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/annotation/YdbType.java b/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/annotation/YdbType.java
new file mode 100644
index 0000000..a8c2b32
--- /dev/null
+++ b/spring-data-jdbc-ydb/src/main/java/tech/ydb/data/core/convert/annotation/YdbType.java
@@ -0,0 +1,39 @@
+package tech.ydb.data.core.convert.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import tech.ydb.data.core.convert.YQLType;
+
+
+/**
+ * The annotation for qualification of the target YDB data type.
+ *
+ * @author Mikhail Polivakha
+ * @author Aleksandr Gorshenin
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+@Documented
+public @interface YdbType {
+ /**
+ * The target YDB data type.
+ * @return The target YDB data type.
+ */
+ YQLType value();
+
+ /**
+ * Decimal precision. Applies only to {@link YQLType#Decimal }
+ * @return Custom decimal type precision.
+ */
+ int decimalPrecision() default 22;
+
+ /**
+ * Decimal scale. Applies only to {@link YQLType#Decimal }
+ * @return Custom decimal type scale.
+ */
+ int decimalScale() default 9;
+}
diff --git a/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/all_types_table/AllTypesTableTest.java b/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/all_types_table/AllTypesTableTest.java
index ea1e98b..e50df70 100644
--- a/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/all_types_table/AllTypesTableTest.java
+++ b/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/all_types_table/AllTypesTableTest.java
@@ -7,11 +7,13 @@
import java.time.temporal.ChronoUnit;
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 tech.ydb.data.YdbBaseTest;
import tech.ydb.data.all_types_table.entity.AllTypesEntity;
import tech.ydb.data.all_types_table.repository.AllTypesEntityRepository;
@@ -41,6 +43,7 @@ public void allTypesTableCrudTest() {
);
repository.save(expected);
+ Assertions.assertEquals(expected.getDecimalColumn(), entity1.get().getDecimalColumn());
Assertions.assertEquals(expected, entity1.get());
Assertions.assertEquals(expected.getTextColumn(),
repository.findAllByTextColumn("Madiyar Nurgazin").get(0).getTextColumn());
@@ -69,10 +72,11 @@ public void allTypesTableCrudTest() {
entities = repository.findAllByDateColumnAfterNow();
Assertions.assertEquals(1, entities.size());
- Assertions.assertEquals(4, entities.get(0).getId());
+ Assertions.assertEquals(Integer.valueOf(4), entities.get(0).getId());
entity3.setJsonColumn("Not json");
- Assertions.assertThrows(DbActionExecutionException.class, () -> repository.save(entity3));
+ var ex = Assertions.assertThrows(DbActionExecutionException.class, () -> repository.save(entity3));
+ Assertions.assertTrue(ex.getMessage().startsWith("Failed to execute DbAction.UpdateRoot"));
entity3.setJsonColumn("{\"values\": [1, 2, 3]}");
AllTypesEntity updated = repository.save(entity3);
diff --git a/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/all_types_table/entity/AllTypesEntity.java b/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/all_types_table/entity/AllTypesEntity.java
index f1caa6b..9fd5b7d 100644
--- a/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/all_types_table/entity/AllTypesEntity.java
+++ b/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/all_types_table/entity/AllTypesEntity.java
@@ -4,20 +4,20 @@
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
-import lombok.AllArgsConstructor;
-import lombok.Data;
+import java.util.Arrays;
+import java.util.Objects;
+
import org.springframework.data.annotation.Id;
-import org.springframework.data.domain.Persistable;
import org.springframework.data.relational.core.mapping.Table;
-import tech.ydb.data.core.convert.YdbType;
+
+import tech.ydb.data.core.convert.YQLType;
+import tech.ydb.data.core.convert.annotation.YdbType;
/**
* @author Madiyar Nurgazin
*/
-@AllArgsConstructor
-@Data
@Table("all_types_table")
-public class AllTypesEntity implements Persistable {
+public class AllTypesEntity {
@Id
private Integer id;
private String textColumn;
@@ -29,28 +29,128 @@ public class AllTypesEntity implements Persistable {
private double doubleColumn;
private BigDecimal decimalColumn;
private byte[] binaryColumn;
- @YdbType("Date")
+ @YdbType(YQLType.Date)
private LocalDate dateColumn;
- @YdbType("Datetime")
+ @YdbType(YQLType.Datetime)
private LocalDateTime datetimeColumn;
private Instant timestampColumn;
- @YdbType("Json")
+ @YdbType(YQLType.Json)
private String jsonColumn;
- @YdbType("JsonDocument")
+ @YdbType(YQLType.JsonDocument)
private String jsonDocumentColumn;
- @YdbType("Uint8")
+ @YdbType(YQLType.Uint8)
private byte uint8Column;
- @YdbType("Uint16")
+ @YdbType(YQLType.Uint16)
private short uint16Column;
- @YdbType("Uint32")
+ @YdbType(YQLType.Uint32)
private int uint32Column;
- @YdbType("Uint64")
+ @YdbType(YQLType.Uint64)
private long uint64Column;
public AllTypesEntity() {
}
+
+ public AllTypesEntity(Integer id, String textColumn, boolean boolColumn, byte tinyintColumn, short smallintColumn,
+ long bigintColumn, float floatColumn, double doubleColumn, BigDecimal decimalColumn, byte[] binaryColumn,
+ LocalDate dateColumn, LocalDateTime datetimeColumn, Instant timestampColumn, String jsonColumn,
+ String jsonDocumentColumn, byte uint8Column, short uint16Column, int uint32Column, long uint64Column) {
+ this.id = id;
+ this.textColumn = textColumn;
+ this.boolColumn = boolColumn;
+ this.tinyintColumn = tinyintColumn;
+ this.smallintColumn = smallintColumn;
+ this.bigintColumn = bigintColumn;
+ this.floatColumn = floatColumn;
+ this.doubleColumn = doubleColumn;
+ this.decimalColumn = decimalColumn;
+ this.binaryColumn = binaryColumn;
+ this.dateColumn = dateColumn;
+ this.datetimeColumn = datetimeColumn;
+ this.timestampColumn = timestampColumn;
+ this.jsonColumn = jsonColumn;
+ this.jsonDocumentColumn = jsonDocumentColumn;
+ this.uint8Column = uint8Column;
+ this.uint16Column = uint16Column;
+ this.uint32Column = uint32Column;
+ this.uint64Column = uint64Column;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public String getTextColumn() {
+ return textColumn;
+ }
+
+ public BigDecimal getDecimalColumn() {
+ return decimalColumn;
+ }
+
+ public void setJsonColumn(String jsonColumn) {
+ this.jsonColumn = jsonColumn;
+ }
+
+ public void setJsonDocumentColumn(String jsonDocumentColumn) {
+ this.jsonDocumentColumn = jsonDocumentColumn;
+ }
+
@Override
- public boolean isNew() {
- return false;
+ public int hashCode() {
+ return Objects.hash(
+ id,
+ textColumn,
+ boolColumn,
+ tinyintColumn,
+ smallintColumn,
+ bigintColumn,
+ floatColumn,
+ doubleColumn,
+ decimalColumn,
+ binaryColumn,
+ dateColumn,
+ datetimeColumn,
+ timestampColumn,
+ jsonColumn,
+ jsonDocumentColumn,
+ uint8Column,
+ uint16Column,
+ uint32Column,
+ uint64Column
+ );
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+
+ AllTypesEntity other = (AllTypesEntity) obj;
+ return Objects.equals(id, other.id)
+ && Objects.equals(textColumn, other.textColumn)
+ && boolColumn == other.boolColumn
+ && tinyintColumn == other.tinyintColumn
+ && smallintColumn == other.smallintColumn
+ && bigintColumn == other.bigintColumn
+ && floatColumn == other.floatColumn
+ && doubleColumn == other.doubleColumn
+ && Objects.equals(decimalColumn, other.decimalColumn)
+ && Arrays.equals(binaryColumn, other.binaryColumn)
+ && Objects.equals(dateColumn, other.dateColumn)
+ && Objects.equals(datetimeColumn, other.datetimeColumn)
+ && Objects.equals(timestampColumn, other.timestampColumn)
+ && Objects.equals(jsonColumn, other.jsonColumn)
+ && Objects.equals(jsonDocumentColumn, other.jsonDocumentColumn)
+ && uint8Column == other.uint8Column
+ && uint16Column == other.uint16Column
+ && uint32Column == other.uint32Column
+ && uint64Column == other.uint64Column;
+ }
+
+
}
diff --git a/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/books/RepositoriesIntegrationTest.java b/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/books/RepositoriesIntegrationTest.java
index 1608a7f..540c6ea 100644
--- a/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/books/RepositoriesIntegrationTest.java
+++ b/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/books/RepositoriesIntegrationTest.java
@@ -1,14 +1,17 @@
package tech.ydb.data.books;
import java.time.Instant;
+import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
+
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
+
import tech.ydb.data.YdbBaseTest;
import tech.ydb.data.books.entity.Author;
import tech.ydb.data.books.entity.Book;
@@ -32,29 +35,27 @@ public class RepositoriesIntegrationTest extends YdbBaseTest {
@Test
public void crudTest() {
- Assertions.assertEquals(1, authorRepository.findAuthorByName("Leo Tolstoy").get(0).getId());
+ Assertions.assertEquals(Long.valueOf(1), authorRepository.findAuthorByName("Leo Tolstoy").get(0).getId());
- Review review1 = createReview(
- 1, 1, "Ivan Ivanov", "Masterpiece!", 10, Instant.parse("2024-03-19T15:52:26Z")
- );
+ Review review1 = reviewRepository.findById(1L).orElseThrow();
+ Review review2 = reviewRepository.findById(2L).orElseThrow();
- Review review2 = createReview(
- 2, 1, "Sergey Petrov", "Complex work, but I liked it", 9, Instant.parse("2024-03-19T16:14:05Z")
- );
+ Book book1 = new Book(1, "War and Peace", "1234", 1869);
+ book1.getReviews().add(review1);
+ book1.getReviews().add(review2);
+ book1.getAuthors().add(new BookAuthor(1, 1));
- List expected = List.of(
- createBook(1, "War and Peace", "1234", 1869, Set.of(review1, review2), Set.of(new BookAuthor(1, 1))),
- createBook(2, "Anna Karenina", "5678", 1878, Set.of(), Set.of(new BookAuthor(1, 2)))
- );
+ Book book2 = new Book(2, "Anna Karenina", "5678", 1878);
+ book1.getAuthors().add(new BookAuthor(1, 2));
+
+ List expected = List.of(book1, book2);
Iterable books = bookRepository.findAll();
Assertions.assertEquals(expected, books);
Optional bookO = bookRepository.findBookByTitle("War and Peace");
Assertions.assertTrue(bookO.isPresent());
- Review review3 = createReview(
- 3, 1, "Madiyar Nurgazin", "Great", 8, Instant.parse("2024-03-19T20:00:00Z")
- );
+ Review review3 = new Review(3, 1, "Madiyar Nurgazin", "Great", 8, Instant.parse("2024-03-19T20:00:00Z"));
Book book = bookO.get();
book.getReviews().add(review3);
@@ -63,11 +64,11 @@ public void crudTest() {
Assertions.assertEquals(3, reviewRepository.count());
review1.setRating(100);
- review1.setNew(false);
+ review1.setIsNew(false);
review2.setRating(90);
- review2.setNew(false);
+ review2.setIsNew(false);
review3.setRating(80);
- review3.setNew(false);
+ review3.setIsNew(false);
Set reviews = Set.of(review1, review2, review3);
reviewRepository.saveAll(reviews);
@@ -78,13 +79,15 @@ public void crudTest() {
book = bookO.get();
Assertions.assertEquals(reviews, book.getReviews());
- Author author1 = createAuthor(2, "Author 1");
- Author author2 = createAuthor(3, "Author 2");
+ Author author1 = new Author(2, "Author 1");
+ Author author2 = new Author(3, "Author 2");
authorRepository.saveAll(List.of(author1, author2));
Assertions.assertEquals(3, authorRepository.count());
- book = createBook(3, "Title", "Isbn", 2024, Set.of(), Set.of(new BookAuthor(2, 3), new BookAuthor(3, 3)));
+ book = new Book(3, "Title", "Isbn", 2024);
+ book.getAuthors().add(new BookAuthor(2, 3));
+ book.getAuthors().add(new BookAuthor(3, 3));
bookRepository.save(book);
expected.get(0).setReviews(Set.of(review1, review2, review3));
@@ -97,7 +100,7 @@ public void crudTest() {
List authors = authorRepository.findAuthorsByBookId(3);
Assertions.assertEquals(Set.of(author1, author2), Set.copyOf(authors));
- Review review = createReview(4, 3, "Reader", "Text", 5, Instant.now());
+ Review review = new Review(4, 3, "Reader", "Text", 5, Instant.now());
reviewRepository.save(review);
bookRepository.deleteById(3L);
@@ -114,16 +117,12 @@ public void crudTest() {
@Test
public void pagingAndSortingTest() {
- Review review1 = createReview(
- 1, 1, "Ivan Ivanov", "Masterpiece!", 10, Instant.parse("2024-03-19T15:52:26Z")
- );
- Review review2 = createReview(
- 2, 1, "Sergey Petrov", "Complex work, but I liked it", 9, Instant.parse("2024-03-19T16:14:05Z")
- );
- Review review3 = createReview(3, 1, "Reader", "Text", 100, Instant.parse("2024-03-19T21:00:00Z"));
- Review review4 = createReview(4, 1, "Reader", "Text2", 80, Instant.parse("2024-03-19T22:00:00Z"));
- Review review5 = createReview(5, 1, "Reader", "Text3", 75, Instant.parse("2024-03-19T23:00:00Z"));
- Review review6 = createReview(6, 1, "Reader", "Text4", 50, Instant.parse("2024-03-20T00:00:00Z"));
+ Review review1 = reviewRepository.findById(1L).orElseThrow();
+ Review review2 = reviewRepository.findById(2L).orElseThrow();
+ Review review3 = new Review(3, 1, "Reader", "Text", 100, Instant.parse("2024-03-19T21:00:00Z"));
+ Review review4 = new Review(4, 1, "Reader", "Text2", 80, Instant.parse("2024-03-19T22:00:00Z"));
+ Review review5 = new Review(5, 1, "Reader", "Text3", 75, Instant.parse("2024-03-19T23:00:00Z"));
+ Review review6 = new Review(6, 1, "Reader", "Text4", 50, Instant.parse("2024-03-20T00:00:00Z"));
reviewRepository.saveAll(List.of(review3, review4, review5, review6));
Iterable reviews = reviewRepository.findByReader(
@@ -137,6 +136,21 @@ public void pagingAndSortingTest() {
Assertions.assertEquals(List.of(review5, review6), reviews);
reviews = reviewRepository.findAll(Sort.by("created").descending());
+
+ Iterator it = reviews.iterator();
+ Assertions.assertTrue(it.hasNext());
+ Assertions.assertEquals(review6, it.next());
+ Assertions.assertTrue(it.hasNext());
+ Assertions.assertEquals(review5, it.next());
+ Assertions.assertTrue(it.hasNext());
+ Assertions.assertEquals(review4, it.next());
+ Assertions.assertTrue(it.hasNext());
+ Assertions.assertEquals(review3, it.next());
+ Assertions.assertTrue(it.hasNext());
+ Assertions.assertEquals(review2, it.next());
+ Assertions.assertTrue(it.hasNext());
+ Assertions.assertEquals(review1, it.next());
+
Assertions.assertEquals(List.of(review6, review5, review4, review3, review2, review1), reviews);
reviews = reviewRepository.findAll(PageRequest.of(0, 3, Sort.by("id"))).getContent();
@@ -148,38 +162,4 @@ public void pagingAndSortingTest() {
reviews = reviewRepository.findAll(PageRequest.of(2, 2)).getContent();
Assertions.assertEquals(List.of(review5, review6), reviews);
}
-
- private Review createReview(long id, long bookId, String reader, String text, long rating, Instant created) {
- Review review = new Review();
- review.setId(id);
- review.setBookId(bookId);
- review.setReader(reader);
- review.setText(text);
- review.setRating(rating);
- review.setCreated(created);
- review.setNew(true);
- return review;
- }
-
- private Book createBook(
- long id, String title, String isbn, long year, Set reviews, Set authors
- ) {
- Book book = new Book();
- book.setId(id);
- book.setTitle(title);
- book.setIsbn(isbn);
- book.setYear(year);
- book.setReviews(reviews);
- book.setAuthors(authors);
- book.setNew(true);
- return book;
- }
-
- private Author createAuthor(long id, String name) {
- Author author = new Author();
- author.setId(id);
- author.setName(name);
- author.setNew(true);
- return author;
- }
}
diff --git a/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/books/entity/Author.java b/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/books/entity/Author.java
index 7f8e15b..c1c0469 100644
--- a/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/books/entity/Author.java
+++ b/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/books/entity/Author.java
@@ -1,9 +1,9 @@
package tech.ydb.data.books.entity;
import java.util.HashSet;
+import java.util.Objects;
import java.util.Set;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
+
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.domain.Persistable;
@@ -13,21 +13,29 @@
/**
* @author Madiyar Nurgazin
*/
-@Data
@Table("authors")
public class Author implements Persistable {
@Id
private Long id;
private String name;
+
@MappedCollection(idColumn = "author_id")
- private Set books;
+ private Set books = new HashSet<>();
@Transient
- @EqualsAndHashCode.Exclude
- private boolean isNew;
+ private boolean isNew = false;
+
+ public Author() { }
+
+ public Author(long id, String name) {
+ this.id = id;
+ this.name = name;
+ this.isNew = true;
+ }
- public Author() {
- this.books = new HashSet<>();
+ @Override
+ public Long getId() {
+ return id;
}
@Override
@@ -35,7 +43,38 @@ public boolean isNew() {
return isNew;
}
- public void setNew(boolean isNew) {
- this.isNew = isNew;
+ public String getName() {
+ return name;
+ }
+
+ public Set getBooks() {
+ return books;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setBooks(Set books) {
+ this.books = books;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, name);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+
+ Author other = (Author) obj;
+ return Objects.equals(id, other.id) && Objects.equals(name, other.name);
}
}
diff --git a/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/books/entity/Book.java b/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/books/entity/Book.java
index cd9b89e..9fd4614 100644
--- a/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/books/entity/Book.java
+++ b/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/books/entity/Book.java
@@ -1,10 +1,9 @@
package tech.ydb.data.books.entity;
import java.util.HashSet;
+import java.util.Objects;
import java.util.Set;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.domain.Persistable;
@@ -14,35 +13,101 @@
/**
* @author Madiyar Nurgazin
*/
-@Data
@Table("books")
-public class Book implements Persistable {
+public class Book implements Persistable {
@Id
private Long id;
private String title;
private String isbn;
private long year;
+ @Transient
+ private boolean isNew = false;
+
@MappedCollection(idColumn = "book_id")
- private Set reviews;
+ private Set reviews = new HashSet<>();
@MappedCollection(idColumn = "book_id")
- private Set authors;
+ private Set authors = new HashSet<>();
- @Transient
- @EqualsAndHashCode.Exclude
- private boolean isNew;
+ public Book() { }
+
+ public Book(long id, String title, String isbn, long year) {
+ this.id = id;
+ this.title = title;
+ this.isbn = isbn;
+ this.year = year;
+ this.isNew = true;
+ }
+
+ @Override
+ public Long getId() {
+ return id;
+ }
@Override
public boolean isNew() {
return isNew;
}
- public Book() {
- this.reviews = new HashSet<>();
- this.authors = new HashSet<>();
+ public String getTitle() {
+ return title;
+ }
+
+ public String getIsbn() {
+ return isbn;
+ }
+
+ public long getYear() {
+ return year;
+ }
+
+ public Set getReviews() {
+ return reviews;
+ }
+
+ public Set getAuthors() {
+ return authors;
}
- public void setNew(boolean isNew) {
- this.isNew = isNew;
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public void setIsbn(String isbn) {
+ this.isbn = isbn;
+ }
+
+ public void setYear(long year) {
+ this.year = year;
+ }
+
+ public void setReviews(Set reviews) {
+ this.reviews = reviews;
+ }
+
+ public void setAuthors(Set authors) {
+ this.authors = authors;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, title, isbn, year);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+
+ Book other = (Book) obj;
+ return Objects.equals(id, other.id)
+ && Objects.equals(title, other.title)
+ && Objects.equals(isbn, other.isbn)
+ && year == other.year;
}
}
diff --git a/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/books/entity/Review.java b/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/books/entity/Review.java
index deb4cbf..fb87e6e 100644
--- a/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/books/entity/Review.java
+++ b/spring-data-jdbc-ydb/src/test/java/tech/ydb/data/books/entity/Review.java
@@ -1,8 +1,8 @@
package tech.ydb.data.books.entity;
import java.time.Instant;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
+import java.util.Objects;
+
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.domain.Persistable;
@@ -11,7 +11,6 @@
/**
* @author Madiyar Nurgazin
*/
-@Data
@Table("reviews")
public class Review implements Persistable {
@Id
@@ -22,20 +21,96 @@ public class Review implements Persistable {
private long rating;
private Instant created;
- public Review() {
- this.created = Instant.now();
+ @Transient
+ private boolean isNew = false;
+
+ public Review() { }
+
+ public Review(long id, long bookId, String reader, String text, long rating, Instant created) {
+ this.id = id;
+ this.bookId = bookId;
+ this.reader = reader;
+ this.text = text;
+ this.rating = rating;
+ this.created = created;
+ this.isNew = true;
}
- @Transient
- @EqualsAndHashCode.Exclude
- private boolean isNew;
+ @Override
+ public Long getId() {
+ return id;
+ }
@Override
public boolean isNew() {
return isNew;
}
- public void setNew(boolean isNew) {
+ public void setIsNew(boolean isNew) {
this.isNew = isNew;
}
+
+ public long getBookId() {
+ return bookId;
+ }
+
+ public String getReader() {
+ return reader;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public long getRationg() {
+ return rating;
+ }
+
+ public Instant getCreated() {
+ return created;
+ }
+
+ public void setBookId(long bookId) {
+ this.bookId = bookId;
+ }
+
+ public void setReader(String reader) {
+ this.reader = reader;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public void setRating(long rating) {
+ this.rating = rating;
+ }
+
+ public void setCreated(Instant created) {
+ this.created = created;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, bookId, reader, text, rating, created);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null || obj.getClass() != getClass()) {
+ return false;
+ }
+
+ Review other = (Review) obj;
+ return Objects.equals(id, other.id)
+ && bookId == other.bookId
+ && Objects.equals(reader, other.reader)
+ && Objects.equals(text, other.text)
+ && rating == other.rating
+ && Objects.equals(created, other.created);
+ }
}