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

org.jdbi.v2.st4.ST4StatementLocator Maven / Gradle / Ivy

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jdbi.v2.st4;

import org.skife.jdbi.v2.StatementContext;
import org.skife.jdbi.v2.tweak.StatementLocator;
import org.stringtemplate.v4.ST;
import org.stringtemplate.v4.STGroup;
import org.stringtemplate.v4.STGroupFile;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;

public class ST4StatementLocator implements StatementLocator {

    private static final ConcurrentMap CACHE = new ConcurrentHashMap<>();

    private final Function group;

    public ST4StatementLocator(final STGroup group) {
        this((_ctx) -> group);
    }

    public ST4StatementLocator(final Function groupProvider) {
        this.group = groupProvider;
    }

    @Override
    public String locate(final String name, final StatementContext ctx) throws Exception {
        ST st = this.group.apply(ctx).getInstanceOf(name);
        if (st == null) {
            // if there is no template by this name in the group, treat it as a template literal.
            st = new ST(name);
        }

        // we add all context values, ST4 explodes if you add a value that lacks a formal argument,
        // iff hasFormalArgs is true. If it is false, it just uses values opportunistically. This is gross
        // but works. -brianm
        st.impl.hasFormalArgs = false;

        for (final Map.Entry attr : ctx.getAttributes().entrySet()) {
            st.add(attr.getKey(), attr.getValue());
        }

        return st.render();
    }

    /**
     * Obtains a locator based on a classpath path, using a global template group CACHE.
     */
    public static StatementLocator fromClasspath(String path) {
        return fromClasspath(UseSTGroupCache.YES, path);
    }

    /**
     * Obtains a locator based on a classpath path. Allows flag to indicate whether the global STGroup CACHE should be
     * used. In general, the only reasons to NOT use the CACHE are: (1) if you are fiddling with the templates during development
     * and want to make changes without having to restart the server; and (2) if you use something like the tomcat
     * deployer to push new versions without restarting the JVM which causes static maps to leak memory.
     */
    public static StatementLocator fromClasspath(UseSTGroupCache useCache, String path) {
        return forURL(useCache, ST4StatementLocator.class.getResource(path));
    }

    /**
     * Obtains a locator based on the type passed in, using a global template group CACHE.
     * 

* STGroup is loaded from the classpath via sqlObjectType.getResource( ), such that names line * up with the package and class, so: com.example.Foo will look for /com/example/Foo.sql.stg . Inner classes * are seperated in the file name by a '.' not a '$', so com.example.Foo.Bar (Bar is an inner class of Foo) would * be at /com/example/Foo.Bar.sql.stg . */ public static StatementLocator forType(final Class sqlObjectType) { return forType(UseSTGroupCache.YES, sqlObjectType); } /** * Obtains a locator based on the type passed in. Allows flag to indicate whether the global STGroup CACHE should be * used. In general, the only reasons to NOT use the CACHE are: (1) if you are fiddling with the templates during development * and want to make changes without having to restart the server; and (2) if you use something like the tomcat * deployer to push new versions without restarting the JVM which causes static maps to leak memory. *

* STGroup is loaded from the classpath via sqlObjectType.getResource( ), such that names line * up with the package and class, so: com.example.Foo will look for /com/example/Foo.sql.stg . Inner classes * are seperated in the file name by a '.' not a '$', so com.example.Foo.Bar (Bar is an inner class of Foo) would * be at /com/example/Foo.Bar.sql.stg . */ public static StatementLocator forType(UseSTGroupCache useCache, final Class sqlObjectType) { return forURL(useCache, classToUrl(sqlObjectType)); } public static StatementLocator forURL(UseSTGroupCache useCache, URL url) { final STGroup stg; if (useCache == UseSTGroupCache.YES) { stg = CACHE.computeIfAbsent(url.toString(), ST4StatementLocator::urlToSTGroup); } else { stg = urlToSTGroup(url.toString()); } return new ST4StatementLocator(stg); } /** * Create a statement locator intended for setting on a DBI or Handle instance which will * lookup a template group to use based on the name of the sql object type for a particular query, using * the same logic as {@link UseST4StatementLocator}. */ public static StatementLocator perType(UseSTGroupCache useCache) { return perType(useCache, new STGroup('<', '>')); } /** * Create a statement locator intended for setting on a DBI or Handle instance which will * lookup a template group to use based on the name of the sql object type for a particular query, using * the same logic as {@link UseST4StatementLocator}. *

* Supports a fallback template group for statements created not using a sql object. */ public static StatementLocator perType(UseSTGroupCache useCache, String fallbackTemplateGroupPath) { return perType(useCache, ST4StatementLocator.class.getResource(fallbackTemplateGroupPath)); } /** * Create a statement locator intended for setting on a DBI or Handle instance which will * lookup a template group to use based on the name of the sql object type for a particular query, using * the same logic as {@link UseST4StatementLocator}. *

* Supports a fallback template group for statements created not using a sql object. */ public static StatementLocator perType(UseSTGroupCache useCache, URL baseTemplate) { return perType(useCache, urlToSTGroup(baseTemplate)); } /** * Create a statement locator intended for setting on a DBI or Handle instance which will * lookup a template group to use based on the name of the sql object type for a particular query, using * the same logic as {@link UseST4StatementLocator}. *

* Supports a fallback template group for statements created not using a sql object. */ public static StatementLocator perType(UseSTGroupCache useCache, STGroup fallbackTemplateGroup) { final StatementLocator fallback = new ST4StatementLocator(fallbackTemplateGroup); if (useCache == UseSTGroupCache.YES) { final ConcurrentMap, StatementLocator> sqlObjectCache = new ConcurrentHashMap<>(); return (name, ctx) -> { if (ctx.getSqlObjectType() != null) { StatementLocator sl = sqlObjectCache.computeIfAbsent(ctx.getSqlObjectType(), (c) -> { return ST4StatementLocator.forType(useCache, ctx.getSqlObjectType()); }); return sl.locate(name, ctx); } else { return fallback.locate(name, ctx); } }; } else { // if we are not caching, let's not cache the lookup of the template group either! return (name, ctx) -> { if (ctx.getSqlObjectType() != null) { return ST4StatementLocator.forType(useCache, ctx.getSqlObjectType()).locate(name, ctx); } else { return fallback.locate(name, ctx); } }; } } public enum UseSTGroupCache { YES, NO } private static URL classToUrl(Class c) { // handle naming of inner classes as Outer.Inner.sql.stg instead of Outer$Inner.sql.stg final String fullName = c.getName(); final String pkg = c.getPackage().getName(); final String className = fullName.substring(pkg.length() + 1, fullName.length()).replace('$', '.'); return c.getResource(className + ".sql.stg"); } private static STGroup urlToSTGroup(String u) { try { return urlToSTGroup(new URL(u)); } catch (MalformedURLException e) { throw new IllegalStateException("a URL failed to roundtrip from a string!", e); } } private static STGroup urlToSTGroup(URL u) { return new STGroupFile(u, "UTF-8", '<', '>'); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy