anyframe.core.query.impl.QueryServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for anyframe.core.query.impl.QueryServiceImpl.java

Source

/*
 * Copyright 2002-2008 the original author or authors.
 *
 * 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 anyframe.core.query.impl;

import java.io.StringWriter;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.velocity.app.Velocity;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.CallableStatementCreator;
import org.springframework.jdbc.core.CallableStatementCreatorFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.SqlTypeValue;
import org.springframework.jdbc.core.StatementCreatorUtils;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.object.BatchSqlUpdate;
import org.springframework.jdbc.support.lob.LobHandler;

import anyframe.core.query.IMappingInfo;
import anyframe.core.query.IQueryInfo;
import anyframe.core.query.IQueryService;
import anyframe.core.query.IResultSetMapper;
import anyframe.core.query.QueryServiceException;
import anyframe.core.query.impl.jdbc.PagingJdbcTemplate;
import anyframe.core.query.impl.jdbc.PagingNamedParamJdbcTemplate;
import anyframe.core.query.impl.jdbc.generator.IPagingSQLGenerator;
import anyframe.core.query.impl.jdbc.mapper.CallbackResultSetMapper;
import anyframe.core.query.impl.jdbc.setter.ExtMapSqlParameterSource;
import anyframe.core.query.impl.jdbc.setter.ExtMapSqlParameterSourceContext;
import anyframe.core.query.impl.jdbc.setter.PreparedStatementArgTypeSetter;
import anyframe.core.query.impl.util.InternalDataAccessException;
import anyframe.core.query.impl.util.NamedParameterUtils;
import anyframe.core.query.impl.util.ParsedSql;
import anyframe.core.query.impl.util.SQLTypeTransfer;

/**
 * @author Soyon Lim
 */
public class QueryServiceImpl extends AbstractQueryService implements IQueryService {
    private static final String DELIMETER = "=";

    private PagingJdbcTemplate jdbcTemplate;

    private PagingNamedParamJdbcTemplate namedJdbcTemplate = null;

    private LobHandler lobHandler;

    /** ************* SETTER METHODS ************** */

    /**
     * @param jdbcTemplate
     *            the jdbcTemplate to set
     */
    public void setJdbcTemplate(PagingJdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
        this.namedJdbcTemplate = new PagingNamedParamJdbcTemplate(jdbcTemplate, jdbcTemplate.getDataSource());
        this.namedJdbcTemplate.setExceptionTranslator(jdbcTemplate.getExceptionTranslator());
    }

    public void setPagingSQLGenerator(IPagingSQLGenerator pagingSQLGenerator) {
        jdbcTemplate.setPaginationSQLGetter(pagingSQLGenerator);
    }

    /**
     * @param lobHandler
     *            the lobHandler to set
     */
    public void setLobHandler(LobHandler lobHandler) {
        this.lobHandler = lobHandler;
    }

    public LobHandler getLobHandler() {
        return lobHandler;
    }

    /** ************* SERVICE METHODS ************** */

    /**
     * ?    QueryService ? INSERT? batch .
     * 
     * @param targets
     *            object of class which is matched with specified table in XML
     *            files. is the List type of Object Array.
     * @return an array of the number of rows affected by each statement
     * @throws QueryServiceException
     *             if there is any problem issuing the insert
     */
    public int[] batchCreate(List targets) throws QueryServiceException {
        String sql = "";
        String className = "";
        try {
            className = targets.get(0).getClass().getName();
            IMappingInfo mappingInfo = (IMappingInfo) getSqlRepository().getMappingInfos().get(className);
            sql = mappingInfo.getInsertQuery();
            return batchDynamicExecutor(sql, targets);
        } catch (Exception e) {
            throw processException("batch-insert using object " + className + " defined table mapping", sql, e);
        }
    }

    /**
     * ?    QueryService ? DELETE? batch .
     * 
     * @param targets
     *            object of class which is matched with specified table in XML
     *            files. is the List type of Object Array.
     * @return array of the number of rows affected by each statement
     * @throws QueryServiceException
     *             if there is any problem issuing the delete
     */
    public int[] batchRemove(List targets) throws QueryServiceException {
        String sql = "";
        String className = "";
        try {
            className = targets.get(0).getClass().getName();
            IMappingInfo mappingInfo = (IMappingInfo) getSqlRepository().getMappingInfos().get(className);
            sql = mappingInfo.getDeleteQuery();
            return batchDynamicExecutor(sql, targets);
        } catch (Exception e) {
            throw processException("batch-remove using object " + className + " defined table mapping", sql, e);
        }
    }

