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 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 TableInfoFactory.init(type.getPackage().getName()); 352 } 353 } 354 355 356 @Override 357 public MapperRegistry getMapperRegistry() { 358 return mapperRegistry; 359 } 360 361 @Override 362 public void addMappers(String packageName, Class<?> superType) { 363 mapperRegistry.addMappers(packageName, superType); 364 TableInfoFactory.init(packageName); 365 } 366 367 @Override 368 public void addMappers(String packageName) { 369 mapperRegistry.addMappers(packageName); 370 TableInfoFactory.init(packageName); 371 } 372 373 @Override 374 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { 375 return mapperRegistry.getMapper(type, sqlSession); 376 } 377 378 @Override 379 public boolean hasMapper(Class<?> type) { 380 return mapperRegistry.hasMapper(type); 381 } 382 383 384}