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

com.wavemaker.runtime.data.dao.callbacks.NativeProcedureExecutor Maven / Gradle / Ivy

There is a newer version: 11.9.2.ee
Show newest version
/*******************************************************************************
 * Copyright (C) 2022-2023 WaveMaker, Inc.
 *
 * 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 com.wavemaker.runtime.data.dao.callbacks;

import java.io.ByteArrayInputStream;
import java.io.StringReader;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.springframework.orm.hibernate5.HibernateOperations;

import com.wavemaker.commons.MessageResource;
import com.wavemaker.commons.WMRuntimeException;
import com.wavemaker.runtime.data.dao.procedure.parameters.ResolvableParam;
import com.wavemaker.runtime.data.model.JavaType;
import com.wavemaker.runtime.data.model.procedures.ProcedureParameter;
import com.wavemaker.runtime.data.transform.Transformers;
import com.wavemaker.runtime.data.transform.WMResultTransformer;
import com.wavemaker.runtime.data.util.JDBCUtils;

/**
 * @author Dilip Kumar
 * @since 16/11/16
 */
public class NativeProcedureExecutor {

    private NativeProcedureExecutor() {
    }

    public static final String CONTENT_FIELD = "content";

    public static  T execute(HibernateOperations hibernateOperations, String jdbcQuery, List params, Class type) {
        return hibernateOperations.execute(session -> session.doReturningWork(connection -> {
            CallableStatement statement = prepareStatement(connection, jdbcQuery, params);
            boolean resultSetType = statement.execute();
            Map result = getResultMap(statement, params, resultSetType, 0);
            return convert(result, type);
        }));
    }

    public static CallableStatement prepareStatement(
        Connection connection, String jdbcQuery, List params) throws SQLException {
        final CallableStatement statement = connection.prepareCall(jdbcQuery);
        configureParameters(statement, params);
        return statement;
    }

    public static Map getResultMap(
        final CallableStatement statement, final List params,
        final boolean resultSetType, final int limit) throws SQLException {
        Map result = new LinkedHashMap<>();
        if (resultSetType) {
            result.put(CONTENT_FIELD, readResultSet(statement.getResultSet(), limit));
        }
        result.putAll(readResponse(statement, params, limit));
        return result;
    }

    public static List convertToOldResponse(Map result) {
        List response = Collections.singletonList(result);
        if (result.keySet().size() == 1) {
            final Object firstValue = result.values().iterator().next();
            if (firstValue instanceof List) {
                response = (List) firstValue;
            }
        }
        return response;
    }

    @SuppressWarnings(value = "unchecked")
    protected static  T convert(final Map object, final Class type) {
        final WMResultTransformer transformer = Transformers.aliasToMappedClass(type);
        return (T) transformer.transformFromMap(object);
    }

    protected static void configureParameters(
        final CallableStatement statement, final List params) throws SQLException {
        for (int i = 0; i < params.size(); i++) {
            final ResolvableParam param = params.get(i);
            if (param.getParameter().getParameterType().isOutParam()) {
                statement.registerOutParameter(i + 1, JDBCUtils.getSqlTypeCode(param.getParameter().getType()));
            }
            if (param.getParameter().getParameterType().isInParam()) {
                // not checking required flag in parameter since spring will handle this in deserialization.
                if (param.getValue() != null) {
                    // XXX these are expected input types wrt JavaType. In future we may have to handle different types.
                    if (param.getParameter().getType() == JavaType.BLOB) {
                        statement.setBlob(i + 1, new ByteArrayInputStream((byte[]) param.getValue()));
                    } else if (param.getParameter().getType() == JavaType.CLOB) {
                        statement.setClob(i + 1, new StringReader((String) param.getValue()));
                    } else {
                        statement.setObject(i + 1, param.getValue(),
                            JDBCUtils.getSqlTypeCode(param.getParameter().getType()));
                    }
                } else {
                    statement.setNull(i + 1, JDBCUtils.getSqlTypeCode(param.getParameter().getType()));
                }
            }
        }
    }

    private static Map readResponse(
        CallableStatement statement, final List params, final int limit) throws SQLException {
        Map result = new LinkedHashMap<>();

        for (int i = 0; i < params.size(); i++) {
            final ResolvableParam param = params.get(i);

            final ProcedureParameter parameter = param.getParameter();
            if (parameter.getParameterType().isOutParam()) {
                Object value = parameter.getType() == JavaType.BLOB ?
                    statement.getBlob(i + 1) :
                    statement.getObject(i + 1);
                if (parameter.getType() == JavaType.CURSOR) {
                    value = readResultSet(value, limit);
                }
                result.put(parameter.getName(), value);
            }
        }

        return result;
    }

    private static List> readResultSet(Object resultSet, final int limit) {
        List> result = new ArrayList<>();
        // Dump the cursor
        try {
            if (resultSet != null) {
                ResultSet rset = (ResultSet) resultSet;
                int row = 0;
                while ((limit < 1 || row++ < limit) && rset.next()) {
                    Map rowData = new LinkedHashMap<>();
                    int colCount = rset.getMetaData().getColumnCount();
                    for (int i = 1; i <= colCount; i++) {
                        rowData.put(rset.getMetaData().getColumnLabel(i), rset.getObject(i));
                    }
                    result.add(rowData);
                }
            }
        } catch (SQLException e) {
            throw new WMRuntimeException(MessageResource.create("com.wavemaker.runtime.error.while.executing.procedure"), e);
        }

        return result;
    }
}