Non-{@code public} (e.g., package-private) classes, interfaces, methods, fields, constants etc. in YOJ are assumed 21 | * to be internal implementation details regardless of the presence of an {@code @InternalApi} annotation on them. 22 | */ 23 | @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE, MODULE, RECORD_COMPONENT}) 24 | @Retention(SOURCE) 25 | public @interface InternalApi { 26 | } 27 | -------------------------------------------------------------------------------- /databind/src/main/java/tech/ydb/yoj/databind/converter/ObjectColumn.java: -------------------------------------------------------------------------------- 1 | package tech.ydb.yoj.databind.converter; 2 | 3 | import tech.ydb.yoj.databind.schema.Column; 4 | 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.Target; 7 | 8 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 9 | import static java.lang.annotation.ElementType.FIELD; 10 | import static java.lang.annotation.ElementType.RECORD_COMPONENT; 11 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 12 | 13 | /** 14 | * Signifies that the field value is stored in a single database column in an opaque serialized form 15 | * (i.e., individual fields cannot be directly accessed by data binding). 16 | * 17 | * @see tech.ydb.yoj.databind.FieldValueType#OBJECT 18 | */ 19 | @Column(flatten = false) 20 | @Target({FIELD, RECORD_COMPONENT, ANNOTATION_TYPE}) 21 | @Retention(RUNTIME) 22 | public @interface ObjectColumn { 23 | } 24 | -------------------------------------------------------------------------------- /databind/src/main/java/tech/ydb/yoj/databind/converter/StringColumn.java: -------------------------------------------------------------------------------- 1 | package tech.ydb.yoj.databind.converter; 2 | 3 | import tech.ydb.yoj.databind.CustomValueType; 4 | import tech.ydb.yoj.databind.schema.Column; 5 | 6 | import java.lang.annotation.Inherited; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.Target; 9 | 10 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 11 | import static java.lang.annotation.ElementType.FIELD; 12 | import static java.lang.annotation.ElementType.RECORD_COMPONENT; 13 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 14 | 15 | /** 16 | * Annotation for fields that should be stored as text in the database. 17 | *
14 | * Example: 15 | * 16 | * @GlobalIndex(name="views_index", fields = {"name", "type"}) 17 | * public class Customer { ... } 18 | *19 | */ 20 | @Target(TYPE) 21 | @Retention(RUNTIME) 22 | @Repeatable(GlobalIndexes.class) 23 | public @interface GlobalIndex { 24 | /** 25 | * Index name. 26 | */ 27 | String name(); 28 | 29 | /** 30 | * List of annotated class fields representing index columns. 31 | */ 32 | String[] fields(); 33 | 34 | /** 35 | * Index type 36 | */ 37 | Type type() default Type.GLOBAL; 38 | 39 | enum Type { 40 | GLOBAL, 41 | UNIQUE 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /databind/src/main/java/tech/ydb/yoj/databind/schema/GlobalIndexes.java: -------------------------------------------------------------------------------- 1 | package tech.ydb.yoj.databind.schema; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.TYPE; 7 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 8 | 9 | @Target(TYPE) 10 | @Retention(RUNTIME) 11 | public @interface GlobalIndexes { 12 | GlobalIndex[] value(); 13 | } 14 | -------------------------------------------------------------------------------- /databind/src/main/java/tech/ydb/yoj/databind/schema/ObjectSchema.java: -------------------------------------------------------------------------------- 1 | package tech.ydb.yoj.databind.schema; 2 | 3 | import tech.ydb.yoj.databind.schema.configuration.SchemaRegistry; 4 | import tech.ydb.yoj.databind.schema.configuration.SchemaRegistry.SchemaKey; 5 | import tech.ydb.yoj.databind.schema.reflect.Reflector; 6 | 7 | public final class ObjectSchema
12 | * Example: 13 | * 14 | * @TTL(field = "createdAt", interval = "PT12H") 15 | * public class LogEntry { ... } 16 | *17 | */ 18 | @Target(ElementType.TYPE) 19 | @Retention(RetentionPolicy.RUNTIME) 20 | public @interface TTL { 21 | /** 22 | * Field, which will be used to calculate if the row might be deleted 23 | * Accepted dbTypes of columns: 24 | *
15 | * Example: 16 | * 17 | * @Table(name="CUSTOMERS") 18 | * public class Customer { ... } 19 | *20 | * 21 | */ 22 | @Target(TYPE) 23 | @Retention(RUNTIME) 24 | public @interface Table { 25 | 26 | /** 27 | * The name of the table. Defaults to the type name. 28 | */ 29 | String name(); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /databind/src/main/java/tech/ydb/yoj/databind/schema/naming/DelegateNamingStrategy.java: -------------------------------------------------------------------------------- 1 | package tech.ydb.yoj.databind.schema.naming; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.EqualsAndHashCode; 5 | import lombok.experimental.Delegate; 6 | 7 | @AllArgsConstructor 8 | @EqualsAndHashCode 9 | public class DelegateNamingStrategy implements NamingStrategy { 10 | @Delegate 11 | protected final NamingStrategy delegate; 12 | } 13 | -------------------------------------------------------------------------------- /databind/src/main/java/tech/ydb/yoj/databind/schema/naming/NamingStrategy.java: -------------------------------------------------------------------------------- 1 | package tech.ydb.yoj.databind.schema.naming; 2 | 3 | import lombok.NonNull; 4 | import tech.ydb.yoj.databind.schema.Schema.JavaField; 5 | 6 | public interface NamingStrategy { 7 | String NAME_DELIMITER = "_"; 8 | 9 | String getNameForClass(@NonNull Class> entityClass); 10 | 11 | /** 12 | * Assigns a name to a field in a schema. 13 | */ 14 | void assignFieldName(@NonNull JavaField javaField); 15 | } 16 | -------------------------------------------------------------------------------- /databind/src/main/java/tech/ydb/yoj/databind/schema/reflect/KotlinReflectionDetector.java: -------------------------------------------------------------------------------- 1 | package tech.ydb.yoj.databind.schema.reflect; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | /*package*/ final class KotlinReflectionDetector { 7 | private static final Logger log = LoggerFactory.getLogger(KotlinReflectionDetector.class); 8 | 9 | private KotlinReflectionDetector() { 10 | } 11 | 12 | public static final boolean kotlinAvailable = detectKotlinReflection(); 13 | 14 | private static boolean detectKotlinReflection() { 15 | var cl = classLoader(); 16 | 17 | try { 18 | Class.forName("kotlin.Metadata", false, cl); 19 | } catch (ClassNotFoundException e) { 20 | return false; 21 | } 22 | 23 | try { 24 | Class.forName("kotlin.reflect.full.KClasses", false, cl); 25 | return true; 26 | } catch (ClassNotFoundException e) { 27 | log.warn("YOJ has detected Kotlin but not kotlin-reflect. Kotlin data classes won't work as Entities."); 28 | return false; 29 | } 30 | } 31 | 32 | private static ClassLoader classLoader() { 33 | ClassLoader cl = null; 34 | try { 35 | cl = Thread.currentThread().getContextClassLoader(); 36 | } catch (Exception ignore) { 37 | } 38 | if (cl == null) { 39 | cl = KotlinDataClassType.class.getClassLoader(); 40 | if (cl == null) { 41 | try { 42 | cl = ClassLoader.getSystemClassLoader(); 43 | } catch (Exception ignore) { 44 | } 45 | } 46 | } 47 | return cl; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /databind/src/main/java/tech/ydb/yoj/databind/schema/reflect/PojoField.java: -------------------------------------------------------------------------------- 1 | package tech.ydb.yoj.databind.schema.reflect; 2 | 3 | import com.google.common.base.Preconditions; 4 | import lombok.NonNull; 5 | import tech.ydb.yoj.databind.schema.FieldValueException; 6 | 7 | import javax.annotation.Nullable; 8 | 9 | /** 10 | * Represents a field of a POJO class, hand-written or generated e.g. by Lombok. 11 | */ 12 | /*package*/ final class PojoField extends ReflectFieldBase { 13 | private final java.lang.reflect.Field delegate; 14 | 15 | public PojoField(@NonNull Reflector reflector, @NonNull java.lang.reflect.Field delegate) { 16 | super(reflector, delegate.getName(), delegate.getGenericType(), delegate.getType(), delegate); 17 | 18 | Preconditions.checkArgument(!delegate.isSynthetic(), 19 | "Encountered a synthetic field, did you forget to declare the ID class as static? Field is: %s", delegate); 20 | this.delegate = delegate; 21 | this.delegate.setAccessible(true); 22 | } 23 | 24 | @Nullable 25 | @Override 26 | public Object getValue(Object containingObject) { 27 | try { 28 | return delegate.get(containingObject); 29 | } catch (Exception e) { 30 | throw new FieldValueException(e, getName(), containingObject); 31 | } 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "PojoField[" + getGenericType().getTypeName() + "::" + getName() + "]"; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /databind/src/main/java/tech/ydb/yoj/databind/schema/reflect/RecordField.java: -------------------------------------------------------------------------------- 1 | package tech.ydb.yoj.databind.schema.reflect; 2 | 3 | import lombok.NonNull; 4 | import tech.ydb.yoj.databind.schema.FieldValueException; 5 | 6 | import javax.annotation.Nullable; 7 | 8 | /** 9 | * Represents a record class component for the purposes of YOJ data-binding. 10 | */ 11 | /*package*/ final class RecordField extends ReflectFieldBase { 12 | private final java.lang.reflect.Method accessor; 13 | 14 | public RecordField(@NonNull Reflector reflector, @NonNull java.lang.reflect.RecordComponent delegate) { 15 | super(reflector, delegate.getName(), delegate.getGenericType(), delegate.getType(), delegate); 16 | 17 | this.accessor = delegate.getAccessor(); 18 | accessor.setAccessible(true); 19 | } 20 | 21 | @Nullable 22 | @Override 23 | public Object getValue(Object containingObject) { 24 | try { 25 | return accessor.invoke(containingObject); 26 | } catch (Exception e) { 27 | throw new FieldValueException(e, getName(), containingObject); 28 | } 29 | } 30 | 31 | @Override 32 | public String toString() { 33 | return "RecordField[" + getGenericType().getTypeName() + "::" + getName() + "]"; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /databind/src/main/java/tech/ydb/yoj/databind/schema/reflect/ReflectFieldBase.java: -------------------------------------------------------------------------------- 1 | package tech.ydb.yoj.databind.schema.reflect; 2 | 3 | import lombok.Getter; 4 | import tech.ydb.yoj.databind.CustomValueTypes; 5 | import tech.ydb.yoj.databind.FieldValueType; 6 | import tech.ydb.yoj.databind.schema.Column; 7 | import tech.ydb.yoj.databind.schema.CustomValueTypeInfo; 8 | import tech.ydb.yoj.util.lang.Annotations; 9 | 10 | import java.lang.reflect.AnnotatedElement; 11 | import java.lang.reflect.Type; 12 | 13 | @Getter 14 | public abstract class ReflectFieldBase implements ReflectField { 15 | private final String name; 16 | 17 | private final Type genericType; 18 | private final Class> type; 19 | 20 | private final Column column; 21 | private final CustomValueTypeInfo, ?> customValueTypeInfo; 22 | 23 | private final FieldValueType valueType; 24 | 25 | private final ReflectType> reflectType; 26 | 27 | protected ReflectFieldBase(Reflector reflector, 28 | String name, 29 | Type genericType, Class> type, 30 | AnnotatedElement component) { 31 | this.name = name; 32 | this.genericType = genericType; 33 | this.type = type; 34 | this.column = Annotations.find(Column.class, component); 35 | this.customValueTypeInfo = CustomValueTypes.getCustomValueTypeInfo(genericType, column); 36 | this.valueType = FieldValueType.forJavaType(genericType, column, customValueTypeInfo); 37 | this.reflectType = reflector.reflectFieldType(genericType, valueType); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /databind/src/main/java/tech/ydb/yoj/databind/schema/reflect/ReflectType.java: -------------------------------------------------------------------------------- 1 | package tech.ydb.yoj.databind.schema.reflect; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.util.List; 5 | 6 | /** 7 | * Basic reflection information for the specified raw type. 8 | * 9 | * @param
To use the new {@code Spliterator} contract-conformant implementation, set {@code ReadTableParams.builder().<...>.useNewSpliterator(true)}. 13 | *
Note that using the new implementation currently has a negative performance impact, for more information refer to
14 | * GitHub Issue #42.
15 | */
16 | @Deprecated
17 | @InternalApi
18 | public class YdbLegacySpliterator > {
10 | String getType();
11 |
12 | int getPriority();
13 |
14 | default Please use {@link #of(JavaField) YqlType.of(JavaField)} because it correcly
16 | * respects the customizations specified in the {@link Column @Column} annotation.
17 | */
18 | @NonNull
19 | @Deprecated(forRemoval = true)
20 | static YqlPrimitiveType of(Type javaType) {
21 | return YqlPrimitiveType.of(javaType);
22 | }
23 |
24 | /**
25 | * Returns the Yql type of the column.
26 | *
27 | * If the {@link Column} annotation is specified for the {@code column} field,
28 | * the annotation field {@code dbType} may be used to specify the column type.
29 | *
30 | * @return the Yql type of the column
31 | */
32 | @NonNull
33 | static YqlPrimitiveType of(JavaField column) {
34 | return YqlPrimitiveType.of(column);
35 | }
36 |
37 | String getYqlTypeName();
38 |
39 | ValueProtos.Value.Builder toYql(Object value);
40 |
41 | Object fromYql(ValueProtos.Value value);
42 | }
43 |
--------------------------------------------------------------------------------
/repository-ydb-v2/src/test/java/tech/ydb/yoj/repository/ydb/NonSerializableEntity.java:
--------------------------------------------------------------------------------
1 | package tech.ydb.yoj.repository.ydb;
2 |
3 | import lombok.NonNull;
4 | import lombok.Value;
5 | import tech.ydb.yoj.databind.schema.Column;
6 | import tech.ydb.yoj.repository.db.Entity;
7 |
8 | @Value
9 | public class NonSerializableEntity implements Entity