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

com.alilitech.mybatis.jpa.statement.PreMapperStatementBuilder Maven / Gradle / Ivy

The newest version!
/*
 *    Copyright 2017-2022 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 com.alilitech.mybatis.jpa.statement;

import com.alilitech.mybatis.dialect.SqlDialectFactory;
import com.alilitech.mybatis.jpa.EntityMetaDataRegistry;
import com.alilitech.mybatis.jpa.definition.GenericType;
import com.alilitech.mybatis.jpa.definition.JoinStatementDefinition;
import com.alilitech.mybatis.jpa.definition.MethodDefinition;
import com.alilitech.mybatis.jpa.meta.ColumnMetaData;
import com.alilitech.mybatis.jpa.meta.EntityMetaData;
import com.alilitech.mybatis.jpa.parameter.GenerationType;
import com.alilitech.mybatis.jpa.parameter.TriggerValue4Jdbc3KeyGenerator;
import com.alilitech.mybatis.jpa.parameter.TriggerValue4NoKeyGenerator;
import com.alilitech.mybatis.jpa.parameter.TriggerValue4SelectKeyGenerator;
import com.alilitech.mybatis.jpa.primary.key.KeyGenerator4Auto;
import com.alilitech.mybatis.jpa.statement.parser.PartTree;
import com.alilitech.mybatis.jpa.statement.parser.SimplePart;
import com.alilitech.mybatis.jpa.util.ResultMapIdUtils;
import org.apache.ibatis.builder.BaseBuilder;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;


/**
 *
 * @author Zhou Xiaoxiang
 * @since 1.0
 */
public abstract class PreMapperStatementBuilder extends BaseBuilder {

    protected Log log = LogFactory.getLog(this.getClass());

    protected MapperBuilderAssistant builderAssistant;

    protected MethodDefinition methodDefinition;

    protected EntityMetaData entityMetaData;

    protected SqlDialectFactory sqlDialectFactory;

    protected MethodType methodType;

    //设置返回值,自定义查询的时候部分需要确定返回值
    protected Class resultType;