    /**
     * ?    QueryService ? UPDATE? batch .
     * 
     * @param targets
     *            object of class which is matched with specified table in XML
     *            files. is the List type of Object Array.
     * @return array of the number of rows affected by each statement
     * @throws QueryServiceException
     *             if there is any problem issuing the update
     */
    public int[] batchUpdate(List targets) throws QueryServiceException {
        String sql = "";
        String className = "";
        try {
            className = targets.get(0).getClass().getName();
            IMappingInfo mappingInfo = (IMappingInfo) getSqlRepository().getMappingInfos().get(className);
            sql = mappingInfo.getUpdateQuery();
            return batchDynamicExecutor(sql, targets);
        } catch (Exception e) {
            throw processException("batch-update using object " + className + " defined table mapping", sql, e);
        }
    }

    /**
     *   ? ? ?? queryId?       ? batch .
     * 
     * @param queryInfo
     *               
     * @param queryId
     *            identifier of query statement to execute
     * @param targets
     *            the List type of Object Array.
     * @return array of the number of rows affected by each statement
     * @throws QueryServiceException
     *             if there is any problem issuing the insert, update, delete
     */
    public int[] batchUpdate(String queryId, List targets) throws QueryServiceException {
        String sql = "";
        try {
            containesQueryId(queryId);
            IQueryInfo queryInfo = (IQueryInfo) getSqlRepository().getQueryInfos().get(queryId);

            sql = queryInfo.getQueryString();
            if (queryInfo.isDynamic())
                return batchDynamicExecutor(sql, targets);
            else
                return batchStaticExecutor(sql, targets);
        } catch (Exception e) {
            throw processException("batch-update [query id = '" + queryId + "']", sql, e);
        }
    }

    public int[] batchUpdateBySQL(String sql, String[] types, List targets) throws QueryServiceException {
        try {
            return batchExecutor(sql, convertTypes(types), targets);
        } catch (Exception e) {
            throw processException("batch update by SQL", sql, e);
        }
    }

    /**
     * ?    QueryService ? INSERT? .
     * 
     * @param obj
     *            INSERT?    ?   ?
     * @return the number of rows affected
     * @throws QueryServiceException
     *             if there is any problem issuing the insert
     */
    public int create(Object obj) throws QueryServiceException {
        String sql = "";
        String className = "";
        try {
            className = obj.getClass().getName();
            IMappingInfo mappingInfo = (IMappingInfo) getSqlRepository().getMappingInfos().get(className);

            sql = mappingInfo.getInsertQuery();
            return objectCUDExecutor(obj, sql);
        } catch (Exception e) {
            throw processException("insert using object " + className + " defined table mapping", sql, e);
        }
    }

    /**
     *   ? ? ?? queryId?       ? .
     * 
     * @param queryId
     *            identifier of query statement to execute
     * @param values
     *             ?     ? ? Object Array
     * @return the number of rows affected
     * @throws QueryServiceException
     *             if there is any problem issuing that query
     */
    public int create(String queryId, Object[] values) throws QueryServiceException {
        IQueryInfo queryInfo = null;
        try {
            containesQueryId(queryId);
            queryInfo = (IQueryInfo) getSqlRepository().getQueryInfos().get(queryId);

            return sqlCUDExecutor(queryInfo, values);
        } catch (Exception e) {
            throw processException("insert [query id = '" + queryId + "']", getQueryString(queryInfo), e);
        }
    }

    public int createBySQL(String sql, String[] types, Object[] values) throws QueryServiceException {
        try {
            return sqlCUDExecutor(sql, values, convertTypes(types), false, null, null, null);
        } catch (Exception e) {
            throw processException("insert by SQL", sql, e);
        }
    }

    public Map execute(String queryId, Map values) throws QueryServiceException {
        return execute(queryId, values, 0);
    }

    public Map execute(String queryId, Map values, int pageIndex) throws QueryServiceException {
        return execute(queryId, values, pageIndex, -1);
    }

    /**
     *   ? ? ?? queryId?       Stored Procedure,
     * Function ? .
     * 
     * @param queryId
     *            identifier of query statement to execute
     * @param values
     *             ?     ? ? Object Array
     * @param pageIndex
     *            page_number which expected to be displayed.
     * @param pageSize
     *            page_Size which expected to be displayed per page.
     * @return Stored Procedure, Function    ? Map? List
     * @throws QueryServiceException
     *             if there is any problem issuing that query
     */
    public Map execute(String queryId, Map values, int pageIndex, int pageSize) throws QueryServiceException {
        String sql = "";
        try {
            containesQueryId(queryId);
            if (pageSize == -1)
                pageSize = getSqlRepository().getFetchCountPerQuery(queryId);
            IQueryInfo queryInfo = (IQueryInfo) getSqlRepository().getQueryInfos().get(queryId);

            sql = queryInfo.getQueryString();
            List paramList = queryInfo.getSqlParameterList();
            CallableStatementCreator callableStatementCreator = new CallableStatementCreatorFactory(sql, paramList)
                    .newCallableStatementCreator(values);
            return jdbcTemplate.call(callableStatementCreator, paramList);
        } catch (Exception e) {
            throw processException("execute statement [query id = '" + queryId + "']", sql, e);
        }
    }

