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

org.babyfish.jimmer.sql.runtime.ScalarProvider Maven / Gradle / Ivy

There is a newer version: 0.9.19
Show newest version
package org.babyfish.jimmer.sql.runtime;

import org.apache.commons.lang3.reflect.TypeUtils;
import org.babyfish.jimmer.impl.util.ClassCache;
import org.babyfish.jimmer.meta.ImmutableProp;
import org.babyfish.jimmer.meta.TypedProp;
import org.babyfish.jimmer.sql.Embeddable;
import org.babyfish.jimmer.sql.Entity;
import org.babyfish.jimmer.sql.MappedSuperclass;
import org.babyfish.jimmer.sql.ast.impl.TupleImplementor;
import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.*;
import java.util.function.Consumer;

public interface ScalarProvider {

    @NotNull
    default Type getScalarType() {
        return Meta.of(this.getClass()).scalarType;
    }

    @SuppressWarnings("unchecked")
    @NotNull
    default Class getSqlType() {
        return (Class) Meta.of(this.getClass()).sqlType;
    }

    T toScalar(@NotNull S sqlValue) throws Exception;

    S toSql(@NotNull T scalarValue) throws Exception;

    /**
     * User can override this method, it can return null, empty or handled property.
     * 
    *
  • Null or empty: Global scalar provider, can be applied to any properties
  • *
  • Otherwise: Property-specific scalar provider
  • *
* *

Actually, there are two ways to add property-specific scalar providers

*
    *
  • Override {@link #getHandledProps()}
  • *
  • Use {@link org.babyfish.jimmer.sql.JSqlClient.Builder#setScalarProvider(ImmutableProp, ScalarProvider)} or * {@link org.babyfish.jimmer.sql.JSqlClient.Builder#setScalarProvider(TypedProp, ScalarProvider)}
  • *
* @return Null or handled property. */ default Collection getHandledProps() { return null; } default boolean isJsonScalar() { return false; } default Reader reader() { return null; } static > ScalarProvider enumProviderByString( Class enumType ) { return enumProviderByString(enumType, null); } static > ScalarProvider enumProviderByString( Class enumType, Consumer> block ) { EnumProviderBuilder builder = EnumProviderBuilder.of(enumType, String.class, Enum::name); if (block != null) { block.accept(builder); } return builder.build(); } static > ScalarProvider enumProviderByInt( Class enumType ) { return enumProviderByInt(enumType, null); } static > ScalarProvider enumProviderByInt( Class enumType, Consumer> block ) { EnumProviderBuilder builder = EnumProviderBuilder.of(enumType, Integer.class, Enum::ordinal); if (block != null) { block.accept(builder); } return builder.build(); } static ScalarProvider uuidByByteArray() { return AbstractScalarProvider.UUID_BY_BYTE_ARRAY; } static ScalarProvider uuidByString() { return AbstractScalarProvider.UUID_BY_STRING; } class Meta { private static final ClassCache META_CACHE = new ClassCache<>(Meta::create); final Type scalarType; final Class sqlType; private Meta(Type scalarType, Class sqlType) { this.scalarType = scalarType; this.sqlType = sqlType; } public static Meta of(Class scalarProviderType) { return META_CACHE.get(scalarProviderType); } static Meta create(Class scalarProviderType) { if (!ScalarProvider.class.isAssignableFrom(scalarProviderType)) { throw new IllegalArgumentException( "The `scalarProviderType` \"" + scalarProviderType.getName() + "\" + does not implement \"" + ScalarProvider.class.getName() + "\"" ); } Map, Type> argMap = TypeUtils.getTypeArguments(scalarProviderType, ScalarProvider.class); if (argMap.isEmpty()) { throw new IllegalStateException( "Illegal type \"" + scalarProviderType.getName() + "\", it does not specify generic arguments for \"" + ScalarProvider.class.getName() + "\"" ); } TypeVariable[] params = ScalarProvider.class.getTypeParameters(); Type scalarType = argMap.get(params[0]); Class sqlType = (Class) argMap.get(params[1]); validateScalarType(scalarType); return new Meta(scalarType, sqlType); } static void validateScalarType(Type scalarType) { if (scalarType == UUID.class) { return; // UUID is standard type, but it can be overridden by ScalarProvider } if (!(scalarType instanceof Class)) { return; } Class scalarClass = (Class) scalarType; if (scalarType == void.class) { throw new IllegalArgumentException( "Illegal scalar type \"" + scalarClass.getName() + "\", it cannot be void" ); } if (scalarType == Object.class) { throw new IllegalArgumentException( "Illegal scalar type \"" + scalarClass.getName() + "\", scalar provider does not support object type which means any" ); } if (TupleImplementor.class.isAssignableFrom(scalarClass)) { throw new IllegalArgumentException( "Illegal scalar type \"" + scalarClass.getName() + "\", scalar provider does not support tuple type" ); } if (ReaderManager.isStandardScalarType(scalarClass)) { throw new IllegalArgumentException( "Illegal scalar type \"" + ((Class)scalarType).getName() + "\", scalar provider does not support standard scalar type" ); } Class annotationType = getOrmAnnotationType(scalarClass); if (annotationType != null) { throw new IllegalArgumentException( "Illegal scalar type \"" + scalarClass.getName() + "\", scalar provider does not support scalar type which is decorated by \"@" + annotationType.getName() + "\"" ); } } private static Class getOrmAnnotationType(Class type) { if (type == null) { return null; } if (type != Object.class) { if (type.isAnnotationPresent(Entity.class)) { return Entity.class; } if (type.isAssignableFrom(MappedSuperclass.class)) { return MappedSuperclass.class; } if (type.isAssignableFrom(Embeddable.class)) { return Embeddable.class; } } Class annoType = getOrmAnnotationType(type.getSuperclass()); if (annoType != null) { return annoType; } for (Class interfaceType : type.getInterfaces()) { annoType = getOrmAnnotationType(interfaceType); if (annoType != null) { return annoType; } } return null; } } }