zoukankan      html  css  js  c++  java
  • 【Java】LambdaToSql

    java lambda to sql

    SqlVisitor

    package xyz.xkind.core.sql.lambda;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Member;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.time.LocalTime;
    import java.time.temporal.TemporalAccessor;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import com.trigersoft.jaque.expression.BinaryExpression;
    import com.trigersoft.jaque.expression.ConstantExpression;
    import com.trigersoft.jaque.expression.DelegateExpression;
    import com.trigersoft.jaque.expression.Expression;
    import com.trigersoft.jaque.expression.ExpressionType;
    import com.trigersoft.jaque.expression.ExpressionVisitor;
    import com.trigersoft.jaque.expression.InvocationExpression;
    import com.trigersoft.jaque.expression.LambdaExpression;
    import com.trigersoft.jaque.expression.MemberExpression;
    import com.trigersoft.jaque.expression.ParameterExpression;
    import com.trigersoft.jaque.expression.UnaryExpression;
    import lombok.NonNull;
    import xyz.xkind.core.text.DateTimeUtils;
    
    public class SqlVisitor implements ExpressionVisitor<SqlWrapper> {
    
        private Expression body;
        private Expression param;
        private final List<ConstantExpression> PARAMS = new ArrayList<>();
        private final SqlWrapper SQL = new SqlWrapper();
    
        private final String head;
        private final String tail;
    
        public SqlVisitor() {
            this("`", "`");
        }
        
        public SqlVisitor(@NonNull String head, @NonNull String tail) {
            this.head = head;
            this.tail = tail;
        }
    
        private static String toSqlOp(int expressionType) {
            switch (expressionType) {
                case ExpressionType.BitwiseAnd: return "&";
                case ExpressionType.LogicalAnd: return "and";
                case ExpressionType.BitwiseOr: return "|";
                case ExpressionType.LogicalOr: return "or";
                case ExpressionType.LogicalNot: return "not";
                case ExpressionType.Equal: return "=";
                case ExpressionType.NotEqual: return "<>";
                case ExpressionType.GreaterThan: return ">";
                case ExpressionType.GreaterThanOrEqual: return ">=";
                case ExpressionType.LessThan: return "<";
                case ExpressionType.LessThanOrEqual: return "<=";
                case ExpressionType.Add: return "+";
                case ExpressionType.Subtract: return "-";
                case ExpressionType.Multiply: return "*";
                case ExpressionType.Divide: return "/";
                case ExpressionType.Modulo: return "%";
                default: throw new RuntimeException("Unknown ExpressionType '" + expressionType + "'");
            }
        }
    
        @Override
        public SqlWrapper visit(BinaryExpression e) {
            boolean quote = e != body && e.getExpressionType() == ExpressionType.LogicalOr;
            if (quote) { SQL.getKey().append('('); };
            e.getFirst().accept(this);
            SQL.getKey().append(' ').append(toSqlOp(e.getExpressionType())).append(' ');
            e.getSecond().accept(this);
            if (quote) { SQL.getKey().append(')'); };
            return SQL;
        }
    
        @Override
        public SqlWrapper visit(ConstantExpression e) {
            final Object value = e.getValue();
            if (value instanceof LambdaExpression) {
                return ((LambdaExpression<?>) value).getBody().accept(this);
            }
            SQL.getKey().append('?');
            if (value instanceof Date) {
                Object ret = DateTimeUtils.DATE_TIME.formatDate((Date) value);
                SQL.getValue().add(ret);
                return SQL;
            }
            if (value instanceof LocalTime) {
                Object ret = DateTimeUtils.TIME.format((TemporalAccessor) value);
                SQL.getValue().add(ret);
                return SQL;
            }
            if (value instanceof LocalDate) {
                Object ret = DateTimeUtils.DATE.format((TemporalAccessor) value);
                SQL.getValue().add(ret);
                return SQL;
            }
            if (value instanceof LocalDateTime) {
                Object ret = DateTimeUtils.DATE_TIME.format((TemporalAccessor) value);
                SQL.getValue().add(ret);
                return SQL;
            }
            Object ret = value.toString();
            SQL.getValue().add(ret);
            return SQL;
        }
    
        @Override
        public SqlWrapper visit(InvocationExpression e) {
            final Expression target = e.getTarget();
            if (target instanceof LambdaExpression<?>) {
                e.getArguments().stream()
                                .filter(x -> x instanceof ConstantExpression)
                                .forEach(x -> PARAMS.add((ConstantExpression) x));
            }
            if (target.getExpressionType() == ExpressionType.MethodAccess) {
                e.getArguments().stream().findFirst().ifPresent(x -> { param = x; });
            }
            return e.getTarget().accept(this);
        }
    
        @Override
        public SqlWrapper visit(LambdaExpression<?> e) {
            if (null == body && e.getBody() instanceof BinaryExpression) {
                this.body = e.getBody();
            }
            return e.getBody().accept(this);
        }
    
        @Override
        public SqlWrapper visit(DelegateExpression e) {
            return e.getDelegate().accept(this);
        }
    
        @Override
        public SqlWrapper visit(MemberExpression e) {
            final Member member = e.getMember();
            final Expression instance = e.getInstance();
            if (instance == null) {
                return invoke(member, e);
            }
            if (instance instanceof ParameterExpression) {
                String name = member.getName();
                name = name.replaceAll("^(get)", "");
                name = name.substring(0, 1).toLowerCase() + name.substring(1);
                SQL.getKey().append(head).append(name).append(tail);
            }
            if (instance instanceof ConstantExpression) {
                param.accept(this);
                return invoke(member, instance);
            }
            if (instance instanceof InvocationExpression) {
                ((InvocationExpression) instance).getTarget().accept(this);
                return invoke(member, param);
            }
            return SQL;
        }
    
        @Override
        public SqlWrapper visit(ParameterExpression e) {
            PARAMS.get(e.getIndex()).accept(this);
            return SQL;
        }
    
        @Override
        public SqlWrapper visit(UnaryExpression e) {
            SQL.getKey().append(toSqlOp(e.getExpressionType())).append(' ');
            return e.getFirst().accept(this);
        }
    
        public void clear() {
            SQL.clear();
        }
    
        // --------------------------------------------------------------------------------------------
    
        private SqlWrapper invoke(final Member member, final Expression expression) {
            if (member instanceof Constructor<?>) {
                return doCtorOp(member, expression);
            } else if (member instanceof Method) {
                final Class<?> clz = member.getDeclaringClass();
                if (Date.class.isAssignableFrom(clz)) {
                    return doDateOp(member, expression);
                } else if (TemporalAccessor.class.isAssignableFrom(clz)) {
                    return doDateOp(member, expression);
                } else if (String.class.isAssignableFrom(clz)) {
                    return doStrOp(member, expression);
                } else {
                    return doMethodOp(member, expression);
                }
            } else {
                throw new RuntimeException("The parameter '" + expression + "' not supported.");
            }
        }
    
        private SqlWrapper doCtorOp(final Member member, final Expression expression) {
            final Class<?> clz = member.getDeclaringClass();
            try {
                Object value = clz.newInstance();
                return Expression.constant(value, clz).accept(this);
            } catch (ReflectiveOperationException ex) {
                throw new RuntimeException(ex.getMessage(), ex.getCause());
            }
        }
    
        private SqlWrapper doStrOp(final Member member, final Expression expression) {
            final String method = member.getName();
            switch (method) {
                case "equals"/* = '{0}' */:
                    SQL.getKey().append(" = ");
                    expression.accept(this);
                    return SQL;
                case "startsWith"/* like '{0}%' */:
                    SQL.getKey().append(" like ").append("concat(");
                    expression.accept(this);
                    SQL.getKey().append(",'%')");
                    return SQL;
                case "endsWith"/* like '%{0}' */:
                    SQL.getKey().append(" like ").append("concat(").append("'%',");
                    expression.accept(this);
                    SQL.getKey().append(")");
                    return SQL;
                case "contains"/* like '%{0}%' */:
                    SQL.getKey().append(" like ").append("concat(").append("'%',");
                    expression.accept(this);
                    SQL.getKey().append(",'%')");
                    return SQL;
                default:
                    return doMethodOp(member, expression);
            }
        }
    
        private SqlWrapper doDateOp(final Member member, final Expression expression) {
            final String method = member.getName();
            switch (method) {
                case "equals"/* = '{0}' */:
                    SQL.getKey().append(" = ");
                    expression.accept(this);
                    return SQL;
                case "after"/* > '{0}' */:
                case "isAfter":
                    SQL.getKey().append(" > ");
                    expression.accept(this);
                    return SQL;
                case "before"/* < '{0}' */:
                case "isBefore":
                    SQL.getKey().append(" < ");
                    expression.accept(this);
                    return SQL;
                default:
                    return doMethodOp(member, expression);
            }
        }
    
        private SqlWrapper doMethodOp(final Member member, final Expression expression) {
            int mod = member.getModifiers();
            if (Modifier.isPublic(mod) && Modifier.isStatic(mod)) {
                try {
                    final Class<?> clz = member.getDeclaringClass();
                    Object value = clz.getMethod(member.getName()).invoke(null);
                    return Expression.constant(value, clz).accept(this);
                } catch (ReflectiveOperationException ex) {
                    throw new RuntimeException(ex.getMessage(), ex.getCause());
                }
            }
            throw new RuntimeException("The parameter '" + expression + "' not supported.");
        }
    
    }
    

    SqlWrapper

    package xyz.xkind.core.sql.lambda;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    
    public class SqlWrapper implements Map.Entry<StringBuilder, List<Object>> {
    
        private final StringBuilder sql;
        private final List<Object> params;
    
        public SqlWrapper() {
            this.sql = new StringBuilder();
            this.params = new ArrayList<>();
        }
    
        @Override
        public synchronized StringBuilder getKey() {
            return this.sql;
        }
    
        public synchronized StringBuilder addSql(CharSequence input) {
            this.sql.append(input);
            return this.sql;
        }
    
        @Override
        public synchronized List<Object> getValue() {
            return this.params;
        }
    
        public synchronized List<Object> addParams(Object o) {
            this.params.add(o);
            return this.params;
        }
    
        @Override
        public List<Object> setValue(List<Object> value) {
            throw new UnsupportedOperationException();
        }
    
        public synchronized void clear() {
            this.sql.setLength(0);
            this.params.clear();
        }
    
        @Override
        public String toString() {
            return this.sql.toString() + System.lineSeparator() + this.params.toString();
        }
    
    }
    

    SerializedExpression

    package xyz.xkind.core.sql.lambda;
    
    import java.io.Serializable;
    import com.trigersoft.jaque.expression.ExpressionVisitor;
    import com.trigersoft.jaque.expression.LambdaExpression;
    
    interface SerializedExpression<R> extends Serializable {
        default SqlWrapper sql() {
            return LambdaExpression.parse(this).accept(new SqlVisitor());
        }
        default R sql(ExpressionVisitor<R> visitor) {
            return (R) LambdaExpression.parse(this).accept(visitor);
        }
    }
    

    SqlPredicate

    package xyz.xkind.core.sql.lambda;
    
    import java.util.Objects;
    import java.util.function.Predicate;
    
    /**
     * Represents a predicate (boolean-valued function) of one argument.
     *
     * <p>This is a <a href="package-summary.html">functional interface</a>
     * whose functional method is {@link #test(Object)}.
     *
     * @param <T> the type of the input to the predicate
     *
     * @since 1.8
     */
    @FunctionalInterface
    public interface SqlPredicate<T> extends Predicate<T>, SerializedExpression<SqlWrapper> {
    
        /**
         * Returns a composed predicate that represents a short-circuiting logical
         * AND of this predicate and another.  When evaluating the composed
         * predicate, if this predicate is {@code false}, then the {@code other}
         * predicate is not evaluated.
         *
         * <p>Any exceptions thrown during evaluation of either predicate are relayed
         * to the caller; if evaluation of this predicate throws an exception, the
         * {@code other} predicate will not be evaluated.
         *
         * @param other a predicate that will be logically-ANDed with this
         *              predicate
         * @return a composed predicate that represents the short-circuiting logical
         * AND of this predicate and the {@code other} predicate
         * @throws NullPointerException if other is null
         */
        default SqlPredicate<T> and(SqlPredicate<? super T> other) {
            Objects.requireNonNull(other);
            return (t) -> test(t) && other.test(t);
        }
    
        /**
         * Returns a predicate that represents the logical negation of this
         * predicate.
         *
         * @return a predicate that represents the logical negation of this
         * predicate
         */
        default SqlPredicate<T> negate() {
            return (t) -> !test(t);
        }
    
        /**
         * Returns a composed predicate that represents a short-circuiting logical
         * OR of this predicate and another.  When evaluating the composed
         * predicate, if this predicate is {@code true}, then the {@code other}
         * predicate is not evaluated.
         *
         * <p>Any exceptions thrown during evaluation of either predicate are relayed
         * to the caller; if evaluation of this predicate throws an exception, the
         * {@code other} predicate will not be evaluated.
         *
         * @param other a predicate that will be logically-ORed with this
         *              predicate
         * @return a composed predicate that represents the short-circuiting logical
         * OR of this predicate and the {@code other} predicate
         * @throws NullPointerException if other is null
         */
        default SqlPredicate<T> or(SqlPredicate<? super T> other) {
            Objects.requireNonNull(other);
            return (t) -> test(t) || other.test(t);
        }
    
        /**
         * Returns a predicate that tests if two arguments are equal according
         * to {@link Objects#equals(Object, Object)}.
         *
         * @param <T> the type of arguments to the predicate
         * @param targetRef the object reference with which to compare for equality,
         *               which may be {@code null}
         * @return a predicate that tests if two arguments are equal according
         * to {@link Objects#equals(Object, Object)}
         */
        static <T> SqlPredicate<T> isEqual(Object targetRef) {
            return (null == targetRef)
                    ? Objects::isNull
                    : object -> targetRef.equals(object);
        }
    
    }
    

    SqlFunction

    package xyz.xkind.core.sql.lambda;
    
    import java.util.Objects;
    import java.util.function.Function;
    
    /**
     * Represents a function that accepts one argument and produces a result.
     *
     * <p>This is a <a href="package-summary.html">functional interface</a>
     * whose functional method is {@link #apply(Object)}.
     *
     * @param <T> the type of the input to the function
     * @param <R> the type of the result of the function
     *
     * @since 1.8
     */
    @FunctionalInterface
    public interface SqlFunction<T, R> extends Function<T, R>, SerializedExpression<SqlWrapper> {
    
        /**
         * Returns a composed function that first applies the {@code before}
         * function to its input, and then applies this function to the result.
         * If evaluation of either function throws an exception, it is relayed to
         * the caller of the composed function.
         *
         * @param <V> the type of input to the {@code before} function, and to the
         *           composed function
         * @param before the function to apply before this function is applied
         * @return a composed function that first applies the {@code before}
         * function and then applies this function
         * @throws NullPointerException if before is null
         *
         * @see #andThen(Function)
         */
        default <V> SqlFunction<V, R> compose(SqlFunction<? super V, ? extends T> before) {
            Objects.requireNonNull(before);
            return (V v) -> apply(before.apply(v));
        }
    
        /**
         * Returns a composed function that first applies this function to
         * its input, and then applies the {@code after} function to the result.
         * If evaluation of either function throws an exception, it is relayed to
         * the caller of the composed function.
         *
         * @param <V> the type of output of the {@code after} function, and of the
         *           composed function
         * @param after the function to apply after this function is applied
         * @return a composed function that first applies this function and then
         * applies the {@code after} function
         * @throws NullPointerException if after is null
         *
         * @see #compose(Function)
         */
        default <V> SqlFunction<T, V> andThen(SqlFunction<? super R, ? extends V> after) {
            Objects.requireNonNull(after);
            return (T t) -> after.apply(apply(t));
        }
    
        /**
         * Returns a function that always returns its input argument.
         *
         * @param <T> the type of the input and output objects to the function
         * @return a function that always returns its input argument
         */
        static <T> SqlFunction<T, T> identity() {
            return t -> t;
        }
    
    }
    

    调用

    import java.time.LocalDate;
    import java.util.Date;
    import lombok.Data;
    import xyz.xkind.core.sql.lambda.SqlPredicate;
    import xyz.xkind.core.sql.lambda.SqlVisitor;
    
    public class TestLambdaToSql {
    
        @Data
        class Person {
            private String name;
            private int age;
            private LocalDate create;
            private Date login;
        }
        
        public static void main(String[] args) {
            final LocalDate ld = LocalDate.of(1970, 1, 1);
            final Date date = new Date();
    
            SqlPredicate<Person> sp_0 = p -> p.getName().equals("root");
            SqlPredicate<Person> sp_1 = p -> p.getName().startsWith("ro") || p.getName().endsWith("ot");
            SqlPredicate<Person> sp_2 = p -> p.getName().contains("root");
    
            SqlVisitor visitor = new SqlVisitor("`", "`");
            System.out.println(sp_0.sql(visitor) + System.lineSeparator());
            visitor.clear();
            System.out.println(sp_1.sql(visitor) + System.lineSeparator());
            visitor.clear();
            System.out.println(sp_2.sql(visitor) + System.lineSeparator());
    
            SqlPredicate<Person> sp_3 = p -> p.getAge() > 18 && p.getLogin().before(date) 
                                                             && p.getCreate().isAfter(ld);
            System.out.println(sp_3.sql() + System.lineSeparator());
        }
    }
    
  • 相关阅读:
    洛谷 3455 (莫比乌斯反演优化)
    HDU 1695 GCD (莫比乌斯反演模板)
    BZOJ 2818 Gcd(欧拉函数+质数筛选)
    欧拉函数(总结)
    Matrix(二维树状数组)入门第一题
    P3919 【模板】可持久化数组(可持久化线段树/平衡树)(入门第一题)
    Color the ball(树状数组区间更新+单点求值)
    快写
    欧拉筛
    D. Magic Breeding
  • 原文地址:https://www.cnblogs.com/zhuzhongxing/p/14147073.html
Copyright © 2011-2022 走看看