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

org.dbflute.s2dao.sqlhandler.TnProcedureHandler Maven / Gradle / Ivy

There is a newer version: 1.2.8
Show newest version
/*
 * Copyright 2014-2021 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 org.dbflute.s2dao.sqlhandler;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import javax.sql.DataSource;

import org.dbflute.bhv.exception.SQLExceptionResource;
import org.dbflute.jdbc.FetchBean;
import org.dbflute.jdbc.StatementFactory;
import org.dbflute.jdbc.ValueType;
import org.dbflute.s2dao.jdbc.TnFetchAssistResultSet;
import org.dbflute.s2dao.jdbc.TnResultSetHandler;
import org.dbflute.s2dao.metadata.TnProcedureMetaData;
import org.dbflute.s2dao.metadata.TnProcedureParameterType;

/**
 * @author modified by jflute (originated in S2Dao)
 */
public class TnProcedureHandler extends TnAbstractBasicSqlHandler {

    // ===================================================================================
    //                                                                           Attribute
    //                                                                           =========
    private TnProcedureMetaData _procedureMetaData;
    private TnProcedureResultSetHandlerProvider _resultSetHandlerProvider;

    // ===================================================================================
    //                                                                         Constructor
    //                                                                         ===========
    public TnProcedureHandler(DataSource dataSource, StatementFactory statementFactory, String sql, TnProcedureMetaData procedureMetaData,
            TnProcedureResultSetHandlerProvider resultSetHandlerProvider) {
        super(dataSource, statementFactory, sql);
        assertObjectNotNull("procedureMetaData", procedureMetaData);
        assertObjectNotNull("resultSetHandlerProvider", resultSetHandlerProvider);
        _procedureMetaData = procedureMetaData;
        _resultSetHandlerProvider = resultSetHandlerProvider;
    }

    public static interface TnProcedureResultSetHandlerProvider { // is needed to construct an instance
        TnResultSetHandler provideResultSetHandler(TnProcedureParameterType ppt);
    }

    // ===================================================================================
    //                                                                             Execute
    //                                                                             =======
    public Object execute(final Object[] args) {
        final Class[] argTypes = getArgTypes(args);
        final Object pmb = getParameterBean(args);
        logSql(args, argTypes);
        Connection conn = null;
        CallableStatement cs = null;
        try {
            conn = getConnection();
            cs = prepareCall(conn);
            bindArgs(conn, cs, pmb);

            // Execute the procedure!
            // The return means whether the first result is a (not-parameter) result set.
            final boolean executed = executeProcedure(cs);

            handleNotParamResult(conn, cs, pmb, executed); // should be before out-parameter handling
            handleOutParameter(conn, cs, pmb, executed);
            return pmb;
        } catch (SQLException e) {
            final SQLExceptionResource resource = createSQLExceptionResource();
            resource.setNotice("Failed to execute the procedure.");
            resource.enableUniqueConstraintHandling();
            handleSQLException(e, resource);
            return null; // unreachable
        } finally {
            close(cs);
            close(conn);
        }
    }

    protected Object getParameterBean(Object[] args) {
        if (args.length == 0) {
            return null;
        }
        if (args.length == 1) {
            if (args[0] == null) {
                throw new IllegalStateException("args[0] should not be null!");
            }
            return args[0];
        }
        throw new IllegalStateException("The size of args should be 1: " + args.length);
    }

    protected void bindArgs(Connection conn, CallableStatement cs, Object dto) throws SQLException {
        if (dto == null) {
            return;
        }
        int i = 0;
        for (TnProcedureParameterType ppt : _procedureMetaData.getBindParameterTypeList()) {
            final ValueType valueType = ppt.getValueType();
            final int bindIndex = (i + 1);
            // if INOUT parameter, both are true
            if (ppt.isOutType()) {
                valueType.registerOutParameter(conn, cs, bindIndex);
            }
            if (ppt.isInType()) {
                // bind as PreparedStatement
                // because CallableStatement's setter might be unsupported
                // (for example, PostgreSQL JDBC Driver for JDBC 3.0)
                final Object value = ppt.getValue(dto);
                valueType.bindValue(conn, cs, bindIndex, value);
            }
            // either must be true
            ++i;
        }
    }

