org.babyfish.jimmer.sql.runtime.ScalarProvider Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jimmer-sql Show documentation
Show all versions of jimmer-sql Show documentation
A revolutionary ORM framework for both java and kotlin
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;
}
}
}