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.executor.FlexBatchExecutor;
026import com.mybatisflex.core.mybatis.executor.FlexReuseExecutor;
027import com.mybatisflex.core.mybatis.executor.FlexSimpleExecutor;
028import com.mybatisflex.core.row.RowMapper;
029import com.mybatisflex.core.table.TableInfo;
030import com.mybatisflex.core.table.TableInfoFactory;
031import com.mybatisflex.core.util.StringUtil;
032import org.apache.ibatis.executor.CachingExecutor;
033import org.apache.ibatis.executor.Executor;
034import org.apache.ibatis.executor.keygen.KeyGenerator;
035import org.apache.ibatis.executor.keygen.NoKeyGenerator;
036import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
037import org.apache.ibatis.executor.parameter.ParameterHandler;
038import org.apache.ibatis.executor.resultset.ResultSetHandler;
039import org.apache.ibatis.executor.statement.StatementHandler;
040import org.apache.ibatis.mapping.BoundSql;
041import org.apache.ibatis.mapping.Environment;
042import org.apache.ibatis.mapping.MappedStatement;
043import org.apache.ibatis.mapping.ResultMap;
044import org.apache.ibatis.session.*;
045import org.apache.ibatis.transaction.Transaction;
046import org.apache.ibatis.util.MapUtil;
047
048import java.lang.reflect.ParameterizedType;
049import java.lang.reflect.Proxy;
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
064    public FlexConfiguration() {
065        setObjectWrapperFactory(new FlexWrapperFactory());
066        setDefaultEnumTypeHandler(CompositeEnumTypeHandler.class);
067        initDefaultMappers();
068    }
069
070
071    public FlexConfiguration(Environment environment) {
072        super(environment);
073        setObjectWrapperFactory(new FlexWrapperFactory());
074        setDefaultEnumTypeHandler(CompositeEnumTypeHandler.class);
075        initDefaultMappers();
076    }
077
078
079    /**
080     * 设置 mybatis-flex 默认的 Mapper
081     * 当前只有 RowMapper {@link RowMapper}
082     */
083    private void initDefaultMappers() {
084        addMapper(RowMapper.class);
085    }
086
087
088    /**
089     * 为原生 sql 设置参数
090     */
091    @Override
092    public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
093        String mappedStatementId = mappedStatement.getId();
094        /**
095         *  以 "!selectKey" 结尾的 mappedStatementId,是用于 Sequence 生成主键的,无需为其设置参数
096         *  {@link SelectKeyGenerator#SELECT_KEY_SUFFIX}
097         */
098        if (!mappedStatementId.endsWith(SelectKeyGenerator.SELECT_KEY_SUFFIX)
099            && parameterObject instanceof Map
100            && ((Map<?, ?>) parameterObject).containsKey(FlexConsts.SQL_ARGS)) {
101            SqlArgsParameterHandler sqlArgsParameterHandler = new SqlArgsParameterHandler(mappedStatement, (Map) parameterObject, boundSql);
102            return (ParameterHandler) interceptorChain.pluginAll(sqlArgsParameterHandler);
103        } else {
104            return super.newParameterHandler(mappedStatement, parameterObject, boundSql);
105        }
106    }
107
108
109    @Override
110    public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement
111        , RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) {
112        ResultSetHandler resultSetHandler = new FlexResultSetHandler(executor, mappedStatement, parameterHandler,
113            resultHandler, boundSql, rowBounds);
114        return (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
115    }
116
117    /**
118     * 替换为 FlexStatementHandler,主要用来为实体类的多主键做支持、和数据审计
119     * FlexStatementHandler 和 原生的 RoutingStatementHandler 对比,没有任何性能影响
120     */
121    @Override
122    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
123        StatementHandler statementHandler = new FlexStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
124        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
125        return statementHandler;
126    }
127
128
129    /**
130     * 替换为 Flex 的 Executor,主要用于重建 CacheKey
131     * 默认情况下,Mybatis 的 CacheKey 构建是必须有 ParameterMapping,而 Flex 的 select 是不带有 ParameterMapping 的
132     */
133    @Override
134    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
135        executorType = executorType == null ? defaultExecutorType : executorType;
136        Executor executor;
137        if (ExecutorType.BATCH == executorType) {
138            executor = new FlexBatchExecutor(this, transaction);
139        } else if (ExecutorType.REUSE == executorType) {
140            executor = new FlexReuseExecutor(this, transaction);
141        } else {
142            executor = new FlexSimpleExecutor(this, transaction);
143        }
144        if (cacheEnabled) {
145            executor = new CachingExecutor(executor);
146        }
147        executor = (Executor) interceptorChain.pluginAll(executor);
148        return executor;
149    }
150
151
152    @Override
153    public MappedStatement getMappedStatement(String id) {
154        MappedStatement ms = super.getMappedStatement(id);
155        //动态 resultsMap,方法名称为:selectListByQuery
156        Class<?> asType = MappedStatementTypes.getCurrentType();
157        if (asType != null) {
158            return MapUtil.computeIfAbsent(dynamicMappedStatementCache, id + ":" + asType.getName(),
159                clazz -> replaceResultMap(ms, TableInfoFactory.ofEntityClass(asType))
160            );
161        }
162
163        return ms;
164    }
165
166
167    @Override
168    public void addMappedStatement(MappedStatement ms) {
169        //替换 RowMapper.insert 的主键生成器
170        //替换 RowMapper.insertBatchWithFirstRowColumns 的主键生成器
171        if (ms.getId().startsWith("com.mybatisflex.core.row.RowMapper.insert")) {
172            ms = replaceRowKeyGenerator(ms);
173        }
174        //entity insert methods
175        else if (StringUtil.endsWithAny(ms.getId(), "insert", FlexConsts.METHOD_INSERT_BATCH)
176            && ms.getKeyGenerator() == NoKeyGenerator.INSTANCE) {
177            ms = replaceEntityKeyGenerator(ms);
178        }
179        //entity select
180        else if (StringUtil.endsWithAny(ms.getId(), "selectOneById", "selectListByIds"
181            , "selectListByQuery", "selectCursorByQuery")) {
182            ms = replaceResultMap(ms, getTableInfo(ms));
183        } else {
184            List<ResultMap> resultMaps = ms.getResultMaps();
185            //根据 resultMap 里面的 class 进行判断
186            for (ResultMap resultMap : resultMaps) {
187                //获取结果的类型
188                Class<?> clazz = resultMap.getType();
189                //判断是否为表实体类
190                if (clazz.getDeclaredAnnotation(Table.class) != null) {
191                    ms = replaceResultMap(ms, getTableInfo(ms));
192                }
193            }
194        }
195        super.addMappedStatement(ms);
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            super.addMapper(type);
351        }
352    }
353
354
355    @SuppressWarnings("unchecked")
356    @Override
357    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
358        T mapper = super.getMapper(type, sqlSession);
359        return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}
360            , new MapperInvocationHandler(mapper, environment.getDataSource()));
361    }
362
363}