Skip to content

Commit

Permalink
#117: More consistent column naming for subobject fields
Browse files Browse the repository at this point in the history
Instead of the legacy YOJ 1.x and 2.x strategy that adds subobject column name as prefix for subobjects' column names
**unless an explicit `Column#name()` annotation is set**, introduce two more consistent strategies:
- Always add subobject column name as prefix (`ColumnNaming.RELATIVE`)
- Never add subobject column name as prefix, treat subobject column as top-level (`ColumnNaming.ABSOLUTE`)
with the more generally applicable `ColumnNaming.RELATIVE` strategy becoming the default in YOJ 3.0.
  • Loading branch information
Andrey Alekseev authored and nvamelichev committed Jan 20, 2025
1 parent 7a6a38f commit 3e9fdde
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 9 deletions.
34 changes: 33 additions & 1 deletion databind/src/main/java/tech/ydb/yoj/databind/schema/Column.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
* </li>
* Defaults to {@code true} (flatten composite fields).<br>
* Changing this parameter for a non-composite field has no effect.
* <p><strong>Tip:</strong> Use the {@link ObjectColumn @ObjectColumn} annotation
* <p><strong>Tip:</strong> 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;
Expand All @@ -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}.
* <br>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.
* <br>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 <strong>only</strong> if current (child) doesn't specify {@link #name()} explicitly.
* <br>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 (<strong>even if there is a parent column!</strong>)
*/
ABSOLUTE,
/**
* <strong>Always</strong> uses parent column name (if any) as prefix: {@code [<name for parent column>_]<name for this column>}.
* <br>This policy will become the default in YOJ 3.0.
*/
RELATIVE
}
}
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,22 @@ public class MixedEntity {

TestEntity.SubEntity subEntity;

SubEntityWithRelative relativeWithoutColumnAnnotation;

@Column(name = "prefix")
SubEntityWithRelative relativeWithPrefix;

@Value
private static class Id {
String stringValue;

@Column(name = "int.val")
Integer intValue;
}

@Value
static class SubEntityWithRelative {
@Column(name = "sfe_relative", columnNaming = Column.ColumnNaming.RELATIVE)
SingleFieldEntity singleFieldEntityRelative;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

0 comments on commit 3e9fdde

Please sign in to comment.