    /**
     * Handle not-parameter result set, for example, MySQL, DB2 and (MS) SQLServer.
     * @param conn The connection for the database. (NotNull)
     * @param cs The statement of procedure. (NotNull)
     * @param pmb The parameter bean from arguments. (NotNull)
     * @param executed The return value of execute() that means whether the first result is a result set. 
     * @throws SQLException When it fails to handle the SQL.
     */
    @SuppressWarnings("resource")
    protected void handleNotParamResult(Connection conn, CallableStatement cs, Object pmb, boolean executed) throws SQLException {
        if (pmb == null) {
            return;
        }
        if (!executed) {
            if (!cs.getMoreResults()) { // just in case
                return;
            }
        }
        final List resultList = _procedureMetaData.getNotParamResultTypeList();
        ResultSet rs = null;
        for (TnProcedureParameterType ppt : resultList) {
            try {
                rs = cs.getResultSet();
                if (rs == null) {
                    break;
                }
                rs = wrapResultSetIfNeeds(pmb, rs);
                final TnResultSetHandler handler = createResultSetHandler(pmb, ppt);
                final Object beanList = handler.handle(rs);
                ppt.setValue(pmb, beanList);
                if (!cs.getMoreResults()) {
                    break;
                }
            } finally {
                if (rs != null) {
                    rs.close();
                }
            }
        }
    }

    /**
     * Handle result set for out-parameter.
     * @param conn The connection for the database. (NotNull)
     * @param cs The statement of procedure. (NotNull)
     * @param pmb The parameter bean from arguments. (NotNull)
     * @param executed The return value of execute() that means whether the first result is a result set.
     * @throws SQLException When it fails to handle the SQL.
     */
    protected void handleOutParameter(Connection conn, CallableStatement cs, Object pmb, boolean executed) throws SQLException {
        if (pmb == null) {
            return;
        }
        int index = 0;
        for (TnProcedureParameterType ppt : _procedureMetaData.getBindParameterTypeList()) {
            final ValueType valueType = ppt.getValueType();
            if (ppt.isOutType()) {
                Object value = valueType.getValue(cs, index + 1);
                if (value instanceof ResultSet) {
                    final ResultSet rs = wrapResultSetIfNeeds(pmb, (ResultSet) value);
                    final TnResultSetHandler handler = createResultSetHandler(pmb, ppt);
                    try {
                        value = handler.handle(rs);
                    } finally {
                        if (rs != null) {
                            rs.close();
                        }
                    }
                }
                ppt.setValue(pmb, value);
            }
            ++index;
        }
    }

    // ===================================================================================
    //                                                                          DisplaySql
    //                                                                          ==========
    @Override
    protected String buildDisplaySql(String sql, Object[] args) { // for procedure call
        if (args == null) { // basically no way, just in case
            return sql;
        }
        final Object dto = getParameterBean(args);
        if (dto == null) {
            return sql;
        }
        final StringBuilder sb = new StringBuilder(100);
        int pos = 0;
        int pos2 = 0;
        for (TnProcedureParameterType ppt : _procedureMetaData.getBindParameterTypeList()) {
            if ((pos2 = sql.indexOf('?', pos)) < 0) {
                break;
            }
            sb.append(sql.substring(pos, pos2));
            pos = pos2 + 1;
            if (ppt.isInType()) {
                sb.append(getBindVariableText(ppt.getValue(dto)));
            } else {
                sb.append(sql.substring(pos2, pos));
            }
        }
        sb.append(sql.substring(pos));
        return sb.toString();
    }

    // ===================================================================================
    //                                                                    ResultSetHandler
    //                                                                    ================
    protected TnResultSetHandler createResultSetHandler(Object pmb, TnProcedureParameterType ppt) {
        return _resultSetHandlerProvider.provideResultSetHandler(ppt);
    }

    protected ResultSet wrapResultSetIfNeeds(Object pmb, ResultSet rs) {
        if (pmb instanceof FetchBean) {
            final FetchBean fcbean = (FetchBean) pmb;
            final int safetyMaxResultSize = fcbean.getSafetyMaxResultSize();
            if (safetyMaxResultSize > 0) { // wrap for check safety
                return new TnFetchAssistResultSet(rs, fcbean, false, false);
            }
        }
        return rs;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy