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.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.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; 045import org.apache.ibatis.util.MapUtil; 046 047import java.lang.reflect.ParameterizedType; 048import java.lang.reflect.Proxy; 049import java.lang.reflect.Type; 050import java.util.Collections; 051import java.util.List; 052import java.util.Map; 053import java.util.concurrent.ConcurrentHashMap; 054 055/** 056 * @author michael 057 * @author life 058 */ 059public class FlexConfiguration extends Configuration { 060 061 private static final Map<String, MappedStatement> dynamicMappedStatementCache = new ConcurrentHashMap<>(); 062 063 public FlexConfiguration() { 064 setObjectWrapperFactory(new FlexWrapperFactory()); 065 setDefaultEnumTypeHandler(CompositeEnumTypeHandler.class); 066 } 067 068 069 public FlexConfiguration(Environment environment) { 070 super(environment); 071 setObjectWrapperFactory(new FlexWrapperFactory()); 072 setDefaultEnumTypeHandler(CompositeEnumTypeHandler.class); 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 SqlArgsParameterHandler sqlArgsParameterHandler = new SqlArgsParameterHandler(mappedStatement, (Map) parameterObject, boundSql); 089 return (ParameterHandler) interceptorChain.pluginAll(sqlArgsParameterHandler); 090 } else { 091 return super.newParameterHandler(mappedStatement, parameterObject, boundSql); 092 } 093 } 094 095 096 @Override 097 public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement 098 , RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { 099 ResultSetHandler resultSetHandler = new FlexResultSetHandler(executor, mappedStatement, parameterHandler, 100 resultHandler, boundSql, rowBounds); 101 return (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); 102 } 103 104 /** 105 * 替换为 FlexStatementHandler,主要用来为实体类的多主键做支持、和数据审计 106 * FlexStatementHandler 和 原生的 RoutingStatementHandler 对比,没有任何性能影响 107 */ 108 @Override 109 public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 110 StatementHandler statementHandler = new FlexStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); 111 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); 112 return statementHandler; 113 } 114 115 116 /** 117 * 替换为 Flex 的 Executor,主要用于重建 CacheKey 118 * 默认情况下,Mybatis 的 CacheKey 构建是必须有 ParameterMapping,而 Flex 的 select 是不带有 ParameterMapping 的 119 */ 120 @Override 121 public Executor newExecutor(Transaction transaction, ExecutorType executorType) { 122 executorType = executorType == null ? defaultExecutorType : executorType; 123 Executor executor; 124 if (ExecutorType.BATCH == executorType) { 125 executor = new FlexBatchExecutor(this, transaction); 126 } else if (ExecutorType.REUSE == executorType) { 127 executor = new FlexReuseExecutor(this, transaction); 128 } else { 129 executor = new FlexSimpleExecutor(this, transaction); 130 } 131 if (cacheEnabled) { 132 executor = new CachingExecutor(executor); 133 } 134 executor = (Executor) interceptorChain.pluginAll(executor); 135 return executor; 136 } 137 138 139 @Override 140 public MappedStatement getMappedStatement(String id) { 141 MappedStatement ms = super.getMappedStatement(id); 142 //动态 resultsMap,方法名称为:selectListByQuery 143 Class<?> asType = MappedStatementTypes.getCurrentType(); 144 if (asType != null) { 145 return MapUtil.computeIfAbsent(dynamicMappedStatementCache, id + ":" + asType.getName(), 146 clazz -> replaceResultMap(ms, TableInfoFactory.ofEntityClass(asType)) 147 ); 148 } 149 150 return ms; 151 } 152 153 154 @Override 155 public void addMappedStatement(MappedStatement ms) { 156 //替换 RowMapper.insert 的主键生成器 157 //替换 RowMapper.insertBatchWithFirstRowColumns 的主键生成器 158 if (ms.getId().startsWith("com.mybatisflex.core.row.RowMapper.insert")) { 159 ms = replaceRowKeyGenerator(ms); 160 } 161 //entity insert methods 162 else if (StringUtil.endsWithAny(ms.getId(), "insert", FlexConsts.METHOD_INSERT_BATCH) 163 && ms.getKeyGenerator() == NoKeyGenerator.INSTANCE) { 164 ms = replaceEntityKeyGenerator(ms); 165 } 166 //entity select 167 else if (StringUtil.endsWithAny(ms.getId(), "selectOneById", "selectListByIds" 168 , "selectListByQuery", "selectCursorByQuery")) { 169 ms = replaceResultMap(ms, getTableInfo(ms)); 170 } else { 171 List<ResultMap> resultMaps = ms.getResultMaps(); 172 //根据 resultMap 里面的 class 进行判断 173 for (ResultMap resultMap : resultMaps) { 174 //获取结果的类型 175 Class<?> clazz = resultMap.getType(); 176 //判断是否为表实体类 177 if (clazz.getDeclaredAnnotation(Table.class) != null && isDefaultResultMap(ms.getId(), resultMap.getId())) { 178 TableInfo tableInfo = TableInfoFactory.ofEntityClass(clazz); 179 ms = replaceResultMap(ms, tableInfo); 180 } 181 } 182 } 183 super.addMappedStatement(ms); 184 } 185 186 /** 187 * 是否为默认的 resultMap,也就是未配置 resultMap 188 * 189 * @return 190 */ 191 private boolean isDefaultResultMap(String statementId, String resultMapId) { 192 // 参考 {@code MapperBuilderAssistant.getStatementResultMaps} 193 return resultMapId.equals(statementId + "-Inline"); 194 } 195 196 197 /** 198 * 替换 entity 查询的 ResultMap 199 */ 200 private MappedStatement replaceResultMap(MappedStatement ms, TableInfo tableInfo) { 201 202 if (tableInfo == null) { 203 return ms; 204 } 205 206 String resultMapId = tableInfo.getEntityClass().getName(); 207 208 ResultMap resultMap; 209 if (hasResultMap(resultMapId)) { 210 resultMap = getResultMap(resultMapId); 211 } else { 212 resultMap = tableInfo.buildResultMap(this); 213 } 214 215 return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType()) 216 .resource(ms.getResource()) 217 .fetchSize(ms.getFetchSize()) 218 .timeout(ms.getTimeout()) 219 .statementType(ms.getStatementType()) 220 .keyGenerator(NoKeyGenerator.INSTANCE) 221 .keyProperty(ms.getKeyProperties() == null ? null : String.join(",", ms.getKeyProperties())) 222 .keyColumn(ms.getKeyColumns() == null ? null : String.join(",", ms.getKeyColumns())) 223 .databaseId(databaseId) 224 .lang(ms.getLang()) 225 .resultOrdered(ms.isResultOrdered()) 226 .resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets())) 227 .resultMaps(Collections.singletonList(resultMap)) 228 .resultSetType(ms.getResultSetType()) 229 .flushCacheRequired(ms.isFlushCacheRequired()) 230 .useCache(ms.isUseCache()) 231 .cache(ms.getCache()) 232 .build(); 233 } 234 235 /** 236 * 生成新的、已替换主键生成器的 MappedStatement 237 * 238 * @param ms MappedStatement 239 * @return replaced MappedStatement 240 */ 241 private MappedStatement replaceRowKeyGenerator(MappedStatement ms) { 242 243 //执行原生 SQL,不需要为其设置主键生成器 244 if (ms.getId().endsWith("BySql")) { 245 return ms; 246 } 247 248 KeyGenerator keyGenerator = new RowKeyGenerator(ms); 249 if (ms.getId().endsWith("insertBatchWithFirstRowColumns")) { 250 keyGenerator = new MultiRowKeyGenerator(keyGenerator); 251 } 252 253 return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType()) 254 .resource(ms.getResource()) 255 .fetchSize(ms.getFetchSize()) 256 .timeout(ms.getTimeout()) 257 .statementType(ms.getStatementType()) 258 // 替换主键生成器 259 .keyGenerator(keyGenerator) 260 .keyProperty(ms.getKeyProperties() == null ? null : String.join(",", ms.getKeyProperties())) 261 .keyColumn(ms.getKeyColumns() == null ? null : String.join(",", ms.getKeyColumns())) 262 .databaseId(databaseId) 263 .lang(ms.getLang()) 264 .resultOrdered(ms.isResultOrdered()) 265 .resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets())) 266 .resultMaps(ms.getResultMaps()) 267 .resultSetType(ms.getResultSetType()) 268 .flushCacheRequired(ms.isFlushCacheRequired()) 269 .useCache(ms.isUseCache()) 270 .cache(ms.getCache()) 271 .build(); 272 } 273 274 /** 275 * 生成新的、已替换主键生成器的 MappedStatement 276 * 277 * @param ms MappedStatement 278 * @return replaced MappedStatement 279 */ 280 private MappedStatement replaceEntityKeyGenerator(MappedStatement ms) { 281 282 TableInfo tableInfo = getTableInfo(ms); 283 if (tableInfo == null) { 284 return ms; 285 } 286 287 KeyGenerator keyGenerator = MybatisKeyGeneratorUtil.createTableKeyGenerator(tableInfo, ms); 288 if (keyGenerator == NoKeyGenerator.INSTANCE) { 289 return ms; 290 } 291 292 //批量插入 293 if (ms.getId().endsWith(FlexConsts.METHOD_INSERT_BATCH)) { 294 keyGenerator = new MultiEntityKeyGenerator(keyGenerator); 295 } 296 297 return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType()) 298 .resource(ms.getResource()) 299 .fetchSize(ms.getFetchSize()) 300 .timeout(ms.getTimeout()) 301 .statementType(ms.getStatementType()) 302 // 替换主键生成器 303 .keyGenerator(keyGenerator) 304 .keyProperty(tableInfo.getKeyProperties()) 305 .keyColumn(tableInfo.getKeyColumns()) 306 .databaseId(databaseId) 307 .lang(ms.getLang()) 308 .resultOrdered(ms.isResultOrdered()) 309 .resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets())) 310 .resultMaps(ms.getResultMaps()) 311 .resultSetType(ms.getResultSetType()) 312 .flushCacheRequired(ms.isFlushCacheRequired()) 313 .useCache(ms.isUseCache()) 314 .cache(ms.getCache()) 315 .build(); 316 } 317 318 319 private TableInfo getTableInfo(MappedStatement ms) { 320 String mapperClassName = ms.getId().substring(0, ms.getId().lastIndexOf(".")); 321 try { 322 Class<?> mapperClass = Class.forName(mapperClassName); 323 return TableInfoFactory.ofMapperClass(mapperClass); 324 } catch (ClassNotFoundException e) { 325 return null; 326 } 327 } 328 329 330 @Override 331 public <T> void addMapper(Class<T> type) { 332 Type[] genericInterfaces = type.getGenericInterfaces(); 333 boolean isGenericInterface = false; 334 for (Type genericInterface : genericInterfaces) { 335 if (genericInterface instanceof ParameterizedType) { 336 Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0]; 337 if (actualTypeArgument instanceof Class) { 338 Mappers.addMapping((Class<?>) actualTypeArgument, type); 339 } else { 340 isGenericInterface = true; 341 break; 342 } 343 } 344 } 345 346 //不支持泛型类添加 347 if (!isGenericInterface) { 348 super.addMapper(type); 349 } 350 } 351 352 353 @SuppressWarnings("unchecked") 354 @Override 355 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { 356 T mapper = super.getMapper(type, sqlSession); 357 return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type} 358 , new MapperInvocationHandler(mapper, environment.getDataSource())); 359 } 360 361}