All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tech.ydb.yoj.repository.db.EntityIdSchema Maven / Gradle / Ivy

Go to download

Core YOJ (YDB ORM for Java) abstractions and APIs for domain entities, repositories, transactions etc.

There is a newer version: 2.6.1
Show newest version
package tech.ydb.yoj.repository.db;

import com.google.common.base.Preconditions;
import com.google.common.reflect.TypeToken;
import lombok.NonNull;
import tech.ydb.yoj.databind.CustomValueTypes;
import tech.ydb.yoj.databind.FieldValueType;
import tech.ydb.yoj.databind.schema.Schema;
import tech.ydb.yoj.databind.schema.configuration.SchemaRegistry;
import tech.ydb.yoj.databind.schema.configuration.SchemaRegistry.SchemaKey;
import tech.ydb.yoj.databind.schema.naming.NamingStrategy;
import tech.ydb.yoj.databind.schema.reflect.ReflectField;

import javax.annotation.Nullable;
import java.lang.reflect.Type;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static tech.ydb.yoj.databind.FieldValueType.BOOLEAN;
import static tech.ydb.yoj.databind.FieldValueType.BYTE_ARRAY;
import static tech.ydb.yoj.databind.FieldValueType.ENUM;
import static tech.ydb.yoj.databind.FieldValueType.INTEGER;
import static tech.ydb.yoj.databind.FieldValueType.STRING;
import static tech.ydb.yoj.databind.FieldValueType.TIMESTAMP;
import static tech.ydb.yoj.databind.FieldValueType.UUID;
import static tech.ydb.yoj.databind.schema.naming.NamingStrategy.NAME_DELIMITER;

