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

tech.ydb.yoj.databind.converter.StringValueConverter Maven / Gradle / Ivy

Go to download

Core data-binding logic used by YOJ (YDB ORM for Java) to convert between Java objects and database rows (or anything representable by a Java Map, really).

The newest version!
package tech.ydb.yoj.databind.converter;

import lombok.NonNull;
import lombok.SneakyThrows;
import tech.ydb.yoj.databind.schema.Schema.JavaField;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Function;

import static java.lang.String.format;
import static java.lang.reflect.Modifier.isStatic;

/**
 * Generic YDB text column ↔ Java value converter. Uses {@link Object#toString()} to convert Java values
 * to YDB column values, and one of ({@code static fromString(String)}, {@code static valueOf(String)} or the
 * 1-arg {@code String} constructor) to convert YDB column values back to Java.
 * 
    *
  • Apply it locally to your entity's field, by using the {@link StringColumn @StringColumn} annotation. * Explicitly specify {@code @Column(customValueType=@CustomValueType(columnClass=String.class, converter=StringValueConverter.class), ...)} * if you need to add more column customizations.
  • *
  • Apply it globally to a user-defined field type, by using the {@link StringValueType @StringValueType} annotation. * Explicitly specify {@code @CustomValueType(columnClass=String.class, converter=StringValueConverter.class), ...)} if you prefer not to use * meta-annotations.
  • *
* * @param Java type */ public final class StringValueConverter implements ValueConverter { private volatile Function deserializer; public StringValueConverter() { } @Override public @NonNull String toColumn(@NonNull JavaField field, @NonNull J value) { return value.toString(); } @Override @SuppressWarnings("unchecked") public @NonNull J toJava(@NonNull JavaField field, @NonNull String column) { var clazz = field.getRawType(); if (String.class.equals(clazz)) { return (J) column; } var f = deserializer; if (deserializer == null) { synchronized (this) { f = deserializer; if (f == null) { deserializer = f = getStringValueDeserializerMethod(clazz); } } } return f.apply(column); } @SuppressWarnings("unchecked") private static ThrowingFunction getStringValueDeserializerMethod(Class clazz) { for (String methodName : new String[]{"fromString", "valueOf"}) { try { Method method = clazz.getMethod(methodName, String.class); if (isStatic(method.getModifiers())) { return s -> (J) method.invoke(null, s); } } catch (NoSuchMethodException ignored) { } } try { var ctor = clazz.getConstructor(String.class); return s -> (J) ctor.newInstance(s); } catch (NoSuchMethodException ignored) { } throw new IllegalArgumentException(format( "Type <%s> does not have a deserializer method: public static fromString(String)/valueOf(String) and" + "doesn't have constructor public %s(String)", clazz.getTypeName(), clazz.getTypeName() )); } private interface ThrowingFunction extends Function { R applyThrowing(T t) throws Exception; @Override @SneakyThrows default R apply(T t) { try { return applyThrowing(t); } catch (InvocationTargetException e) { // Propagate the real exception thrown by the deserializer method. // All unhandled getter exceptions are wrapped in ConversionException by the Repository, automatically, // so we don't need to do any wrapping here. throw e.getCause(); } catch (Exception e) { throw new IllegalStateException("Reflection problem with deserializer method", e); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy