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.core.FlexConsts;
019import com.mybatisflex.core.keygen.MultiEntityKeyGenerator;
020import com.mybatisflex.core.keygen.MultiRowKeyGenerator;
021import com.mybatisflex.core.keygen.MybatisKeyGeneratorUtil;
022import com.mybatisflex.core.keygen.RowKeyGenerator;
023import com.mybatisflex.core.mybatis.executor.FlexBatchExecutor;
024import com.mybatisflex.core.mybatis.executor.FlexReuseExecutor;
025import com.mybatisflex.core.mybatis.executor.FlexSimpleExecutor;
026import com.mybatisflex.core.row.RowMapper;
027import com.mybatisflex.core.table.EntityWrapperFactory;
028import com.mybatisflex.core.table.TableInfo;
029import com.mybatisflex.core.table.TableInfoFactory;
030import com.mybatisflex.core.util.CollectionUtil;
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.statement.StatementHandler;
039import org.apache.ibatis.mapping.BoundSql;
040import org.apache.ibatis.mapping.Environment;
041import org.apache.ibatis.mapping.MappedStatement;
042import org.apache.ibatis.mapping.ResultMap;
043import org.apache.ibatis.session.*;
044import org.apache.ibatis.transaction.Transaction;
045
046import java.lang.reflect.Proxy;
047import java.util.Map;
048
049public class FlexConfiguration extends Configuration {
050
051
052    public FlexConfiguration(Environment environment) {
053        super(environment);
054        setMapUnderscoreToCamelCase(true);
055        setObjectWrapperFactory(new EntityWrapperFactory());
056        initDefaultMappers();
057    }
058
059    public FlexConfiguration() {
060        setMapUnderscoreToCamelCase(true);
061        setObjectWrapperFactory(new EntityWrapperFactory());
062        initDefaultMappers();
063    }
064
065
066    /**
067     * 设置 mybatis-flex 默认的 Mapper
068     * 当前只有 RowMapper {@link RowMapper}
069     */
070    private void initDefaultMappers() {
071        addMapper(RowMapper.class);
072    }
073
074
075    /**
076     * 为原生 sql 设置参数
077     */
078    @Override
079    public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
080        String mappedStatementId = mappedStatement.getId();
081        /**
082         *  以 "!selectKey" 结尾的 mappedStatementId,是用于 Sequence 生成主键的,无需为其设置参数
083         *  {@link SelectKeyGenerator#SELECT_KEY_SUFFIX}
084         */
085        if (!mappedStatementId.endsWith(SelectKeyGenerator.SELECT_KEY_SUFFIX)
086                && parameterObject instanceof Map
087                && ((Map<?, ?>) parameterObject).containsKey(FlexConsts.SQL_ARGS)) {
088            return new SqlArgsParameterHandler(mappedStatement, (Map) parameterObject, boundSql);
089        } else {
090            return super.newParameterHandler(mappedStatement, parameterObject, boundSql);
091        }
092    }
093
094    /**
095     * 替换为 FlexStatementHandler,主要用来为实体类的多主键做支持、和数据审计
096     * FlexStatementHandler 和 原生的 RoutingStatementHandler 对比,没有任何性能影响
097     */
098    @Override
099    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
100        StatementHandler statementHandler = new FlexStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
101        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
102        return statementHandler;
103    }
104
105
106    /**
107     * 替换为 Flex 的 Executor,主要用于重建 CacheKey
108     * 默认情况下,Mybatis 的 CacheKey 构建是必须有 ParameterMapping,而 Flex 的 select 是不带有 ParameterMapping 的
109     */
110    @Override
111    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
112        executorType = executorType == null ? defaultExecutorType : executorType;
113        Executor executor;
114        if (ExecutorType.BATCH == executorType) {
115            executor = new FlexBatchExecutor(this, transaction);
116        } else if (ExecutorType.REUSE == executorType) {
117            executor = new FlexReuseExecutor(this, transaction);
118        } else {
119            executor = new FlexSimpleExecutor(this, transaction);
120        }
121        if (cacheEnabled) {
122            executor = new CachingExecutor(executor);
123        }
124        executor = (Executor) interceptorChain.pluginAll(executor);
125        return executor;
126    }
127
128
129    @Override
130    public void addMappedStatement(MappedStatement ms) {
131        //替换 RowMapper.insert 的主键生成器
132        //替换 RowMapper.insertBatchWithFirstRowColumns 的主键生成器
133        if (ms.getId().startsWith("com.mybatisflex.core.row.RowMapper.insert")) {
134            ms = replaceRowKeyGenerator(ms);
135        }
136        //entity insert methods
137        else if (StringUtil.endsWithAny(ms.getId(), "insert", FlexConsts.METHOD_INSERT_BATCH)
138                && ms.getKeyGenerator() == NoKeyGenerator.INSTANCE) {
139            ms = replaceEntityKeyGenerator(ms);
140        }
141        //entity select
142        else if (StringUtil.endsWithAny(ms.getId(), "selectOneById", "selectListByIds"
143                , "selectListByQuery")) {
144            ms = replaceResultMap(ms);
145        }
146
147        super.addMappedStatement(ms);
148    }
149
150
151    /**
152     * 替换 entity 查询的 ResultMap
153     */
154    private MappedStatement replaceResultMap(MappedStatement ms) {
155
156        TableInfo tableInfo = getTableInfo(ms);
157        if (tableInfo == null) {
158            return ms;
159        }
160
161        String resultMapId = tableInfo.getEntityClass().getName();
162
163        ResultMap resultMap;
164        if (hasResultMap(resultMapId)) {
165            resultMap = getResultMap(resultMapId);
166        } else {
167            resultMap = tableInfo.buildResultMap(this);
168            this.addResultMap(resultMap);
169        }
170
171        return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType())
172                .resource(ms.getResource())
173                .fetchSize(ms.getFetchSize())
174                .timeout(ms.getTimeout())
175                .statementType(ms.getStatementType())
176                .keyGenerator(NoKeyGenerator.INSTANCE)
177                .keyProperty(ms.getKeyProperties() == null ? null : String.join(",", ms.getKeyProperties()))
178                .keyColumn(ms.getKeyColumns() == null ? null : String.join(",", ms.getKeyColumns()))
179                .databaseId(databaseId)
180                .lang(ms.getLang())
181                .resultOrdered(ms.isResultOrdered())
182                .resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets()))
183                .resultMaps(CollectionUtil.newArrayList(resultMap)) // 替换resultMap
184                .resultSetType(ms.getResultSetType())
185                .flushCacheRequired(ms.isFlushCacheRequired())
186                .useCache(ms.isUseCache())
187                .cache(ms.getCache())
188                .build();
189    }
190
191    /**
192     * 生成新的、已替换主键生成器的 MappedStatement
193     *
194     * @param ms MappedStatement
195     * @return replaced MappedStatement
196     */
197    private MappedStatement replaceRowKeyGenerator(MappedStatement ms) {
198
199        //执行原生 SQL,不需要为其设置主键生成器
200        if (ms.getId().endsWith("BySql")) {
201            return ms;
202        }
203
204        KeyGenerator keyGenerator = new RowKeyGenerator(ms);
205        if (ms.getId().endsWith("insertBatchWithFirstRowColumns")) {
206            keyGenerator = new MultiRowKeyGenerator(keyGenerator);
207        }
208
209        return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType())
210                .resource(ms.getResource())
211                .fetchSize(ms.getFetchSize())
212                .timeout(ms.getTimeout())
213                .statementType(ms.getStatementType())
214                .keyGenerator(keyGenerator) // 替换主键生成器
215                .keyProperty(ms.getKeyProperties() == null ? null : String.join(",", ms.getKeyProperties()))
216                .keyColumn(ms.getKeyColumns() == null ? null : String.join(",", ms.getKeyColumns()))
217                .databaseId(databaseId)
218                .lang(ms.getLang())
219                .resultOrdered(ms.isResultOrdered())
220                .resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets()))
221                .resultMaps(ms.getResultMaps())
222                .resultSetType(ms.getResultSetType())
223                .flushCacheRequired(ms.isFlushCacheRequired())
224                .useCache(ms.isUseCache())
225                .cache(ms.getCache())
226                .build();
227    }
228
229    /**
230     * 生成新的、已替换主键生成器的 MappedStatement
231     *
232     * @param ms MappedStatement
233     * @return replaced MappedStatement
234     */
235    private MappedStatement replaceEntityKeyGenerator(MappedStatement ms) {
236
237        TableInfo tableInfo = getTableInfo(ms);
238        if (tableInfo == null) {
239            return ms;
240        }
241
242        KeyGenerator keyGenerator = MybatisKeyGeneratorUtil.createTableKeyGenerator(tableInfo, ms);
243        if (keyGenerator == NoKeyGenerator.INSTANCE) {
244            return ms;
245        }
246
247        //批量插入
248        if (ms.getId().endsWith(FlexConsts.METHOD_INSERT_BATCH)) {
249            keyGenerator = new MultiEntityKeyGenerator(keyGenerator);
250        }
251
252        return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType())
253                .resource(ms.getResource())
254                .fetchSize(ms.getFetchSize())
255                .timeout(ms.getTimeout())
256                .statementType(ms.getStatementType())
257                .keyGenerator(keyGenerator) // 替换主键生成器
258                .keyProperty(tableInfo.getKeyProperties())
259                .keyColumn(tableInfo.getKeyColumns())
260                .databaseId(databaseId)
261                .lang(ms.getLang())
262                .resultOrdered(ms.isResultOrdered())
263                .resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets()))
264                .resultMaps(ms.getResultMaps())
265                .resultSetType(ms.getResultSetType())
266                .flushCacheRequired(ms.isFlushCacheRequired())
267                .useCache(ms.isUseCache())
268                .cache(ms.getCache())
269                .build();
270    }
271
272
273    private TableInfo getTableInfo(MappedStatement ms) {
274        String mapperClassName = ms.getId().substring(0, ms.getId().lastIndexOf("."));
275        try {
276            Class<?> mapperClass = Class.forName(mapperClassName);
277            return TableInfoFactory.ofMapperClass(mapperClass);
278        } catch (ClassNotFoundException e) {
279            return null;
280        }
281    }
282
283
284    @Override
285    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
286        T mapper = super.getMapper(type, sqlSession);
287        return (T) Proxy.newProxyInstance(type.getClassLoader()
288                , new Class[]{type}
289                , new MapperInvocationHandler(mapper, this));
290
291    }
292}