org.hibernate.dialect.function.DerbyConcatFunction.java Source code

Java tutorial

Introduction

Here is the source code for org.hibernate.dialect.function.DerbyConcatFunction.java

Source

/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */
package org.hibernate.dialect.function;

import java.util.Iterator;
import java.util.List;

import org.hibernate.QueryException;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;

/**
 * A specialized concat() function definition in which:<ol>
 *     <li>we translate to use the concat operator ('||')</li>
 *     <li>wrap dynamic parameters in CASTs to VARCHAR</li>
 * </ol>
 * <p/>
 * This last spec is to deal with a limitation on DB2 and variants (e.g. Derby)
 * where dynamic parameters cannot be used in concatenation unless they are being
 * concatenated with at least one non-dynamic operand.  And even then, the rules
 * are so convoluted as to what is allowed and when the CAST is needed and when
 * it is not that we just go ahead and do the CASTing.
 *
 * @author Steve Ebersole
 */
public class DerbyConcatFunction implements SQLFunction {
    /**
     * {@inheritDoc}
     * <p/>
     * Here we always return <tt>true</tt>
     */
    @Override
    public boolean hasArguments() {
        return true;
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Here we always return <tt>true</tt>
     */
    @Override
    public boolean hasParenthesesIfNoArguments() {
        return true;
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Here we always return {@link StandardBasicTypes#STRING}.
     */
    @Override
    public Type getReturnType(Type argumentType, Mapping mapping) throws QueryException {
        return StandardBasicTypes.STRING;
    }

    /**
     * {@inheritDoc}
     * <p/>
     * Here's the meat..  The whole reason we have a separate impl for this for Derby is to re-define
     * this method.  The logic here says that if not all the incoming args are dynamic parameters
     * (i.e. <tt>?</tt>) then we simply use the Derby concat operator (<tt>||</tt>) on the unchanged
     * arg elements.  However, if all the args are dynamic parameters, then we need to wrap the individual
     * arg elements in <tt>cast</tt> function calls, use the concatenation operator on the <tt>cast</tt>
     * returns, and then wrap that whole thing in a call to the Derby <tt>varchar</tt> function.
     */
    @Override
    public String render(Type argumentType, List args, SessionFactoryImplementor factory) throws QueryException {
        // first figure out if all arguments are dynamic (jdbc parameters) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        boolean areAllArgumentsDynamic = true;
        for (Object arg1 : args) {
            final String arg = (String) arg1;
            if (!"?".equals(arg)) {
                // we found a non-dynamic argument
                areAllArgumentsDynamic = false;
                break;
            }
        }

        if (areAllArgumentsDynamic) {
            return join(args.iterator(), CAST_STRING_TRANSFORMER, new StringJoinTemplate() {
                public String getBeginning() {
                    return "varchar( ";
                }

                public String getSeparator() {
                    return " || ";
                }

                public String getEnding() {
                    return " )";
                }
            });
        } else {
            return join(args.iterator(), NO_TRANSFORM_STRING_TRANSFORMER, new StringJoinTemplate() {
                public String getBeginning() {
                    return "(";
                }

                public String getSeparator() {
                    return "||";
                }

                public String getEnding() {
                    return ")";
                }
            });
        }
    }

    private static interface StringTransformer {
        /**
         * Transform a string to another
         *
         * @param string The String to be transformed
         *
         * @return The transformed form
         */
        public String transform(String string);
    }

    private static final StringTransformer CAST_STRING_TRANSFORMER = new StringTransformer() {
        @Override
        public String transform(String string) {
            // expectation is that incoming string is "?"
            return "cast( ? as varchar(32672) )";
        }
    };

    private static final StringTransformer NO_TRANSFORM_STRING_TRANSFORMER = new StringTransformer() {
        @Override
        public String transform(String string) {
            return string;
        }
    };

    private static interface StringJoinTemplate {
        /**
         * Getter for property 'beginning'.
         *
         * @return Value for property 'beginning'.
         */
        public String getBeginning();

        /**
         * Getter for property 'separator'.
         *
         * @return Value for property 'separator'.
         */
        public String getSeparator();

        /**
         * Getter for property 'ending'.
         *
         * @return Value for property 'ending'.
         */
        public String getEnding();
    }

    private static String join(Iterator/*<String>*/ elements, StringTransformer elementTransformer,
            StringJoinTemplate template) {
        // todo : make this available via StringHelper?
        final StringBuilder buffer = new StringBuilder(template.getBeginning());
        while (elements.hasNext()) {
            final String element = (String) elements.next();
            buffer.append(elementTransformer.transform(element));
            if (elements.hasNext()) {
                buffer.append(template.getSeparator());
            }
        }
        return buffer.append(template.getEnding()).toString();
    }
}