    protected PreMapperStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, MethodType methodType) {
        super(configuration);
        this.methodType = methodType;
        this.builderAssistant = builderAssistant;
        this.sqlDialectFactory = new SqlDialectFactory(configuration.getDatabaseId());
    }

    public PreMapperStatement buildPreMapperStatement(MethodDefinition methodDefinition, GenericType genericType) {
        this.methodDefinition = methodDefinition;
        this.entityMetaData = EntityMetaDataRegistry.getInstance().get(genericType.getDomainType());

        PreMapperStatement preMapperStatement = new PreMapperStatement();

        String id = methodDefinition.getStatementId();
        LanguageDriver langDriver = getLanguageDriver(null);

        String scriptString = buildSQL();
        if(log.isTraceEnabled()) {
            log.trace("script==>" + scriptString);
        }
        SqlSource sqlSource = this.buildSqlSource(scriptString, langDriver);

        preMapperStatement.setId(id);
        preMapperStatement.setLang(langDriver);
        preMapperStatement.setSqlSource(sqlSource);
        preMapperStatement.setParameterType(getParameterTypeClass());

        // dynamic & has parameters
        preMapperStatement.setStatementType(StatementType.PREPARED);
        // unknown
        preMapperStatement.setResultSetType(ResultSetType.FORWARD_ONLY);

        buildPreMapperStatementExtend(preMapperStatement, genericType);

        boolean isSelect = preMapperStatement.getSqlCommandType() == SqlCommandType.SELECT;
        boolean flushCache = !isSelect;
        boolean useCache = isSelect;

        preMapperStatement.setFlushCache(flushCache);
        preMapperStatement.setUseCache(useCache);

        return preMapperStatement;
    }

    protected abstract void buildPreMapperStatementExtend(PreMapperStatement preMapperStatement, GenericType genericType);

    protected abstract String buildSQL() ;

    protected abstract Class getParameterTypeClass();

    /**
     * create mybatis SqlSource
     */
    private SqlSource buildSqlSource(String sqlScript, LanguageDriver languageDriver) {
        return languageDriver.createSqlSource(configuration, sqlScript, getParameterTypeClass());
    }

    protected LanguageDriver getLanguageDriver(String lang) {
        Class langClass = null;
        if (lang != null) {
            langClass = resolveClass(lang);
        }
        return builderAssistant.getConfiguration().getLanguageDriver(langClass);
    }

    /**
     * build part tree
     * @return
     */
    protected PartTree buildPartTree() {
        String expression = methodDefinition.getMethodName();

        //将ById 转换成 ByPrimaryKey
        if(ParameterType.ID == methodType.getParameterType()) {
            String paramExpression = entityMetaData.getPrimaryCondition();
            expression = expression.replace("ById", "By" + paramExpression);
        }

        return new PartTree(expression, entityMetaData.getEntityType(), methodDefinition);
    }

    protected String buildPrimaryCondition() {
        return entityMetaData.getPrimaryColumnMetaDatas().stream().map(columnMetaData -> columnMetaData.getColumnName() + " = " + "#{" + columnMetaData.getProperty() + "}").collect(Collectors.joining(" AND "));
    }

    protected String buildPrimaryCondition(String alias) {
        return entityMetaData.getPrimaryColumnMetaDatas().stream().map(columnMetaData -> alias + "." + columnMetaData.getColumnName() + " = " + "#{" + columnMetaData.getProperty() + "}").collect(Collectors.joining(" AND "));
    }

    /**
     * build simple predicate part
     * @param property
     * @return
     */
    protected SimplePart buildSimplePart(String property) {
        return new SimplePart(property, entityMetaData.getEntityType(), methodDefinition);
    }

    /**
     * build sort sql script
     */
    protected String buildSort(String tableAlias) {
        String columnPrefix = tableAlias == null ? "" : tableAlias + ".";
        if(methodDefinition.getSortIndex() > -1) {
            String paramName = methodDefinition.isOneParameter() ? "_parameter" : ("arg" + methodDefinition.getSortIndex());
            StringBuilder orderString = new StringBuilder()
                    .append("")
                    .append("")
                    .append(columnPrefix)
                    .append("${item.property} ${item.direction}")
                    .append("")
                    .append("");
            return orderString.toString();
        } else {
            return "";
        }
    }

    /**
     * build mybatis script
     */
    protected String buildScript(List sqlParts) {
        return sqlParts.stream().filter(part -> !part.isEmpty()).collect(Collectors.joining(" ", ""));
//        StringBuilder script = new StringBuilder()
//                .append("");
//        return script.toString();
    }

    /**
     * 设置无key生成器
     */
    protected void setNoKeyGenerator(PreMapperStatement preMapperStatement) {
        preMapperStatement.setKeyGenerator(new TriggerValue4NoKeyGenerator());
        preMapperStatement.setKeyColumn(null);
        preMapperStatement.setKeyProperty(null);
    }

    /**
     * set jdbc key generator
     */
    protected void setTriggerValue4Jdbc3KeyGenerator(PreMapperStatement preMapperStatement) {
        preMapperStatement.setKeyGenerator(new TriggerValue4Jdbc3KeyGenerator());
        preMapperStatement.setKeyColumn(entityMetaData.getPrimaryColumnMetaData().getColumnName());
        preMapperStatement.setKeyProperty(entityMetaData.getPrimaryColumnMetaData().getProperty());
    }

    /**
     * set jdbc key generator, but not callback
     */
    protected void setTriggerValue4Jdbc3KeyGeneratorButNotCallBack(PreMapperStatement preMapperStatement) {
        preMapperStatement.setKeyGenerator(new TriggerValue4Jdbc3KeyGenerator());
        preMapperStatement.setKeyColumn(null);
        preMapperStatement.setKeyProperty(null);
    }

    /**
     * set select(sequence) key generator, need execute the sql
     */
    protected void setTriggerValue4SelectKeyGenerator(PreMapperStatement preMapperStatement) {
        preMapperStatement.setKeyGenerator(buildSelectKeyGenerator(entityMetaData.getPrimaryColumnMetaData(), true));
        preMapperStatement.setKeyColumn(entityMetaData.getPrimaryColumnMetaData().getColumnName());
        preMapperStatement.setKeyProperty(entityMetaData.getPrimaryColumnMetaData().getProperty());
    }

    /**
     * 只有insert或update的时候才有key和触发值
     */
    protected void setKeyGeneratorAndTriggerValue(PreMapperStatement preMapperStatement) {

        //未指定主键或复合主键
        if(entityMetaData.getPrimaryColumnMetaData() == null) {
            setNoKeyGenerator(preMapperStatement);
        }
        //指定了id class优先用自定义id class去生成
        else if(entityMetaData.getPrimaryColumnMetaData().getIdGeneratorClass() != KeyGenerator4Auto.class) {
            setTriggerValue4Jdbc3KeyGenerator(preMapperStatement);
        }
        //下面都是没有自定义id class
        else if(entityMetaData.getPrimaryColumnMetaData().getIdGenerationType() == GenerationType.AUTO){
            setNoKeyGenerator(preMapperStatement);
        }
        //自增,指定回调
        else if(entityMetaData.getPrimaryColumnMetaData().getIdGenerationType() == GenerationType.IDENTITY) {
            setTriggerValue4Jdbc3KeyGenerator(preMapperStatement);
        }
        //主键不是序列,并且不是自增,没有回调,否则一些数据库会出此置空ID的情况
        else if(entityMetaData.getPrimaryColumnMetaData().getIdGenerationType() != GenerationType.SEQUENCE) {
            setTriggerValue4Jdbc3KeyGeneratorButNotCallBack(preMapperStatement);
        }
        //主键是序列
        else {
            setTriggerValue4SelectKeyGenerator(preMapperStatement);
        }
    }

    /**
     * find语句提供ResultType或id
     */
    protected void setFindResultIdOrType(PreMapperStatement preMapperStatement, GenericType genericType) {
        if(resultType != null) {
            preMapperStatement.setResultType(resultType);
        } else if(methodDefinition.isBaseResultMap() && methodDefinition.isJoinMethod()) {
            String resultMapId = buildJoinResultMap();
            preMapperStatement.setResultMap(resultMapId);
        } else if(methodDefinition.isBaseResultMap()) {
            preMapperStatement.setResultMap(ResultMapIdUtils.buildBaseResultMapId(builderAssistant));
        } else if(methodDefinition.isCompositeResultMap()) {
            String resultMapId = buildCompositeResultMap();
            preMapperStatement.setResultMap(resultMapId);
        } else {
            preMapperStatement.setResultType((Class)genericType.getDomainType());
        }
    }

    private String buildJoinResultMap() {
        String resultMapId = builderAssistant.getCurrentNamespace() + "." + methodDefinition.getMethodName() + "ResultMap";
        if(configuration.hasResultMap(resultMapId)) {
            return resultMapId;
        }

        List resultMappings = methodDefinition.getColumnDefinitions().stream().map(columnDefinition ->
                builderAssistant.buildResultMapping(
                        columnDefinition.getJavaType(),
                        columnDefinition.getProperty(),
                        columnDefinition.getColumnName(),
                        columnDefinition.getJavaType(),
                        columnDefinition.getJdbcType(),
                        null,
                        null,
                        null,
                        null,
                        columnDefinition.getTypeHandler(),
                        columnDefinition.isId() ? Collections.singletonList(ResultFlag.ID) : null
                )).collect(Collectors.toList());

        builderAssistant.addResultMap(
                resultMapId,
                entityMetaData.getEntityType(),
                null,
                null,
                resultMappings,
                true
        );
        return resultMapId;
    }

    /**
     * 生成复合查询的ResultMap
     */
    private String buildCompositeResultMap() {

        String resultMapId = builderAssistant.getCurrentNamespace() + "." + methodDefinition.getMethodName() + "ResultMap";
        if(configuration.hasResultMap(resultMapId)) {
            return resultMapId;
        }

        List mappings = new ArrayList<>();

        for (JoinStatementDefinition joinStatementDefinition : methodDefinition.getJoinStatementDefinitions()) {

//            if(resultMapId.equals("com.alilitech.mybatis.jpa.test.mapper.TestUserMapper.findByIdResultMap")) {
                ResultMapping resultMapping = builderAssistant.buildResultMapping(
                        joinStatementDefinition.getResultType(),
                        joinStatementDefinition.getProperty(),
                        null,
                        joinStatementDefinition.getJavaType(),
                        null,
                        null,
                        joinStatementDefinition.getNestedSelect() + "ResultMap",
                        null,
                        null,
                        null,
                        null
                );
                mappings.add(resultMapping);
//            } else {
//
//                ResultMapping resultMapping = builderAssistant.buildResultMapping(
//                        joinStatementDefinition.getResultType(),
//                        joinStatementDefinition.getProperty(),
//                        joinStatementDefinition.getColumn(),
//                        joinStatementDefinition.getJavaType(),
//                        null,
//                        joinStatementDefinition.getNestedSelect(),
//                        null,
//                        null,
//                        null,
//                        null,
//                        null
//                );
//                mappings.add(resultMapping);
//            }
        }

        ResultMap resultMap = builderAssistant.addResultMap(
                resultMapId,
                entityMetaData.getEntityType(),
                ResultMapIdUtils.buildBaseResultMapId(builderAssistant),
                null,
                mappings,
                true
        );

        return resultMap.getId();
    }

    private KeyGenerator buildSelectKeyGenerator(ColumnMetaData columnMetaData, boolean executeBefore) {

        String id = "BaseKey" + SelectKeyGenerator.SELECT_KEY_SUFFIX + executeBefore;

        if(configuration.hasStatement(builderAssistant.applyCurrentNamespace(id, false))) {
            return configuration.getKeyGenerator(builderAssistant.applyCurrentNamespace(id, false));
        }

        Class resultTypeClass = columnMetaData.getType();
        StatementType statementType = StatementType.PREPARED;
        String keyProperty = columnMetaData.getProperty();
        String keyColumn = columnMetaData.getColumnName();

        // defaults
        boolean useCache = false;
        KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
        Integer fetchSize = null;
        Integer timeout = null;
        boolean flushCache = false;
        String parameterMap = null;
        String resultMap = null;
        ResultSetType resultSetTypeEnum = null;

        SqlSource sqlSource = getLanguageDriver(null).createSqlSource(builderAssistant.getConfiguration(),
                sqlDialectFactory.buildKeyGeneratorScript(columnMetaData.getSequenceName()), null);
        SqlCommandType sqlCommandType = SqlCommandType.SELECT;

        builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, null, resultMap, resultTypeClass, resultSetTypeEnum,
                flushCache, useCache, false,
                keyGenerator, keyProperty, keyColumn, null, getLanguageDriver(null), null);

        id = builderAssistant.applyCurrentNamespace(id, false);

        MappedStatement keyStatement = configuration.getMappedStatement(id, false);
        SelectKeyGenerator answer = new TriggerValue4SelectKeyGenerator(keyStatement, executeBefore);
        configuration.addKeyGenerator(id, answer);

        return answer;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy