001/*
002 *  Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
003 *  <p>
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *  <p>
008 *  http://www.apache.org/licenses/LICENSE-2.0
009 *  <p>
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package com.mybatisflex.core.mybatis;
017
018import com.mybatisflex.annotation.Table;
019import com.mybatisflex.core.FlexConsts;
020import com.mybatisflex.core.handler.CompositeEnumTypeHandler;
021import com.mybatisflex.core.keygen.MultiEntityKeyGenerator;
022import com.mybatisflex.core.keygen.MultiRowKeyGenerator;
023import com.mybatisflex.core.keygen.MybatisKeyGeneratorUtil;
024import com.mybatisflex.core.keygen.RowKeyGenerator;
025import com.mybatisflex.core.mybatis.binding.FlexMapperRegistry;
026import com.mybatisflex.core.mybatis.executor.FlexBatchExecutor;
027import com.mybatisflex.core.mybatis.executor.FlexReuseExecutor;
028import com.mybatisflex.core.mybatis.executor.FlexSimpleExecutor;
029import com.mybatisflex.core.table.TableInfo;
030import com.mybatisflex.core.table.TableInfoFactory;
031import com.mybatisflex.core.util.MapUtil;
032import com.mybatisflex.core.util.StringUtil;
033import org.apache.ibatis.binding.MapperRegistry;
034import org.apache.ibatis.executor.CachingExecutor;
035import org.apache.ibatis.executor.Executor;
036import org.apache.ibatis.executor.keygen.KeyGenerator;
037import org.apache.ibatis.executor.keygen.NoKeyGenerator;
038import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
039import org.apache.ibatis.executor.parameter.ParameterHandler;
040import org.apache.ibatis.executor.resultset.ResultSetHandler;
041import org.apache.ibatis.executor.statement.StatementHandler;
042import org.apache.ibatis.mapping.BoundSql;
043import org.apache.ibatis.mapping.Environment;
044import org.apache.ibatis.mapping.MappedStatement;
045import org.apache.ibatis.mapping.ResultMap;
046import org.apache.ibatis.session.*;
047import org.apache.ibatis.transaction.Transaction;
048
049import java.lang.reflect.ParameterizedType;
050import java.lang.reflect.Type;
051import java.util.Collections;
052import java.util.List;
053import java.util.Map;
054import java.util.concurrent.ConcurrentHashMap;
055
056/**
057 * @author michael
058 * @author life
059 */
060public class FlexConfiguration extends Configuration {
061
062    private static final Map<String, MappedStatement> dynamicMappedStatementCache = new ConcurrentHashMap<>();
063    private final MapperRegistry mapperRegistry = new FlexMapperRegistry(this);
064
065    public FlexConfiguration() {
066        setObjectWrapperFactory(new FlexWrapperFactory());
067        setDefaultEnumTypeHandler(CompositeEnumTypeHandler.class);
068    }
069
070
071    public FlexConfiguration(Environment environment) {
072        super(environment);
073        setObjectWrapperFactory(new FlexWrapperFactory());
074        setDefaultEnumTypeHandler(CompositeEnumTypeHandler.class);
075    }
076
077    /**
078     * 为原生 sql 设置参数
079     */
080    @Override
081    public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
082        String mappedStatementId = mappedStatement.getId();
083        /**
084         *  以 "!selectKey" 结尾的 mappedStatementId,是用于 Sequence 生成主键的,无需为其设置参数
085         *  {@link SelectKeyGenerator#SELECT_KEY_SUFFIX}
086         */
087        if (!mappedStatementId.endsWith(SelectKeyGenerator.SELECT_KEY_SUFFIX)
088            && parameterObject instanceof Map
089            && ((Map<?, ?>) parameterObject).containsKey(FlexConsts.SQL_ARGS)) {
090            SqlArgsParameterHandler sqlArgsParameterHandler = new SqlArgsParameterHandler(mappedStatement, parameterObject, boundSql);
091            return (ParameterHandler) interceptorChain.pluginAll(sqlArgsParameterHandler);
092        } else {
093            return super.newParameterHandler(mappedStatement, parameterObject, boundSql);
094        }
095    }
096
097
098    @Override
099    public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement
100        , RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {
101        ResultSetHandler resultSetHandler = new FlexResultSetHandler(executor, mappedStatement, parameterHandler,
102            resultHandler, boundSql, rowBounds);
103        return (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
104    }
105
106    /**
107     * 替换为 FlexStatementHandler,主要用来为实体类的多主键做支持、和数据审计
108     * FlexStatementHandler 和 原生的 RoutingStatementHandler 对比,没有任何性能影响
109     */
110    @Override
111    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
112        StatementHandler statementHandler = new FlexStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
113        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
114        return statementHandler;
115    }
116
117
118    /**
119     * 替换为 Flex 的 Executor,主要用于重建 CacheKey
120     * 默认情况下,Mybatis 的 CacheKey 构建是必须有 ParameterMapping,而 Flex 的 select 是不带有 ParameterMapping 的
121     */
122    @Override
123    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
124        executorType = executorType == null ? defaultExecutorType : executorType;
125        Executor executor;
126        if (ExecutorType.BATCH == executorType) {
127            executor = new FlexBatchExecutor(this, transaction);
128        } else if (ExecutorType.REUSE == executorType) {
129            executor = new FlexReuseExecutor(this, transaction);
130        } else {
131            executor = new FlexSimpleExecutor(this, transaction);
132        }
133        if (cacheEnabled) {
134            executor = new CachingExecutor(executor);
135        }
136        executor = (Executor) interceptorChain.pluginAll(executor);
137        return executor;
138    }
139
140
141    @Override
142    public MappedStatement getMappedStatement(String id) {
143        MappedStatement ms = super.getMappedStatement(id);
144        //动态 resultsMap,方法名称为:selectListByQuery
145        Class<?> asType = MappedStatementTypes.getCurrentType();
146        if (asType != null) {
147            return MapUtil.computeIfAbsent(dynamicMappedStatementCache, id + ":" + asType.getName(),
148                clazz -> replaceResultMap(ms, TableInfoFactory.ofEntityClass(asType))
149            );
150        }
151
152        return ms;
153    }
154
155
156    @Override
157    public void addMappedStatement(MappedStatement ms) {
158        //替换 RowMapper.insert 的主键生成器
159        //替换 RowMapper.insertBatchWithFirstRowColumns 的主键生成器
160        if (ms.getId().startsWith("com.mybatisflex.core.row.RowMapper.insert")) {
161            ms = replaceRowKeyGenerator(ms);
162        }
163        //entity insert methods
164        else if (StringUtil.endsWithAny(ms.getId(), "insert", FlexConsts.METHOD_INSERT_BATCH)
165            && ms.getKeyGenerator() == NoKeyGenerator.INSTANCE) {
166            ms = replaceEntityKeyGenerator(ms);
167        }
168        //entity select
169        else if (StringUtil.endsWithAny(ms.getId(), "selectOneById", "selectListByIds"
170            , "selectListByQuery", "selectCursorByQuery")) {
171            ms = replaceResultMap(ms, getTableInfo(ms));
172        } else {
173            List<ResultMap> resultMaps = ms.getResultMaps();
174            //根据 resultMap 里面的 class 进行判断
175            for (ResultMap resultMap : resultMaps) {
176                //获取结果的类型
177                Class<?> clazz = resultMap.getType();
178                //判断是否为表实体类
179                if (clazz.getDeclaredAnnotation(Table.class) != null && isDefaultResultMap(ms.getId(), resultMap.getId())) {
180                    TableInfo tableInfo = TableInfoFactory.ofEntityClass(clazz);
181                    ms = replaceResultMap(ms, tableInfo);
182                }
183            }
184        }
185        super.addMappedStatement(ms);
186    }
187
188    /**
189     * 是否为默认的 resultMap,也就是未配置 resultMap
190     *
191     * @return
192     */
193    private boolean isDefaultResultMap(String statementId, String resultMapId) {
194        // 参考 {@code  MapperBuilderAssistant.getStatementResultMaps}
195        return resultMapId.equals(statementId + "-Inline");
196    }
197
198
199    /**
200     * 替换 entity 查询的 ResultMap
201     */
202    private MappedStatement replaceResultMap(MappedStatement ms, TableInfo tableInfo) {
203
204        if (tableInfo == null) {
205            return ms;
206        }
207
208        String resultMapId = tableInfo.getEntityClass().getName();
209
210        ResultMap resultMap;
211        if (hasResultMap(resultMapId)) {
212            resultMap = getResultMap(resultMapId);
213        } else {
214            resultMap = tableInfo.buildResultMap(this);
215        }
216
217        return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType())
218            .resource(ms.getResource())
219            .fetchSize(ms.getFetchSize())
220            .timeout(ms.getTimeout())
221            .statementType(ms.getStatementType())
222            .keyGenerator(NoKeyGenerator.INSTANCE)
223            .keyProperty(ms.getKeyProperties() == null ? null : String.join(",", ms.getKeyProperties()))
224            .keyColumn(ms.getKeyColumns() == null ? null : String.join(",", ms.getKeyColumns()))
225            .databaseId(databaseId)
226            .lang(ms.getLang())
227            .resultOrdered(ms.isResultOrdered())
228            .resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets()))
229            .resultMaps(Collections.singletonList(resultMap))
230            .resultSetType(ms.getResultSetType())
231            .flushCacheRequired(ms.isFlushCacheRequired())
232            .useCache(ms.isUseCache())
233            .cache(ms.getCache())
234            .build();
235    }
236
237    /**
238     * 生成新的、已替换主键生成器的 MappedStatement
239     *
240     * @param ms MappedStatement
241     * @return replaced MappedStatement
242     */
243    private MappedStatement replaceRowKeyGenerator(MappedStatement ms) {
244
245        //执行原生 SQL,不需要为其设置主键生成器
246        if (ms.getId().endsWith("BySql")) {
247            return ms;
248        }
249
250        KeyGenerator keyGenerator = new RowKeyGenerator(ms);
251        if (ms.getId().endsWith("insertBatchWithFirstRowColumns")) {
252            keyGenerator = new MultiRowKeyGenerator(keyGenerator);
253        }
254
255        return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType())
256            .resource(ms.getResource())
257            .fetchSize(ms.getFetchSize())
258            .timeout(ms.getTimeout())
259            .statementType(ms.getStatementType())
260            // 替换主键生成器
261            .keyGenerator(keyGenerator)
262            .keyProperty(ms.getKeyProperties() == null ? null : String.join(",", ms.getKeyProperties()))
263            .keyColumn(ms.getKeyColumns() == null ? null : String.join(",", ms.getKeyColumns()))
264            .databaseId(databaseId)
265            .lang(ms.getLang())
266            .resultOrdered(ms.isResultOrdered())
267            .resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets()))
268            .resultMaps(ms.getResultMaps())
269            .resultSetType(ms.getResultSetType())
270            .flushCacheRequired(ms.isFlushCacheRequired())
271            .useCache(ms.isUseCache())
272            .cache(ms.getCache())
273            .build();
274    }
275
276    /**
277     * 生成新的、已替换主键生成器的 MappedStatement
278     *
279     * @param ms MappedStatement
280     * @return replaced MappedStatement
281     */
282    private MappedStatement replaceEntityKeyGenerator(MappedStatement ms) {
283
284        TableInfo tableInfo = getTableInfo(ms);
285        if (tableInfo == null) {
286            return ms;
287        }
288
289        KeyGenerator keyGenerator = MybatisKeyGeneratorUtil.createTableKeyGenerator(tableInfo, ms);
290        if (keyGenerator == NoKeyGenerator.INSTANCE) {
291            return ms;
292        }
293
294        //批量插入
295        if (ms.getId().endsWith(FlexConsts.METHOD_INSERT_BATCH)) {
296            keyGenerator = new MultiEntityKeyGenerator(keyGenerator);
297        }
298
299        return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType())
300            .resource(ms.getResource())
301            .fetchSize(ms.getFetchSize())
302            .timeout(ms.getTimeout())
303            .statementType(ms.getStatementType())
304            // 替换主键生成器
305            .keyGenerator(keyGenerator)
306            .keyProperty(tableInfo.getKeyProperties())
307            .keyColumn(tableInfo.getKeyColumns())
308            .databaseId(databaseId)
309            .lang(ms.getLang())
310            .resultOrdered(ms.isResultOrdered())
311            .resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets()))
312            .resultMaps(ms.getResultMaps())
313            .resultSetType(ms.getResultSetType())
314            .flushCacheRequired(ms.isFlushCacheRequired())
315            .useCache(ms.isUseCache())
316            .cache(ms.getCache())
317            .build();
318    }
319
320
321    private TableInfo getTableInfo(MappedStatement ms) {
322        String mapperClassName = ms.getId().substring(0, ms.getId().lastIndexOf("."));
323        try {
324            Class<?> mapperClass = Class.forName(mapperClassName);
325            return TableInfoFactory.ofMapperClass(mapperClass);
326        } catch (ClassNotFoundException e) {
327            return null;
328        }
329    }
330
331
332    @Override
333    public <T> void addMapper(Class<T> type) {
334        Type[] genericInterfaces = type.getGenericInterfaces();
335        boolean isGenericInterface = false;
336        for (Type genericInterface : genericInterfaces) {
337            if (genericInterface instanceof ParameterizedType) {
338                Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
339                if (actualTypeArgument instanceof Class) {
340                    Mappers.addMapping((Class<?>) actualTypeArgument, type);
341                } else {
342                    isGenericInterface = true;
343                    break;
344                }
345            }
346        }
347
348        //不支持泛型类添加
349        if (!isGenericInterface) {
350            mapperRegistry.addMapper(type);
351        }
352    }
353
354
355    @Override
356    public MapperRegistry getMapperRegistry() {
357        return mapperRegistry;
358    }
359
360    @Override
361    public void addMappers(String packageName, Class<?> superType) {
362        mapperRegistry.addMappers(packageName, superType);
363    }
364
365    @Override
366    public void addMappers(String packageName) {
367        mapperRegistry.addMappers(packageName);
368    }
369
370    @Override
371    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
372        return mapperRegistry.getMapper(type, sqlSession);
373    }
374
375    @Override
376    public boolean hasMapper(Class<?> type) {
377        return mapperRegistry.hasMapper(type);
378    }
379
380
381}