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.StringUtil; 031import org.apache.ibatis.executor.CachingExecutor; 032import org.apache.ibatis.executor.Executor; 033import org.apache.ibatis.executor.keygen.KeyGenerator; 034import org.apache.ibatis.executor.keygen.NoKeyGenerator; 035import org.apache.ibatis.executor.keygen.SelectKeyGenerator; 036import org.apache.ibatis.executor.parameter.ParameterHandler; 037import org.apache.ibatis.executor.resultset.ResultSetHandler; 038import org.apache.ibatis.executor.statement.StatementHandler; 039import org.apache.ibatis.mapping.*; 040import org.apache.ibatis.session.*; 041import org.apache.ibatis.transaction.Transaction; 042import org.apache.ibatis.util.MapUtil; 043 044import java.lang.reflect.Proxy; 045import java.util.ArrayList; 046import java.util.Collections; 047import java.util.List; 048import java.util.Map; 049import java.util.concurrent.ConcurrentHashMap; 050 051public class FlexConfiguration extends Configuration { 052 053 private static Map<Class<?>, MappedStatement> dynamicMappedStatementCache = new ConcurrentHashMap<>(); 054 055 public FlexConfiguration(Environment environment) { 056 super(environment); 057 setMapUnderscoreToCamelCase(true); 058 setObjectWrapperFactory(new EntityWrapperFactory()); 059 initDefaultMappers(); 060 } 061 062 public FlexConfiguration() { 063 setMapUnderscoreToCamelCase(true); 064 setObjectWrapperFactory(new EntityWrapperFactory()); 065 initDefaultMappers(); 066 } 067 068 069 /** 070 * 设置 mybatis-flex 默认的 Mapper 071 * 当前只有 RowMapper {@link RowMapper} 072 */ 073 private void initDefaultMappers() { 074 addMapper(RowMapper.class); 075 } 076 077 078 /** 079 * 为原生 sql 设置参数 080 */ 081 @Override 082 public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { 083 String mappedStatementId = mappedStatement.getId(); 084 /** 085 * 以 "!selectKey" 结尾的 mappedStatementId,是用于 Sequence 生成主键的,无需为其设置参数 086 * {@link SelectKeyGenerator#SELECT_KEY_SUFFIX} 087 */ 088 if (!mappedStatementId.endsWith(SelectKeyGenerator.SELECT_KEY_SUFFIX) 089 && parameterObject instanceof Map 090 && ((Map<?, ?>) parameterObject).containsKey(FlexConsts.SQL_ARGS)) { 091 return new SqlArgsParameterHandler(mappedStatement, (Map) parameterObject, boundSql); 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 DefaultResultSetHandler(executor, mappedStatement, parameterHandler, 102// resultHandler, boundSql, rowBounds); 103 ResultSetHandler resultSetHandler = new FlexResultSetHandler(executor, mappedStatement, parameterHandler, 104 resultHandler, boundSql, rowBounds); 105 return (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); 106 } 107 108 /** 109 * 替换为 FlexStatementHandler,主要用来为实体类的多主键做支持、和数据审计 110 * FlexStatementHandler 和 原生的 RoutingStatementHandler 对比,没有任何性能影响 111 */ 112 @Override 113 public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 114 StatementHandler statementHandler = new FlexStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); 115 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); 116 return statementHandler; 117 } 118 119 120 /** 121 * 替换为 Flex 的 Executor,主要用于重建 CacheKey 122 * 默认情况下,Mybatis 的 CacheKey 构建是必须有 ParameterMapping,而 Flex 的 select 是不带有 ParameterMapping 的 123 */ 124 @Override 125 public Executor newExecutor(Transaction transaction, ExecutorType executorType) { 126 executorType = executorType == null ? defaultExecutorType : executorType; 127 Executor executor; 128 if (ExecutorType.BATCH == executorType) { 129 executor = new FlexBatchExecutor(this, transaction); 130 } else if (ExecutorType.REUSE == executorType) { 131 executor = new FlexReuseExecutor(this, transaction); 132 } else { 133 executor = new FlexSimpleExecutor(this, transaction); 134 } 135 if (cacheEnabled) { 136 executor = new CachingExecutor(executor); 137 } 138 executor = (Executor) interceptorChain.pluginAll(executor); 139 return executor; 140 } 141 142 143 @Override 144 public MappedStatement getMappedStatement(String id) { 145 MappedStatement ms = super.getMappedStatement(id); 146 147 //动态 resultsMap,方法名称为:selectListByQuery 148 Class<?> asType = MappedStatementTypes.getCurrentType(); 149 if (asType != null) { 150 return MapUtil.computeIfAbsent(dynamicMappedStatementCache, asType, 151 aClass -> replaceResultMap(ms, TableInfoFactory.ofEntityClass(asType)) 152 ); 153 } 154 155 return ms; 156 } 157 158 159 @Override 160 public void addMappedStatement(MappedStatement ms) { 161 //替换 RowMapper.insert 的主键生成器 162 //替换 RowMapper.insertBatchWithFirstRowColumns 的主键生成器 163 if (ms.getId().startsWith("com.mybatisflex.core.row.RowMapper.insert")) { 164 ms = replaceRowKeyGenerator(ms); 165 } 166 //entity insert methods 167 else if (StringUtil.endsWithAny(ms.getId(), "insert", FlexConsts.METHOD_INSERT_BATCH) 168 && ms.getKeyGenerator() == NoKeyGenerator.INSTANCE) { 169 ms = replaceEntityKeyGenerator(ms); 170 } 171 //entity select 172 else if (StringUtil.endsWithAny(ms.getId(), "selectOneById", "selectListByIds" 173 , "selectListByQuery")) { 174 ms = replaceResultMap(ms, getTableInfo(ms)); 175 } 176 177 super.addMappedStatement(ms); 178 } 179 180 181 /** 182 * 替换 entity 查询的 ResultMap 183 */ 184 private MappedStatement replaceResultMap(MappedStatement ms, TableInfo tableInfo) { 185 186 if (tableInfo == null) { 187 return ms; 188 } 189 190 String resultMapId = tableInfo.getEntityClass().getName(); 191 192 /*ResultMap resultMap; 193 if (hasResultMap(resultMapId)) { 194 resultMap = getResultMap(resultMapId); 195 } else { 196 resultMap = tableInfo.buildResultMap(this); 197 this.addResultMap(resultMap); 198 }*/ 199 200 // 变量名与属性名区分开 201 List<ResultMap> resultMapList; 202 if (hasResultMap(resultMapId)) { 203 resultMapList = new ArrayList<>(); 204 fillResultMapList(resultMapId, resultMapList); 205 } else { 206 resultMapList = tableInfo.buildResultMapList(this); 207 for (ResultMap resultMap : resultMapList) { 208 if (!hasResultMap(resultMap.getId())) { 209 addResultMap(resultMap); 210 } 211 } 212 } 213 214 /* 215 * 这里解释一下为什么要反转这个集合: 216 * 217 * MyBatis 在解析 ResultMaps 的时候,是按照顺序一个一个进行解析的,对于有嵌套 218 * 的 ResultMap 对象,也就是 nestResultMap 需要放在靠前的位置,首先解析。 219 * 220 * 而我们进行递归 buildResultMapList 也好,fillResultMapList 也好,都是 221 * 非嵌套 ResultMap 在集合最开始的位置,所以要反转一下集合,将 hasNestedResultMaps 222 * 的 ResultMap 对象放到集合的最前面。 223 */ 224 Collections.reverse(resultMapList); 225 226 return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType()) 227 .resource(ms.getResource()) 228 .fetchSize(ms.getFetchSize()) 229 .timeout(ms.getTimeout()) 230 .statementType(ms.getStatementType()) 231 .keyGenerator(NoKeyGenerator.INSTANCE) 232 .keyProperty(ms.getKeyProperties() == null ? null : String.join(",", ms.getKeyProperties())) 233 .keyColumn(ms.getKeyColumns() == null ? null : String.join(",", ms.getKeyColumns())) 234 .databaseId(databaseId) 235 .lang(ms.getLang()) 236 .resultOrdered(ms.isResultOrdered()) 237 .resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets())) 238 //.resultMaps(CollectionUtil.newArrayList(resultMap)) // 替换resultMap 239 .resultMaps(resultMapList) 240 .resultSetType(ms.getResultSetType()) 241 .flushCacheRequired(ms.isFlushCacheRequired()) 242 .useCache(ms.isUseCache()) 243 .cache(ms.getCache()) 244 .build(); 245 } 246 247 private void fillResultMapList(String resultMapId, List<ResultMap> resultMapList) { 248 ResultMap resultMap = this.getResultMap(resultMapId); 249 resultMapList.add(resultMap); 250 if (resultMap.hasNestedResultMaps()) { 251 for (ResultMapping resultMapping : resultMap.getResultMappings()) { 252 String nestedResultMapId = resultMapping.getNestedResultMapId(); 253 if (nestedResultMapId != null) { 254 fillResultMapList(nestedResultMapId, resultMapList); 255 } 256 } 257 } 258 } 259 260 /** 261 * 生成新的、已替换主键生成器的 MappedStatement 262 * 263 * @param ms MappedStatement 264 * @return replaced MappedStatement 265 */ 266 private MappedStatement replaceRowKeyGenerator(MappedStatement ms) { 267 268 //执行原生 SQL,不需要为其设置主键生成器 269 if (ms.getId().endsWith("BySql")) { 270 return ms; 271 } 272 273 KeyGenerator keyGenerator = new RowKeyGenerator(ms); 274 if (ms.getId().endsWith("insertBatchWithFirstRowColumns")) { 275 keyGenerator = new MultiRowKeyGenerator(keyGenerator); 276 } 277 278 return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType()) 279 .resource(ms.getResource()) 280 .fetchSize(ms.getFetchSize()) 281 .timeout(ms.getTimeout()) 282 .statementType(ms.getStatementType()) 283 .keyGenerator(keyGenerator) // 替换主键生成器 284 .keyProperty(ms.getKeyProperties() == null ? null : String.join(",", ms.getKeyProperties())) 285 .keyColumn(ms.getKeyColumns() == null ? null : String.join(",", ms.getKeyColumns())) 286 .databaseId(databaseId) 287 .lang(ms.getLang()) 288 .resultOrdered(ms.isResultOrdered()) 289 .resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets())) 290 .resultMaps(ms.getResultMaps()) 291 .resultSetType(ms.getResultSetType()) 292 .flushCacheRequired(ms.isFlushCacheRequired()) 293 .useCache(ms.isUseCache()) 294 .cache(ms.getCache()) 295 .build(); 296 } 297 298 /** 299 * 生成新的、已替换主键生成器的 MappedStatement 300 * 301 * @param ms MappedStatement 302 * @return replaced MappedStatement 303 */ 304 private MappedStatement replaceEntityKeyGenerator(MappedStatement ms) { 305 306 TableInfo tableInfo = getTableInfo(ms); 307 if (tableInfo == null) { 308 return ms; 309 } 310 311 KeyGenerator keyGenerator = MybatisKeyGeneratorUtil.createTableKeyGenerator(tableInfo, ms); 312 if (keyGenerator == NoKeyGenerator.INSTANCE) { 313 return ms; 314 } 315 316 //批量插入 317 if (ms.getId().endsWith(FlexConsts.METHOD_INSERT_BATCH)) { 318 keyGenerator = new MultiEntityKeyGenerator(keyGenerator); 319 } 320 321 return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType()) 322 .resource(ms.getResource()) 323 .fetchSize(ms.getFetchSize()) 324 .timeout(ms.getTimeout()) 325 .statementType(ms.getStatementType()) 326 .keyGenerator(keyGenerator) // 替换主键生成器 327 .keyProperty(tableInfo.getKeyProperties()) 328 .keyColumn(tableInfo.getKeyColumns()) 329 .databaseId(databaseId) 330 .lang(ms.getLang()) 331 .resultOrdered(ms.isResultOrdered()) 332 .resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets())) 333 .resultMaps(ms.getResultMaps()) 334 .resultSetType(ms.getResultSetType()) 335 .flushCacheRequired(ms.isFlushCacheRequired()) 336 .useCache(ms.isUseCache()) 337 .cache(ms.getCache()) 338 .build(); 339 } 340 341 342 private TableInfo getTableInfo(MappedStatement ms) { 343 String mapperClassName = ms.getId().substring(0, ms.getId().lastIndexOf(".")); 344 try { 345 Class<?> mapperClass = Class.forName(mapperClassName); 346 return TableInfoFactory.ofMapperClass(mapperClass); 347 } catch (ClassNotFoundException e) { 348 return null; 349 } 350 } 351 352 353 @Override 354 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { 355 T mapper = super.getMapper(type, sqlSession); 356 return (T) Proxy.newProxyInstance(type.getClassLoader() 357 , new Class[]{type} 358 , new MapperInvocationHandler(mapper, this)); 359 360 } 361}