public final class EntityIdSchema> extends Schema implements Comparator {
    /**
     * {@link JavaField#getName() Name} and also {@link JavaField#getPath() path} of Entity's {@link Entity#getId() ID}
     * field. Use this constant if you need to reference ID field in a query, filter or order expression.
* If you need to check whether the specified field is a [part of] ID field, use the {@link #isIdField(JavaField)}, * {@link #isIdFieldPath(String)} and {@link #isIdFieldName(String)} methods instead. * * @see #isIdField(JavaField) * @see #isIdFieldPath(String) * @see #isIdFieldName(String) */ public static final String ID_FIELD_NAME = "id"; private static final String ID_SUBFIELD_PATH_PREFIX = ID_FIELD_NAME + PATH_DELIMITER; private static final String ID_SUBFIELD_NAME_PREFIX = ID_FIELD_NAME + NAME_DELIMITER; public static final Comparator> SORT_ENTITY_BY_ID = Comparator.comparing( Entity::getId, (a, b) -> EntityIdSchema.ofEntity(a.getType()).compare(a, b) ); public static > Comparator> getIdComparator(Class type) { return Comparator.comparing( id -> id, (a, b) -> EntityIdSchema.ofEntity(type).compare(a, b) ); } private static final Type ENTITY_TYPE_PARAMETER = Entity.Id.class.getTypeParameters()[0]; private static final Set ALLOWED_ID_FIELD_TYPES = Set.of( STRING, INTEGER, ENUM, BOOLEAN, TIMESTAMP, UUID, BYTE_ARRAY ); private > EntityIdSchema(EntitySchema entitySchema) { super(entitySchema, ID_FIELD_NAME); var flattenedFields = flattenFields(); var idField = entitySchema.getField(ID_FIELD_NAME); if (idField.getValueType().isComposite()) { Preconditions.checkArgument(!flattenedFields.isEmpty(), "ID must have at least 1 field, but got none: %s", idField.getType()); } else { Preconditions.checkArgument( idField.getCustomValueTypeInfo() != null, "ID must be either a composite with >= 1 field, or a compatible type annotated with @CustomValueType, but got: %s", idField.getType() ); } flattenedFields.stream() .filter(f -> !ALLOWED_ID_FIELD_TYPES.contains(FieldValueType.forSchemaField(f))) .findAny() .ifPresent(f -> { throw new IllegalArgumentException(String.format( "Leaf ID field \"[%s].%s\" is none of the allowed types %s", getType().getName(), f.getName(), f.getType(), FieldValueType.forSchemaField(f), ALLOWED_ID_FIELD_TYPES)); }); } @Override protected boolean isFlattenable(ReflectField field) { return true; } public static , ID extends Entity.Id> EntityIdSchema ofEntity(Class entityType) { return ofEntity(entityType, null); } /** * @param namingStrategy naming strategy with mandatory equals/hashCode. */ public static , ID extends Entity.Id> EntityIdSchema ofEntity( Class entityType, NamingStrategy namingStrategy) { return ofEntity(SchemaRegistry.getDefault(), entityType, namingStrategy); } /** * @param namingStrategy naming strategy with mandatory equals/hashCode. */ public static , ID extends Entity.Id> EntityIdSchema ofEntity( SchemaRegistry registry, Class entityType, NamingStrategy namingStrategy) { EntitySchema entitySchema = EntitySchema.of(registry, entityType, namingStrategy); return from(entitySchema); } public static , ID extends Entity.Id> EntityIdSchema of(Class idType) { return of(idType, null); } /** * @param namingStrategy naming strategy with mandatory equals/hashCode. */ public static , ID extends Entity.Id> EntityIdSchema of( Class idType, NamingStrategy namingStrategy) { return of(SchemaRegistry.getDefault(), idType, namingStrategy); } public static , ID extends Entity.Id> EntityIdSchema of(SchemaRegistry registry, Class idType) { return of(registry, idType, null); } /** * @param namingStrategy naming strategy with mandatory equals/hashCode. */ public static , ID extends Entity.Id> EntityIdSchema of( SchemaRegistry registry, Class idType, NamingStrategy namingStrategy) { @SuppressWarnings("unchecked") Class entityType = (Class) resolveEntityType(idType); EntitySchema entitySchema = EntitySchema.of(registry, entityType, namingStrategy); return from(entitySchema); } public static , ID extends Entity.Id> EntityIdSchema from(EntitySchema entitySchema) { var key = SchemaKey.of(entitySchema.getType(), entitySchema.getNamingStrategy()); return entitySchema.getRegistry().getOrCreate(EntityIdSchema.class, (k, r) -> new EntityIdSchema<>(entitySchema), key); } static Class resolveEntityType(Class idType) { return TypeToken.of(idType).resolveType(ENTITY_TYPE_PARAMETER).getRawType(); } public static boolean isIdField(@NonNull JavaField field) { return isIdFieldPath(field.getPath()); } public static boolean isIdFieldPath(@NonNull String path) { return path.equals(ID_FIELD_NAME) || path.startsWith(ID_SUBFIELD_PATH_PREFIX); } public static boolean isIdFieldName(@NonNull String name) { return name.equals(ID_FIELD_NAME) || name.startsWith(ID_SUBFIELD_NAME_PREFIX); } @Override public int compare(@NonNull ID a, @NonNull ID b) { Map idA = flatten(a); Map idB = flatten(b); List flatFields = flattenFields(); for (JavaField field : flatFields) { var fieldName = field.getName(); @SuppressWarnings("unchecked") int res = compare(toComparable(idA.get(fieldName), field), toComparable(idB.get(fieldName), field)); if (res != 0) { return res; } } return 0; } @Nullable @SuppressWarnings("rawtypes") private static Comparable toComparable(@Nullable Object value, @NonNull JavaField field) { if (value == null) { return null; } value = CustomValueTypes.preconvert(field, value); if (value instanceof Enum) { return ((Enum) value).name(); } else if (value instanceof Comparable) { // String, Instant, Boolean return (Comparable) value; } else if (value instanceof Number) { return ((Number) value).longValue(); } throw new IllegalArgumentException("ID fields must implement Comparable"); } private static > int compare(@Nullable E a, @Nullable E b) { // nulls first if (a == null && b == null) { return 0; } else if (a == null /* && b != null */) { return -1; } else if (/* a != null && */ b == null) { return 1; } else /* if (a != null && b != null) */ { return a.compareTo(b); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy