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}