    public Map executeBySQL(String query, String[] paramTypeNames, String[] paramBindingNames,
            String[] paramBindingTypes, Map values) throws QueryServiceException {
        return executeBySQL(query, paramTypeNames, paramBindingNames, paramBindingTypes, values, 0, 0);
    }

    public Map executeBySQL(String sql, String[] paramTypeNames, String[] paramBindingNames,
            String[] paramBindingTypes, Map values, int pageIndex, int pageSize) throws QueryServiceException {
        try {
            List paramList = SQLTypeTransfer.getSqlParameterList(paramTypeNames, paramBindingTypes,
                    paramBindingNames);

            CallableStatementCreator callableStatementCreator = new CallableStatementCreatorFactory(sql, paramList)
                    .newCallableStatementCreator(values);
            return jdbcTemplate.call(callableStatementCreator, paramList);
        } catch (Exception e) {
            throw processException("execute by SQL", sql, e);
        }
    }

    public Collection find(String queryId, Object[] values) throws QueryServiceException {
        return find(queryId, values, -1, -1, false);
    }

    public Collection find(String queryId, Object[] values, int pageIndex) throws QueryServiceException {
        return find(queryId, values, pageIndex, -1, true);
    }

    /**
     * ?    QueryService ?   SELECT? .
     * 
     * @param obj
     *            SELECT?    ?   ?
     * @return   ? ?? List
     * @throws QueryServiceException
     *             if there is any problem issuing that query
     */
    public Collection find(Object obj) throws QueryServiceException {
        String sql = "";
        String className = "";
        try {
            className = obj.getClass().getName();
            IMappingInfo mappingInfo = (IMappingInfo) getSqlRepository().getMappingInfos().get(className);

            sql = mappingInfo.getSelectByPrimaryKeyQuery();
            // result mapping?  RowCallbackHandler 
            // 2009.05.28
            CallbackResultSetMapper rowCallbackHandler = new CallbackResultSetMapper(Class.forName(className),
                    mappingInfo, lobHandler, getSqlRepository().getNullCheck(), "none");

            Map inputMap = new HashMap();
            inputMap.put("anyframe", obj);
            SqlParameterSource sqlParameterSource = new ExtMapSqlParameterSource(inputMap);

            namedJdbcTemplate.query(sql, sqlParameterSource, (RowCallbackHandler) rowCallbackHandler);
            return rowCallbackHandler.getObjects();
        } catch (Exception e) {
            throw processException("select using object " + className + " defined table mapping", sql, e);
        }
    }

    public Collection find(String queryId, Object[] values, int pageIndex, int pageSize)
            throws QueryServiceException {
        return find(queryId, values, pageIndex, pageSize, true);
    }

    public Collection findBySQL(String sql, String[] types, Object[] values) throws QueryServiceException {
        try {
            return jdbcTemplate.queryForList(sql, values, convertTypes(types));
        } catch (Exception e) {
            throw processException("select by SQL", sql, e);
        }
    }

    public Collection findBySQL(String sql, String[] types, Object[] values, int pageIndex, int pageSize)
            throws QueryServiceException {
        try {
            PaginationVO paginationVO = new PaginationVO(pageSize);
            paginationVO.setPageIndex(pageIndex);
            return jdbcTemplate.queryForListWithPagination(sql, values, convertTypes(types), paginationVO);
        } catch (Exception e) {
            throw processException("select by SQL", sql, e);
        }
    }

    public Map findBySQLWithRowCount(String sql, String[] types, Object[] values) throws QueryServiceException {
        return findBySQLWithRowCount(sql, types, values, -1, -1);
    }

    public Map findBySQLWithRowCount(String sql, String[] types, Object[] values, int pageIndex, int pageSize)
            throws QueryServiceException {
        try {
            PaginationVO paginationVO = new PaginationVO(pageSize);
            paginationVO.setPageIndex(pageIndex);
            paginationVO.setCountRecordSize(true);
            List list = jdbcTemplate.queryForListWithPagination(sql, values, convertTypes(types), paginationVO);

            return makeResultMap(list, paginationVO.getRecordCount(), null);
        } catch (Exception e) {
            throw processException("select by paging SQL", sql, e);
        }
    }

    public Map findWithColInfo(String queryId, Object[] values) throws QueryServiceException {
        return findWithColInfo(queryId, values, -1, -1, false);
    }

    public Map findWithColInfo(String queryId, Object[] values, int pageIndex) throws QueryServiceException {
        return findWithColInfo(queryId, values, pageIndex, -1, true);
    }

    public Map findWithColInfo(String queryId, Object[] values, int pageIndex, int pageSize)
            throws QueryServiceException {
        return findWithColInfo(queryId, values, pageIndex, pageSize, true);
    }

    public Map findWithRowCount(String queryId, Object[] values) throws QueryServiceException {
        return findWithRowCount(queryId, values, -1, -1, false);
    }

    public Map findWithRowCount(String queryId, Object[] values, int pageIndex) throws QueryServiceException {
        return findWithRowCount(queryId, values, pageIndex, -1, true);
    }

    public Map findWithRowCount(String queryId, Object[] values, int pageIndex, int pageSize)
            throws QueryServiceException {
        return findWithRowCount(queryId, values, pageIndex, pageSize, true);
    }

    public int remove(Object obj) throws QueryServiceException {
        String sql = "";
        String className = "";
        try {
            className = obj.getClass().getName();
            IMappingInfo mappingInfo = (IMappingInfo) getSqlRepository().getMappingInfos().get(className);

            sql = mappingInfo.getDeleteQuery();
            return objectCUDExecutor(obj, sql);
        } catch (Exception e) {
            throw processException("delete using object " + className + " defined table mapping", sql, e);
        }
    }

    public int remove(String queryId, Object[] values) throws QueryServiceException {
        IQueryInfo queryInfo = null;
        try {
            containesQueryId(queryId);
            queryInfo = (IQueryInfo) getSqlRepository().getQueryInfos().get(queryId);

            return sqlCUDExecutor(queryInfo, values);
        } catch (Exception e) {
            throw processException("delete [query id = '" + queryId + "']", getQueryString(queryInfo), e);
        }
    }

    public int removeBySQL(String sql, String[] types, Object[] values) throws QueryServiceException {
        try {
            return sqlCUDExecutor(sql, values, convertTypes(types), false, null, null, null);
        } catch (Exception e) {
            throw processException("remove by SQL", sql, e);
        }
    }

    public int update(Object obj) throws QueryServiceException {
        String className = "";
        String sql = "";
        try {
            className = obj.getClass().getName();
            IMappingInfo mappingInfo = (IMappingInfo) getSqlRepository().getMappingInfos().get(className);

            sql = mappingInfo.getUpdateQuery();
            return objectCUDExecutor(obj, sql);
        } catch (Exception e) {
            throw processException("update using object " + className + " defined table mapping", sql, e);
        }
    }

    public int update(String queryId, Object[] values) throws QueryServiceException {
        IQueryInfo queryInfo = null;
        try {
            containesQueryId(queryId);
            queryInfo = (IQueryInfo) getSqlRepository().getQueryInfos().get(queryId);

            return sqlCUDExecutor(queryInfo, values);
        } catch (Exception e) {
            throw processException("update [query id = '" + queryId + "']", getQueryString(queryInfo), e);

        }
    }

    public int updateBySQL(String sql, String[] types, Object[] values) throws QueryServiceException {
        try {
            return sqlCUDExecutor(sql, values, convertTypes(types), false, null, null, null);
        } catch (Exception e) {
            throw processException("update by SQL", sql, e);
        }
    }

    public int countQuery() {
        return getSqlRepository().countQuery();
    }

    public Map getQueryMap() throws QueryServiceException {
        HashMap queryMap = new HashMap();
        try {
            Set keys = getSqlRepository().getQueryInfos().keySet();
            Iterator keyItr = keys.iterator();

            while (keyItr.hasNext()) {
                String queryId = (String) keyItr.next();
                IQueryInfo queryInfo = (IQueryInfo) getSqlRepository().getQueryInfos().get(queryId);
                String statement = queryInfo.getQueryString();
                queryMap.put(queryId, statement);
            }

            return queryMap;
        } catch (Exception e) {
            throw new QueryServiceException(getMessageSource(), "error.query.get.querymap", new Object[] {}, e);
        }
    }

    public ArrayList getQueryParams(String queryId) throws QueryServiceException {
        try {
            IQueryInfo queryInfo = (IQueryInfo) getSqlRepository().getQueryInfos().get(queryId);

            List paramList = queryInfo.getSqlParameterList();

            ArrayList results = new ArrayList();
            for (int i = 0; i < paramList.size(); i++) {
                String[] params = new String[2];
                SqlParameter param = (SqlParameter) paramList.get(i);
                params[0] = param.getName();
                String paramTypeName = param.getTypeName();

                // param type? OTHER ? CURSOR? 
                // SqlOutParameter  param
                // type name?    API ?. ? ?
                // ? ?? ? .
                if (paramTypeName == null) {
                    int type = param.getSqlType();
                    paramTypeName = SQLTypeTransfer.getSQLTypeName(type);
                }

                params[1] = paramTypeName;
                results.add(params);
            }

            return results;
        } catch (Exception e) {
            throw new QueryServiceException(getMessageSource(), "error.query.common.checkparams",
                    new Object[] { queryId }, e);
        }
    }

