io.lavagna.common.QueryType.java Source code

Java tutorial

Introduction

Here is the source code for io.lavagna.common.QueryType.java

Source

/**
 * This file is part of lavagna.
 *
 * lavagna is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * lavagna is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with lavagna.  If not, see <http://www.gnu.org/licenses/>.
 */
package io.lavagna.common;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.StatementCreatorUtils;
import org.springframework.jdbc.core.namedparam.EmptySqlParameterSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.util.StringUtils;

/**
 * Query Type:
 * 
 * <ul>
 * <li>TEMPLATE : we receive the string defined in @Query/@QueryOverride annotation.
 * <li>EXECUTE : the query will be executed. If it's a select, the result will be mapped with a
 * ConstructorAnnotationRowMapper if it has the correct form.
 * </ul>
 * 
 */
public enum QueryType {
    /**
     * Receive the string defined in @Query/@QueryOverride annotation.
     */
    TEMPLATE {
        @Override
        Object apply(String template, NamedParameterJdbcTemplate jdbc, Method method, Object[] args) {
            return template;
        }
    },

    /**
     */
    EXECUTE {

        /**
         * Keep a mapping between a given class and a possible RowMapper.
         * 
         * If the Class has the correct form, a ConstructorAnnotationRowMapper will be built and the boolean set to true
         * in the pair. If the class has not the correct form, the boolean will be false and the class will be used as
         * it is in the jdbc template.
         */
        private final Map<Class<Object>, HasRowmapper> cachedClassToMapper = new ConcurrentHashMap<Class<Object>, HasRowmapper>();

        @Override
        Object apply(String template, NamedParameterJdbcTemplate jdbc, Method method, Object[] args) {
            JdbcAction action = actionFromTemplate(template);
            SqlParameterSource parameters = extractParameters(method, args);
            if (action == JdbcAction.QUERY) {
                return doQuery(template, jdbc, method, parameters);
            } else {
                return jdbc.update(template, parameters);
            }
        }

        @SuppressWarnings("unchecked")
        private Object doQuery(String template, NamedParameterJdbcTemplate jdbc, Method method,
                SqlParameterSource parameters) {
            if (method.getReturnType().isAssignableFrom(List.class)) {
                Class<Object> c = (Class<Object>) ((ParameterizedType) method.getGenericReturnType())
                        .getActualTypeArguments()[0];
                HasRowmapper r = ensurePresence(c);
                if (r.present) {
                    return jdbc.query(template, parameters, r.rowMapper);
                } else {
                    return jdbc.queryForList(template, parameters, c);
                }
            } else {
                Class<Object> c = (Class<Object>) method.getReturnType();
                HasRowmapper r = ensurePresence(c);
                if (r.present) {
                    return jdbc.queryForObject(template, parameters, r.rowMapper);
                } else {
                    return jdbc.queryForObject(template, parameters, c);
                }
            }
        }

        private HasRowmapper ensurePresence(Class<Object> c) {
            if (!cachedClassToMapper.containsKey(c)) {
                cachedClassToMapper.put(c, handleClass(c));
            }
            return cachedClassToMapper.get(c);
        }
    };

    private static JdbcAction actionFromTemplate(String template) {
        String tmpl = StringUtils.deleteAny(template.toLowerCase(Locale.ENGLISH), "() ").trim();
        return tmpl.indexOf("select") == 0 ? JdbcAction.QUERY : JdbcAction.UPDATE;
    }

    private enum JdbcAction {
        QUERY, UPDATE
    }

    abstract Object apply(String template, NamedParameterJdbcTemplate jdbc, Method method, Object[] args);

    private static HasRowmapper handleClass(Class<Object> c) {
        if (ConstructorAnnotationRowMapper.hasConstructorInTheCorrectForm(c)) {
            return new HasRowmapper(true, new ConstructorAnnotationRowMapper<Object>(c));
        } else {
            return new HasRowmapper(false, null);
        }
    }

    private static SqlParameterSource extractParameters(Method m, Object[] args) {

        Class<?>[] parameterTypes = m.getParameterTypes();
        Annotation[][] parameterAnnotations = m.getParameterAnnotations();
        if (parameterAnnotations == null || parameterAnnotations.length == 0) {
            return new EmptySqlParameterSource();
        }

        MapSqlParameterSource ps = new MapSqlParameterSource();
        for (int i = 0; i < args.length; i++) {
            String name = parameterName(parameterAnnotations[i]);
            if (name != null) {
                ps.addValue(name, args[i], StatementCreatorUtils.javaTypeToSqlParameterType(parameterTypes[i]));
            }
        }

        return ps;
    }

    private static String parameterName(Annotation[] annotation) {

        if (annotation == null) {
            return null;
        }

        for (Annotation a : annotation) {
            if (a instanceof Bind) {
                return ((Bind) a).value();
            }
        }
        return null;
    }

    private static class HasRowmapper {
        private final boolean present;
        private final RowMapper<Object> rowMapper;

        HasRowmapper(boolean present, RowMapper<Object> rowMapper) {
            this.present = present;
            this.rowMapper = rowMapper;
        }
    }
}