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

io.github.imsejin.mybatis.pagination.support.rebuilder.BoundSqlRebuilder Maven / Gradle / Ivy

package io.github.imsejin.mybatis.pagination.support.rebuilder;

import io.github.imsejin.mybatis.pagination.constant.MapperParameterType;
import io.github.imsejin.mybatis.pagination.constant.RebuildMode;
import io.github.imsejin.mybatis.pagination.model.PageRequest;
import io.github.imsejin.mybatis.pagination.model.Pageable;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeException;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @see RebuildMode#WRAP
 */
public class BoundSqlRebuilder implements Rebuilder {

    private final BoundSql boundSql;
    private final RebuildMode rebuildMode;
    private final MapperParameterType mapperParameterType;
    private Object parameterObject;
    private Configuration config;
    private String sql;
    private List parameterMappings;

    BoundSqlRebuilder(BoundSql boundSql, RebuildMode rebuildMode) {
        this.boundSql = boundSql;
        this.rebuildMode = rebuildMode;
        this.mapperParameterType = MapperParameterType.from(boundSql);
        this.parameterObject = boundSql.getParameterObject();
    }

    public BoundSqlRebuilder config(Configuration config) {
        this.config = config;
        return this;
    }

    public BoundSqlRebuilder sql(String sql) {
        this.sql = sql;
        return this;
    }

    public BoundSqlRebuilder parameterMappings(List parameterMappings) {
        this.parameterMappings = parameterMappings;
        return this;
    }

    @Override
    public BoundSql rebuild() {
        // Sets options from original bound SQL.
        if (this.parameterMappings == null) this.parameterMappings = this.boundSql.getParameterMappings();

        // Converts the parameter type of mapper method to "java.util.Map".
        if (this.rebuildMode == RebuildMode.WRAP && this.mapperParameterType == MapperParameterType.SINGLE) {
            this.parameterObject = wrap();
        }

        return new BoundSql(this.config, this.sql, this.parameterMappings, this.parameterObject);
    }

    @SuppressWarnings("unchecked")
    private Object wrap() {
        Object parameterObject = this.parameterObject;

        // Checks if additional parameters exist.
        Map additionalParameterMap = getAdditionalParameters();

        // Finds additional parameters and merges them with static parameters as total parameters.
        if (!additionalParameterMap.isEmpty()) {
            switch (this.mapperParameterType) {
                case NONE:
                    return null;

                case SINGLE:
                    // Merges the additional parameters with query of PageRequest.
                    Pageable pageable = (Pageable) parameterObject;

                    if (pageable instanceof PageRequest) {
                        PageRequest pageRequest = (PageRequest) pageable;
                        Map param = new HashMap<>(pageRequest.getQuery());
                        param.putAll(additionalParameterMap);

                        param.forEach(this.boundSql::setAdditionalParameter);

                        // Replaces non-additional ParameterMapping
                        // with new one that has property with "query." removed.
                        String prefix = PageRequest.QUERY_PROPERTY_NAME + '.';
                        for (int i = 0; i < this.parameterMappings.size(); i++) {
                            ParameterMapping mapping = this.parameterMappings.get(i);
                            if (!mapping.getProperty().startsWith(prefix)) continue;

                            String newProperty = mapping.getProperty().substring(prefix.length());
                            ParameterMapping newMapping = new ParameterMapping.Builder(
                                    this.config, newProperty, mapping.getJavaType())
                                    .mode(mapping.getMode()).build();

                            this.parameterMappings.set(i, newMapping);
                        }

                        // Substitutes a merged map for parameter object.
                        return param;
                    }

                    break;

                case MULTIPLE:
                    // Merges the additional parameters with a instance of ParamMap.
                    Map param = (MapperMethod.ParamMap) parameterObject;
                    param.putAll(additionalParameterMap);

                    // Substitutes a merged map for parameter object.
                    return param;

                default:
                    throw new TypeException("Failed to find the number of parameters: " + parameterObject);
            }
        }

        return parameterObject;
    }

    private Map getAdditionalParameters() {
        Map additionalParameters = new HashMap<>();

        for (ParameterMapping parameterMapping : this.parameterMappings) {
            String property = parameterMapping.getProperty();
            if (!this.boundSql.hasAdditionalParameter(property)) continue;

            additionalParameters.put(property, this.boundSql.getAdditionalParameter(property));
        }

        return additionalParameters;
    }

}