    public JdbcTemplate getQueryServiceJdbcTemplate() {
        return jdbcTemplate;
    }

    public String getStatement(String queryId) throws QueryServiceException {
        IQueryInfo queryInfo = (IQueryInfo) getSqlRepository().getQueryInfos().get(queryId);
        String sql = queryInfo.getQueryString();
        return sql;
    }

    public IQueryInfo getQueryInfo(String queryId) {
        return (IQueryInfo) getSqlRepository().getQueryInfos().get(queryId);
    }

    /** ************* PROTECTED METHODS ************** */

    /**
     * BatchSqlUpdate ? ? ? SQL?  Batch  .
     * 
     * @param sql
     *            query statement.
     * @param types
     *            is matched with input parameters. A type must belong to fields
     *            defined java.sql.Types package.
     * @param targets
     *            object of class which is matched with specified table in XML
     *            files. is the List type of Object Array.
     * @return an array of the number of rows affected by each statement.
     */
    protected int[] batchExecutor(final String sql, int[] types, final List targets) throws QueryServiceException {
        if (targets.size() <= 0)
            throw new QueryServiceException(getMessageSource(), "error.query.runtime.batch");

        BatchSqlUpdate sqlBatch = new BatchSqlUpdate();
        sqlBatch.setJdbcTemplate(jdbcTemplate);
        sqlBatch.setSql(sql);

        for (int i = 0; types != null && i < types.length; i++) {
            SqlParameter sp = new SqlParameter(types[i]);
            sqlBatch.declareParameter(sp);
        }
        sqlBatch.compile();

        for (int i = 0; i < targets.size(); i++) {
            Object obj = targets.get(i);
            if (obj instanceof Object[])
                sqlBatch.update((Object[]) obj);
        }
        return sqlBatch.flush();
    }

