diff --git a/databind/src/main/java/tech/ydb/yoj/databind/schema/Column.java b/databind/src/main/java/tech/ydb/yoj/databind/schema/Column.java
index da1a3a4b..1259f4d8 100644
--- a/databind/src/main/java/tech/ydb/yoj/databind/schema/Column.java
+++ b/databind/src/main/java/tech/ydb/yoj/databind/schema/Column.java
@@ -68,7 +68,7 @@
*
* Defaults to {@code true} (flatten composite fields).
* Changing this parameter for a non-composite field has no effect.
- *
Tip: Use the {@link ObjectColumn @ObjectColumn} annotation + *
Tip: Use the {@link tech.ydb.yoj.databind.converter.ObjectColumn @ObjectColumn} annotation
* if you only need to override {@code @Column.flatten} to {@code false}.
*/
boolean flatten() default true;
@@ -77,7 +77,39 @@
* Specifies custom conversion logic for this column, if any.
*
* @see CustomValueType
+ * @see tech.ydb.yoj.databind.converter.ValueConverter ValueConverter
*/
@ExperimentalApi(issue = "https://github.com/ydb-platform/yoj-project/issues/24")
CustomValueType customValueType() default @CustomValueType(columnClass = Comparable.class, converter = NoConverter.class);
+
+ /**
+ * Column naming policy, used by {@link tech.ydb.yoj.databind.schema.naming.AnnotationFirstNamingStrategy the default naming strategy}.
+ *
Determines how the column name is derived from this column's name and its parent column, if any.
+ */
+ ColumnNaming columnNaming() default ColumnNaming.LEGACY;
+
+ /**
+ * Column naming policy.
+ *
Determines how the column name is derived from this column's name and its parent column, if any.
+ */
+ enum ColumnNaming {
+ /**
+ * Adds parent column name (if any) as prefix, but only if current (child) doesn't specify {@link #name()} explicitly.
+ *
It works both like {@link #RELATIVE} and {@link #ABSOLUTE} policy, depending on the presence of {@link #name()} annotation attribute
+ * for this column.
+ *
+ * @deprecated This column naming policy is deprecated, but will stay the default in YOJ 2.x.
+ * {@link #RELATIVE} will become the default policy in YOJ 3.0.
+ */
+ @Deprecated LEGACY,
+ /**
+ * Uses this column's name as-is, without ever using consulting parent column's name (even if there is a parent column!)
+ */
+ ABSOLUTE,
+ /**
+ * Always uses parent column name (if any) as prefix: {@code [
This policy will become the default in YOJ 3.0.
+ */
+ RELATIVE
+ }
}
diff --git a/databind/src/main/java/tech/ydb/yoj/databind/schema/naming/AnnotationFirstNamingStrategy.java b/databind/src/main/java/tech/ydb/yoj/databind/schema/naming/AnnotationFirstNamingStrategy.java
index dbba9ba2..b245de69 100644
--- a/databind/src/main/java/tech/ydb/yoj/databind/schema/naming/AnnotationFirstNamingStrategy.java
+++ b/databind/src/main/java/tech/ydb/yoj/databind/schema/naming/AnnotationFirstNamingStrategy.java
@@ -1,6 +1,7 @@
package tech.ydb.yoj.databind.schema.naming;
import lombok.NonNull;
+import tech.ydb.yoj.databind.schema.Column;
import tech.ydb.yoj.databind.schema.Schema;
import tech.ydb.yoj.databind.schema.Table;
@@ -30,17 +31,34 @@ protected String getNameFromClass(Class> entityClass) {
private String getColumnName(Schema.JavaField field) {
var annotation = field.getField().getColumn();
- if (annotation != null && !annotation.name().isEmpty()) {
- return annotation.name();
+ var parentName = (field.getParent() == null) ? null : field.getParent().getName();
+
+ if (annotation == null) {
+ var fieldName = field.getField().getName();
+ return parentName == null ? fieldName : parentName + NAME_DELIMITER + fieldName;
}
- return getColumnNameFromField(field);
+ return getColumnNameWithNaming(field, annotation.name(), parentName, annotation.columnNaming());
}
- protected String getColumnNameFromField(Schema.JavaField field) {
- return (field.getParent() == null)
- ? field.getField().getName()
- : field.getParent().getName() + NAME_DELIMITER + field.getField().getName();
+ private static String getColumnNameWithNaming(
+ Schema.JavaField field,
+ String annotationName,
+ String parentName, Column.ColumnNaming columnNaming
+ ) {
+ var name = (annotationName.isEmpty()) ? field.getField().getName() : annotationName;
+
+ return switch (columnNaming) {
+ case ABSOLUTE -> name;
+ case RELATIVE -> parentName + NAME_DELIMITER + name;
+ case LEGACY -> {
+ if (!annotationName.isEmpty()) {
+ yield annotationName;
+ } else {
+ yield parentName == null ? name : parentName + NAME_DELIMITER + name;
+ }
+ }
+ };
}
protected void propagateFieldNameToFlatSubfield(Schema.JavaField field) {
diff --git a/databind/src/test/java/tech/ydb/yoj/databind/schema/naming/AnnotationFirstNamingStrategyTest.java b/databind/src/test/java/tech/ydb/yoj/databind/schema/naming/AnnotationFirstNamingStrategyTest.java
index 472d7d0e..7fba9cab 100644
--- a/databind/src/test/java/tech/ydb/yoj/databind/schema/naming/AnnotationFirstNamingStrategyTest.java
+++ b/databind/src/test/java/tech/ydb/yoj/databind/schema/naming/AnnotationFirstNamingStrategyTest.java
@@ -37,7 +37,7 @@ public void testMixedEntityTableName() {
@Test
public void testMixedEntityFieldNameTest() {
verifyFieldNames(MixedEntity.class,
- "column_name", "subEntity_boolValue", "sfe_timestamp", "id_stringValue", "int.val");
+ "column_name", "subEntity_boolValue", "absoluteBoolValue", "sfe_timestamp", "id_stringValue", "int.val", "prefix_sfe_relative_timestamp", "sfe_absolute_timestamp", "relativeWithoutColumnAnnotation_sfe_relative_timestamp");
}
@Override
diff --git a/databind/src/test/java/tech/ydb/yoj/databind/schema/naming/MixedEntity.java b/databind/src/test/java/tech/ydb/yoj/databind/schema/naming/MixedEntity.java
index 5c8f60d8..2345feb4 100644
--- a/databind/src/test/java/tech/ydb/yoj/databind/schema/naming/MixedEntity.java
+++ b/databind/src/test/java/tech/ydb/yoj/databind/schema/naming/MixedEntity.java
@@ -12,6 +12,11 @@ public class MixedEntity {
TestEntity.SubEntity subEntity;
+ SubEntityWithRelative relativeWithoutColumnAnnotation;
+
+ @Column(name = "prefix")
+ SubEntityWithRelative relativeWithPrefix;
+
@Value
private static class Id {
String stringValue;
@@ -19,4 +24,10 @@ private static class Id {
@Column(name = "int.val")
Integer intValue;
}
+
+ @Value
+ static class SubEntityWithRelative {
+ @Column(name = "sfe_relative", columnNaming = Column.ColumnNaming.RELATIVE)
+ SingleFieldEntity singleFieldEntityRelative;
+ }
}
diff --git a/databind/src/test/java/tech/ydb/yoj/databind/schema/naming/TestEntity.java b/databind/src/test/java/tech/ydb/yoj/databind/schema/naming/TestEntity.java
index 8eac83e8..6324bbe7 100644
--- a/databind/src/test/java/tech/ydb/yoj/databind/schema/naming/TestEntity.java
+++ b/databind/src/test/java/tech/ydb/yoj/databind/schema/naming/TestEntity.java
@@ -18,7 +18,13 @@ private static class Id {
static class SubEntity {
boolean boolValue;
+ @Column(columnNaming = Column.ColumnNaming.ABSOLUTE)
+ boolean absoluteBoolValue;
+
@Column(name = "sfe")
SingleFieldEntity singleFieldEntity;
+
+ @Column(name = "sfe_absolute", columnNaming = Column.ColumnNaming.ABSOLUTE)
+ SingleFieldEntity singleFieldEntityAbsolute;
}
}