001/*
002 *  Copyright (c) 2022-2025, 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        //忽略掉查询 Rows 的方法
147        if (asType != null) {
148            return MapUtil.computeIfAbsent(dynamicMappedStatementCache, id + ":" + asType.getName(),
149                clazz -> replaceResultMap(ms, TableInfoFactory.ofEntityClass(asType))
150            );
151        }
152
153        return ms;
154    }
155
156
157    @Override
158    public void addMappedStatement(MappedStatement ms) {
159        //替换 RowMapper.insert 的主键生成器
160        //替换 RowMapper.insertBatchWithFirstRowColumns 的主键生成器
161        if (ms.getId().startsWith("com.mybatisflex.core.row.RowMapper.insert")) {
162            ms = replaceRowKeyGenerator(ms);
163        }
164        //entity insert methods
165        else if (StringUtil.endsWithAny(ms.getId(), "insert", FlexConsts.METHOD_INSERT_BATCH)
166            && ms.getKeyGenerator() == NoKeyGenerator.INSTANCE) {
167            ms = replaceEntityKeyGenerator(ms);
168        }
169        //entity select
170        else if (StringUtil.endsWithAny(ms.getId(), "selectOneById", "selectListByIds"
171            , "selectListByQuery", "selectCursorByQuery")) {
172            ms = replaceResultMap(ms, getTableInfo(ms));
173        } else {
174            List<ResultMap> resultMaps = ms.getResultMaps();
175            //根据 resultMap 里面的 class 进行判断
176            for (ResultMap resultMap : resultMaps) {
177                //获取结果的类型
178                Class<?> clazz = resultMap.getType();
179                //判断是否为表实体类
180                if (clazz.getDeclaredAnnotation(Table.class) != null && isDefaultResultMap(ms.getId(), resultMap.getId())) {
181                    TableInfo tableInfo = TableInfoFactory.ofEntityClass(clazz);
182                    ms = replaceResultMap(ms, tableInfo);
183                }
184            }
185        }
186        super.addMappedStatement(ms);
187    }
188
189    /**
190     * 是否为默认的 resultMap,也就是未配置 resultMap
191     *
192     * @return
193     */
194    private boolean isDefaultResultMap(String statementId, String resultMapId) {
195        // 参考 {@code  MapperBuilderAssistant.getStatementResultMaps}
196        return resultMapId.equals(statementId + "-Inline");
197    }
198
199
200    /**
201     * 替换 entity 查询的 ResultMap
202     */
203    private MappedStatement replaceResultMap(MappedStatement ms, TableInfo tableInfo) {
204
205        if (tableInfo == null) {
206            return ms;
207        }
208
209        String resultMapId = tableInfo.getEntityClass().getName();
210
211        ResultMap resultMap;
212        if (hasResultMap(resultMapId)) {
213            resultMap = getResultMap(resultMapId);
214        } else {
215            resultMap = tableInfo.buildResultMap(this);
216        }
217
218        return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType())
219            .resource(ms.getResource())
220            .fetchSize(ms.getFetchSize())
221            .timeout(ms.getTimeout())
222            .statementType(ms.getStatementType())
223            .keyGenerator(NoKeyGenerator.INSTANCE)
224            .keyProperty(ms.getKeyProperties() == null ? null : String.join(",", ms.getKeyProperties()))
225            .keyColumn(ms.getKeyColumns() == null ? null : String.join(",", ms.getKeyColumns()))
226            .databaseId(databaseId)
227            .lang(ms.getLang())
228            .resultOrdered(ms.isResultOrdered())
229            .resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets()))
230            .resultMaps(Collections.singletonList(resultMap))
231            .resultSetType(ms.getResultSetType())
232            .flushCacheRequired(ms.isFlushCacheRequired())
233            .useCache(ms.isUseCache())
234            .cache(ms.getCache())
235            .build();
236    }
237
238    /**
239     * 生成新的、已替换主键生成器的 MappedStatement
240     *
241     * @param ms MappedStatement
242     * @return replaced MappedStatement
243     */
244    private MappedStatement replaceRowKeyGenerator(MappedStatement ms) {
245
246        //执行原生 SQL,不需要为其设置主键生成器
247        if (ms.getId().endsWith("BySql")) {
248            return ms;
249        }
250
251        KeyGenerator keyGenerator = new RowKeyGenerator(ms);
252        if (ms.getId().endsWith("insertBatchWithFirstRowColumns")) {
253            keyGenerator = new MultiRowKeyGenerator(keyGenerator);
254        }
255
256        return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType())
257            .resource(ms.getResource())
258            .fetchSize(ms.getFetchSize())
259            .timeout(ms.getTimeout())
260            .statementType(ms.getStatementType())
261            // 替换主键生成器
262            .keyGenerator(keyGenerator)
263            .keyProperty(ms.getKeyProperties() == null ? null : String.join(",", ms.getKeyProperties()))
264            .keyColumn(ms.getKeyColumns() == null ? null : String.join(",", ms.getKeyColumns()))
265            .databaseId(databaseId)
266            .lang(ms.getLang())
267            .resultOrdered(ms.isResultOrdered())
268            .resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets()))
269            .resultMaps(ms.getResultMaps())
270            .resultSetType(ms.getResultSetType())
271            .flushCacheRequired(ms.isFlushCacheRequired())
272            .useCache(ms.isUseCache())
273            .cache(ms.getCache())
274            .build();
275    }
276
277    /**
278     * 生成新的、已替换主键生成器的 MappedStatement
279     *
280     * @param ms MappedStatement
281     * @return replaced MappedStatement
282     */
283    private MappedStatement replaceEntityKeyGenerator(MappedStatement ms) {
284
285        TableInfo tableInfo = getTableInfo(ms);
286        if (tableInfo == null) {
287            return ms;
288        }
289
290        KeyGenerator keyGenerator = MybatisKeyGeneratorUtil.createTableKeyGenerator(tableInfo, ms);
291        if (keyGenerator == NoKeyGenerator.INSTANCE) {
292            return ms;
293        }
294
295        //批量插入
296        if (ms.getId().endsWith(FlexConsts.METHOD_INSERT_BATCH)) {
297            keyGenerator = new MultiEntityKeyGenerator(keyGenerator);
298        }
299
300        return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType())
301            .resource(ms.getResource())
302            .fetchSize(ms.getFetchSize())
303            .timeout(ms.getTimeout())
304            .statementType(ms.getStatementType())
305            // 替换主键生成器
306            .keyGenerator(keyGenerator)
307            .keyProperty(tableInfo.getKeyProperties())
308            .keyColumn(tableInfo.getKeyColumns())
309            .databaseId(databaseId)
310            .lang(ms.getLang())
311            .resultOrdered(ms.isResultOrdered())
312            .resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets()))
313            .resultMaps(ms.getResultMaps())
314            .resultSetType(ms.getResultSetType())
315            .flushCacheRequired(ms.isFlushCacheRequired())
316            .useCache(ms.isUseCache())
317            .cache(ms.getCache())
318            .build();
319    }
320
321
322    private TableInfo getTableInfo(MappedStatement ms) {
323        String mapperClassName = ms.getId().substring(0, ms.getId().lastIndexOf("."));
324        try {
325            Class<?> mapperClass = Class.forName(mapperClassName);
326            return TableInfoFactory.ofMapperClass(mapperClass);
327        } catch (ClassNotFoundException e) {
328            return null;
329        }
330    }
331
332
333    @Override
334    public <T> void addMapper(Class<T> type) {
335        Type[] genericInterfaces = type.getGenericInterfaces();
336        boolean isGenericInterface = false;
337        for (Type genericInterface : genericInterfaces) {
338            if (genericInterface instanceof ParameterizedType) {
339                Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
340                if (actualTypeArgument instanceof Class) {
341                    Mappers.addMapping((Class<?>) actualTypeArgument, type);
342                } else {
343                    isGenericInterface = true;
344                    break;
345                }
346            }
347        }
348
349        //不支持泛型类添加
350        if (!isGenericInterface) {
351            mapperRegistry.addMapper(type);
352            TableInfoFactory.init(type.getPackage().getName());
353        }
354    }
355
356
357    @Override
358    public MapperRegistry getMapperRegistry() {
359        return mapperRegistry;
360    }
361
362    @Override
363    public void addMappers(String packageName, Class<?> superType) {
364        mapperRegistry.addMappers(packageName, superType);
365        TableInfoFactory.init(packageName);
366    }
367
368    @Override
369    public void addMappers(String packageName) {
370        mapperRegistry.addMappers(packageName);
371        TableInfoFactory.init(packageName);
372    }
373
374    @Override
375    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
376        return mapperRegistry.getMapper(type, sqlSession);
377    }
378
379    @Override
380    public boolean hasMapper(Class<?> type) {
381        return mapperRegistry.hasMapper(type);
382    }
383
384
385}