    /**
     * Dynamic SQL?  Spring JdbcTemplate? batchUpdate   Insert,
     * Update, Delete Batch . ( ?? ? ?? ? ?? ? ,  SQL?
     * isDynamic? true? )
     * 
     * @param sql
     *            dynamic query statement.
     * @param targets
     *            object of class which is matched with specified table in XML
     *            files. is the List type of Object Array.
     * @return an array of the number of rows affected by each statement
     */
    protected int[] batchDynamicExecutor(final String sql, final List targets) {
        // NamedParameterUtils   Bind Variables
        // ? Prepared Statement
        //  .
        final ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql);
        return jdbcTemplate.batchUpdate(parsedSql.getNewSql(), new BatchPreparedStatementSetter() {

            public int getBatchSize() {
                return targets.size();
            }

            // Spring JdbcTemplate? ? ?
            // callback method
            public void setValues(PreparedStatement ps, int index) throws SQLException {
                Map properties = new HashMap();
                properties.put("anyframe", targets.get(index));
                // NamedParameterUtils? 
                // inputMap?  dynamic SQL?
                //    Bind Variables? ? 
                //   .
                Object[] args = NamedParameterUtils.buildValueArray(parsedSql,
                        new ExtMapSqlParameterSource(properties));
                // Set the value for the parameter
                for (int i = 0; i < args.length; i++) {
                    StatementCreatorUtils.setParameterValue(ps, i + 1, SqlTypeValue.TYPE_UNKNOWN, null, args[i]);
                }
            }
        });
    }

    /**
     * ? SQL?  Spring JdbcTemplate? batchUpdate   Insert, Update,
     * Delete Batch .  SQL? isDynamic? false? )
     * 
     * @param sql
     *            static query statement.
     * @param targets
     *            object of class which is matched with specified table in XML
     *            files. is the List type of Object Array.
     * @return an array of the number of rows affected by each statement
     */
    protected int[] batchStaticExecutor(final String sql, final List targets) {
        return jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {

            public int getBatchSize() {
                return targets.size();
            }

            // Spring JdbcTemplate? ? ?
            // callback method
            public void setValues(PreparedStatement ps, int index) throws SQLException {
                Object[] args = (Object[]) targets.get(index);
                // Set the value for the parameter
                for (int i = 0; i < args.length; i++) {
                    StatementCreatorUtils.setParameterValue(ps, i + 1, SqlTypeValue.TYPE_UNKNOWN, null, args[i]);
                }
            }
        });
    }

    /**
     * ? SQL Type Name? ? ??  java.sql.Types .
     * 
     * @param types
     *            SQL Type Names
     * @return an array of the SQL Type
     */
    protected int[] convertTypes(String[] types) {
        int[] iTypes = new int[types.length];
        for (int i = 0; i < types.length; i++) {
            iTypes[i] = SQLTypeTransfer.getSQLType(types[i]);
        }
        return iTypes;
    }

    /**
     * ?  ? ???  INSERT, UPDATE, DELETE .  ?? ? SQL?
     * QueryService? ? ?    ?? ?? ?.
     * 
     * @param obj
     *            INSERT, UPDATE, DELETE ?? ? ?
     * @param sql
     *            QueryService? ? ?    ?? ?? query statement
     * @return the number of rows affected
     */
    protected int objectCUDExecutor(Object obj, String sql) {
        Map properties = new HashMap();
        properties.put("anyframe", obj);
        return namedJdbcTemplate.update(sql, new ExtMapSqlParameterSource(properties));
    }

    /**
     *  queryId?      Variable? SQL Type?   
     * sqlCUDExecutor    query statement .
     * 
     * @param queryInfo
     *             queryId?    
     * @param values
     *            a set of variable for executing query
     * @return the number of rows affected
     * @throws Exception
     *              ? ?  
     */
    protected int sqlCUDExecutor(IQueryInfo queryInfo, Object[] values) throws Exception {
        int[] types = null;

        if (!queryInfo.isDynamic()) {
            types = queryInfo.getSqlTypes();
        } else {
            types = generateValueTypes(queryInfo, values);
        }

        return sqlCUDExecutor(queryInfo.getQueryString(), values, types, queryInfo.isDynamic(),
                queryInfo.getLobStatement(), queryInfo.getLobParamTypes(), queryInfo.getQueryId());
    }

    /**
     *  queryId?       ?  .
     * 
     * @param sql
     *            query statement.
     * @param values
     *            a set of variable for executing query
     * @param types
     *            an array of the SQL Type
     * @param isDynamic
     *            dynamic query statement 
     * @param lobStatement
     *            lob statement (update for Handling Lob of Oracle 8i)
     * @param queryId
     *            identifier of query statement to execute
     * @return the number of rows affected
     * @throws Exception
     *              ? ?  
     */
    protected int sqlCUDExecutor(String sql, Object[] values, int[] types, boolean isDynamic, String lobStatement,
            String[] lobParamTypes, String queryId) throws Exception {
        boolean hasLobStatement = false;

        Object[] inputValues = values;
        if (lobStatement != null) {
            hasLobStatement = true;
            if (values.length != 3 || !(values[0] instanceof Object[]) || !(values[1] instanceof Object[])
                    || !(values[2] instanceof Object[])) {
                throw new QueryServiceException(getMessageSource(), "error.query.get.lobvalues");
            }
            inputValues = (Object[]) values[0];
        }

        ExtMapSqlParameterSource sqlParameterSource = null;
        if (isDynamic) {
            sqlParameterSource = new ExtMapSqlParameterSource();
            generatePropertiesMap(values, types, sqlParameterSource);

            sql = getRunnableSQL(sql, sqlParameterSource);

            if (isVelocity(sql)) {
                StringWriter writer = new StringWriter();
                Velocity.evaluate(new ExtMapSqlParameterSourceContext(sqlParameterSource), writer, "QueryService",
                        sql);
                sql = writer.toString();
            }
        }

        // 2008.05.08 - update for Handling Lob of
        // Oracle 8i
        if (hasLobStatement) {
            return jdbcTemplate.update(sql, inputValues, lobHandler, lobStatement, lobParamTypes,
                    (Object[]) values[1], (Object[]) values[2]);
        } else {
            if (isDynamic) {
                return namedJdbcTemplate.update(sql, sqlParameterSource, lobHandler);
            } else {
                if (types == null)
                    return jdbcTemplate.update(sql, values);
                else
                    return jdbcTemplate.update(sql, new PreparedStatementArgTypeSetter(values, types, lobHandler));
            }
        }

    }

    /**
     * ************* PRIVATE Internal METHODS **************
     */

    private Collection find(String queryId, Object[] values, int pageIndex, int pageSize, boolean paging)
            throws QueryServiceException {
        IQueryInfo queryInfo = null;
        try {
            containesQueryId(queryId);
            queryInfo = (IQueryInfo) getSqlRepository().getQueryInfos().get(queryId);

            if (pageSize <= 0)
                pageSize = getSqlRepository().getFetchCountPerQuery(queryId);

            PaginationVO paginationVO = new PaginationVO(pageSize);
            paginationVO.setPaging(paging);
            paginationVO.setPageIndex(pageIndex);

            return findInternal(queryInfo, queryId, values, paginationVO, paging, null);
        } catch (Exception e) {
            throw processException("execute statement [query id = '" + queryId + "']", getQueryString(queryInfo),
                    e);
        }
    }

    private Map findWithRowCount(String queryId, Object[] values, int pageIndex, int pageSize, boolean paging)
            throws QueryServiceException {
        IQueryInfo queryInfo = null;
        try {
            containesQueryId(queryId);
            queryInfo = (IQueryInfo) getSqlRepository().getQueryInfos().get(queryId);

            if (pageSize == -1)
                pageSize = getSqlRepository().getFetchCountPerQuery(queryId);

            PaginationVO paginationVO = new PaginationVO(pageSize);
            paginationVO.setPaging(paging);
            paginationVO.setPageIndex(pageIndex);
            paginationVO.setCountRecordSize(true);

            List list = findInternal(queryInfo, queryId, values, paginationVO, paging, null);

            long count = 0;

            if (!paging)
                count = list.size();
            else
                count = paginationVO.getRecordCount();

            return makeResultMap(list, count, null);
        } catch (Exception e) {
            throw processException("select by paging [query id = '" + queryId + "']", getQueryString(queryInfo), e);
        }
    }

    private Map findWithColInfo(String queryId, Object[] values, int pageIndex, int pageSize, boolean paging)
            throws QueryServiceException {
        IQueryInfo queryInfo = null;
        try {
            containesQueryId(queryId);
            queryInfo = (IQueryInfo) getSqlRepository().getQueryInfos().get(queryId);

            if (pageSize == -1)
                pageSize = getSqlRepository().getFetchCountPerQuery(queryId);

            PaginationVO paginationVO = new PaginationVO(pageSize);
            paginationVO.setPaging(paging);
            paginationVO.setPageIndex(pageIndex);
            paginationVO.setCountRecordSize(true);

            CallbackResultSetMapper rowCallbackHandler = createResultSetMapper(queryInfo, lobHandler,
                    getSqlRepository().getNullCheck());

            List resultList = findInternal(queryInfo, queryId, values, paginationVO, paging, rowCallbackHandler);

            long count = 0;
            if (!paging)
                count = resultList.size();
            else
                count = paginationVO.getRecordCount();
            return makeResultMap(resultList, count, rowCallbackHandler.getColumnInfo());
        } catch (Exception e) {
            throw processException("select with column info [query id = '" + queryId + "']",
                    getQueryString(queryInfo), e);
        }
    }

    /**
     * Exception   ?   ?? ? queryId     ?? 
     * .
     */
    private String getQueryString(IQueryInfo queryInfo) {
        String sql = "";
        if (queryInfo != null)
            sql = queryInfo.getQueryString();
        return sql;
    }

    private List findInternal(IQueryInfo queryInfo, String queryId, Object[] values, PaginationVO paginationVO,
            boolean paging, CallbackResultSetMapper resultSetMapper) throws QueryServiceException {
        String sql = "";

        try {
            sql = queryInfo.getQueryString();
            boolean isDynamic = queryInfo.isDynamic();

            if (resultSetMapper == null)
                resultSetMapper = createResultSetMapper(queryInfo, lobHandler, getSqlRepository().getNullCheck());

            if (isDynamic) {
                Map properties = generatePropertiesMap(values, null, null);
                if (properties == null)
                    properties = new Properties();

                ExtMapSqlParameterSource sqlParameterSource = new ExtMapSqlParameterSource(properties);

                sql = getRunnableSQL(sql, sqlParameterSource);

                if (isVelocity(sql)) {
                    StringWriter writer = new StringWriter();
                    Velocity.evaluate(new ExtMapSqlParameterSourceContext(sqlParameterSource), writer,
                            "QueryService", sql);
                    sql = writer.toString();
                }

                namedJdbcTemplate.query(sql, sqlParameterSource, resultSetMapper, paginationVO);

                return resultSetMapper.getObjects();
            } else {
                if (!paging)
                    return jdbcTemplate.query(sql, values, queryInfo.getSqlTypes(), (RowMapper) resultSetMapper);
                else
                    return jdbcTemplate.queryWithPagination(sql, values, queryInfo.getSqlTypes(),
                            (RowMapper) resultSetMapper, paginationVO);
            }
        } catch (Exception e) {
            throw processException("select [query id = '" + queryId + "']", sql, e);
        }
    }

    /**
     * ************* PRIVATE Other METHODS **************
     */

    private CallbackResultSetMapper createResultSetMapper(IQueryInfo queryInfo, LobHandler lobHandler,
            Map nullchecks) throws Exception {
        Class targetClazz = null;
        // 2009.01.15 - custom resultset mapper
        IResultSetMapper customResultSetMapper = null;
        if (queryInfo.doesNeedColumnMapping()) {
            targetClazz = Thread.currentThread().getContextClassLoader().loadClass(queryInfo.getResultClass());
        } else if (queryInfo.getResultMapper() != null) {
            targetClazz = Thread.currentThread().getContextClassLoader().loadClass(queryInfo.getResultMapper());
            if (IResultSetMapper.class.isAssignableFrom(targetClazz)) {
                customResultSetMapper = (IResultSetMapper) targetClazz.newInstance();
            }
            // 2009.01.15 - custom resultset mapper }
        } else {
            targetClazz = HashMap.class;
        }

        IMappingInfo mappingInfo = this.getSqlRepository().getMappingInfo(queryInfo.getQueryId());
        // 2009.05.28
        CallbackResultSetMapper callbackResultSetMapper = new CallbackResultSetMapper(targetClazz, mappingInfo,
                lobHandler, nullchecks, queryInfo.getMappingStyle());

        // 2009.01.15 - custom resultset mapper
        if (customResultSetMapper != null)
            callbackResultSetMapper.setCustomResultSetMapper(customResultSetMapper);
        // 2009.01.15 - custom resultset mapper
        return callbackResultSetMapper;
    }

    private int[] generateValueTypes(IQueryInfo queryInfo, Object[] values) throws QueryServiceException {
        int[] types = new int[values.length];

        for (int i = 0; i < values.length; i++) {
            String tempStr = null;
            Object[] tempArray = null;
            if (values[i] instanceof String) {
                tempStr = (String) values[i];
                int pos = tempStr.indexOf(DELIMETER);
                if (pos < 0) {
                    // types[i] =
                    // SqlTypeValue.TYPE_UNKNOWN;
                    // continue;
                    throw new QueryServiceException(getMessageSource(), "error.query.generate.valuemap.string");
                }
                types[i] = queryInfo.getSqlType(tempStr.substring(0, pos));
            } else if (values[i] instanceof Object[]) {
                tempArray = (Object[]) values[i];
                if (tempArray.length != 2) {
                    throw new QueryServiceException(getMessageSource(), "error.query.generate.valuemap.array");
                }
                types[i] = queryInfo.getSqlType((String) tempArray[0]);
            } else if (values[i] == null) {
                types[i] = SqlTypeValue.TYPE_UNKNOWN;
            } else {
                types[i] = SqlTypeValue.TYPE_UNKNOWN;
            }

        }

        return types;
    }

    private Map generatePropertiesMap(Object[] values, int[] types, MapSqlParameterSource mapSqlParameterSource)
            throws QueryServiceException {
        Map properties = new HashMap();
        String tempStr = null;
        Object[] tempArray = null;

        for (int i = 0; i < values.length; i++) {
            if (values[i] instanceof String) {
                tempStr = (String) values[i];
                int pos = tempStr.indexOf(DELIMETER);
                if (pos < 0) {
                    throw new QueryServiceException(getMessageSource(), "error.query.generate.valuemap.string");
                }
                properties.put(tempStr.substring(0, pos), tempStr.substring(pos + 1));
                if (mapSqlParameterSource != null)
                    mapSqlParameterSource.addValue(tempStr.substring(0, pos), tempStr.substring(pos + 1), types[i]);
            } else if (values[i] instanceof Object[]) {
                tempArray = (Object[]) values[i];
                if (tempArray.length != 2) {
                    throw new QueryServiceException(getMessageSource(), "error.query.generate.valuemap.array");
                }
                properties.put(tempArray[0], tempArray[1]);
                if (mapSqlParameterSource != null)
                    mapSqlParameterSource.addValue((String) tempArray[0], tempArray[1], types[i]);
            } else if (values[i] == null) {
                continue;
            } else {
                return null;
            }
        }
        return properties;
    }

    private HashMap makeResultMap(List resultList, long totalCount, Map columnInfo) {
        HashMap result = new HashMap();
        result.put(IQueryService.LIST, resultList);
        result.put(IQueryService.COUNT, new Long(totalCount));
        if (columnInfo != null)
            result.put(IQueryService.COL_INFO, columnInfo);
        return result;
    }

    public QueryServiceException processException(String actionName, String sql, Exception exception) {
        IQueryService.LOGGER.error(getMessageSource().getMessage("error.query.runtime.error",
                new Object[] { actionName, sql, exception.getMessage() }, Locale.getDefault()), exception);
        // ??? ? Exception?
        // InternalDataAccessException ? ?  
        //  ? ErrorCode ?.
        if (exception instanceof InternalDataAccessException) {
            InternalDataAccessException idaException = (InternalDataAccessException) exception;
            QueryServiceException queryServiceException = new QueryServiceException(getMessageSource(),
                    "error.query.runtime.error", new Object[] { actionName, sql, exception.getMessage() },
                    idaException);
            queryServiceException.setSqlErrorCode(idaException.getSqlErrorCode());
            queryServiceException.setSqlErrorMessage(idaException.getSqlErrorMessage());
            return queryServiceException;
        }
        if (exception instanceof QueryServiceException)
            return (QueryServiceException) exception;
        else
            return new QueryServiceException(getMessageSource(), "error.query.runtime.error",
                    new Object[] { actionName, sql, exception.getMessage() }, exception);
    }
}