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.row.RowMapper; 029import com.mybatisflex.core.table.TableInfo; 030import com.mybatisflex.core.table.TableInfoFactory; 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.resultset.ResultSetHandler; 039import org.apache.ibatis.executor.statement.StatementHandler; 040import org.apache.ibatis.mapping.BoundSql; 041import org.apache.ibatis.mapping.Environment; 042import org.apache.ibatis.mapping.MappedStatement; 043import org.apache.ibatis.mapping.ResultMap; 044import org.apache.ibatis.session.*; 045import org.apache.ibatis.transaction.Transaction; 046import org.apache.ibatis.util.MapUtil; 047 048import java.lang.reflect.ParameterizedType; 049import java.lang.reflect.Proxy; 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 064 public FlexConfiguration() { 065 setObjectWrapperFactory(new FlexWrapperFactory()); 066 setDefaultEnumTypeHandler(CompositeEnumTypeHandler.class); 067 initDefaultMappers(); 068 } 069 070 071 public FlexConfiguration(Environment environment) { 072 super(environment); 073 setObjectWrapperFactory(new FlexWrapperFactory()); 074 setDefaultEnumTypeHandler(CompositeEnumTypeHandler.class); 075 initDefaultMappers(); 076 } 077 078 079 /** 080 * 设置 mybatis-flex 默认的 Mapper 081 * 当前只有 RowMapper {@link RowMapper} 082 */ 083 private void initDefaultMappers() { 084 addMapper(RowMapper.class); 085 } 086 087 088 /** 089 * 为原生 sql 设置参数 090 */ 091 @Override 092 public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { 093 String mappedStatementId = mappedStatement.getId(); 094 /** 095 * 以 "!selectKey" 结尾的 mappedStatementId,是用于 Sequence 生成主键的,无需为其设置参数 096 * {@link SelectKeyGenerator#SELECT_KEY_SUFFIX} 097 */ 098 if (!mappedStatementId.endsWith(SelectKeyGenerator.SELECT_KEY_SUFFIX) 099 && parameterObject instanceof Map 100 && ((Map<?, ?>) parameterObject).containsKey(FlexConsts.SQL_ARGS)) { 101 SqlArgsParameterHandler sqlArgsParameterHandler = new SqlArgsParameterHandler(mappedStatement, (Map) parameterObject, boundSql); 102 return (ParameterHandler) interceptorChain.pluginAll(sqlArgsParameterHandler); 103 } else { 104 return super.newParameterHandler(mappedStatement, parameterObject, boundSql); 105 } 106 } 107 108 109 @Override 110 public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement 111 , RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { 112 ResultSetHandler resultSetHandler = new FlexResultSetHandler(executor, mappedStatement, parameterHandler, 113 resultHandler, boundSql, rowBounds); 114 return (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); 115 } 116 117 /** 118 * 替换为 FlexStatementHandler,主要用来为实体类的多主键做支持、和数据审计 119 * FlexStatementHandler 和 原生的 RoutingStatementHandler 对比,没有任何性能影响 120 */ 121 @Override 122 public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 123 StatementHandler statementHandler = new FlexStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); 124 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); 125 return statementHandler; 126 } 127 128 129 /** 130 * 替换为 Flex 的 Executor,主要用于重建 CacheKey 131 * 默认情况下,Mybatis 的 CacheKey 构建是必须有 ParameterMapping,而 Flex 的 select 是不带有 ParameterMapping 的 132 */ 133 @Override 134 public Executor newExecutor(Transaction transaction, ExecutorType executorType) { 135 executorType = executorType == null ? defaultExecutorType : executorType; 136 Executor executor; 137 if (ExecutorType.BATCH == executorType) { 138 executor = new FlexBatchExecutor(this, transaction); 139 } else if (ExecutorType.REUSE == executorType) { 140 executor = new FlexReuseExecutor(this, transaction); 141 } else { 142 executor = new FlexSimpleExecutor(this, transaction); 143 } 144 if (cacheEnabled) { 145 executor = new CachingExecutor(executor); 146 } 147 executor = (Executor) interceptorChain.pluginAll(executor); 148 return executor; 149 } 150 151 152 @Override 153 public MappedStatement getMappedStatement(String id) { 154 MappedStatement ms = super.getMappedStatement(id); 155 //动态 resultsMap,方法名称为:selectListByQuery 156 Class<?> asType = MappedStatementTypes.getCurrentType(); 157 if (asType != null) { 158 return MapUtil.computeIfAbsent(dynamicMappedStatementCache, id + ":" + asType.getName(), 159 clazz -> replaceResultMap(ms, TableInfoFactory.ofEntityClass(asType)) 160 ); 161 } 162 163 return ms; 164 } 165 166 167 @Override 168 public void addMappedStatement(MappedStatement ms) { 169 //替换 RowMapper.insert 的主键生成器 170 //替换 RowMapper.insertBatchWithFirstRowColumns 的主键生成器 171 if (ms.getId().startsWith("com.mybatisflex.core.row.RowMapper.insert")) { 172 ms = replaceRowKeyGenerator(ms); 173 } 174 //entity insert methods 175 else if (StringUtil.endsWithAny(ms.getId(), "insert", FlexConsts.METHOD_INSERT_BATCH) 176 && ms.getKeyGenerator() == NoKeyGenerator.INSTANCE) { 177 ms = replaceEntityKeyGenerator(ms); 178 } 179 //entity select 180 else if (StringUtil.endsWithAny(ms.getId(), "selectOneById", "selectListByIds" 181 , "selectListByQuery", "selectCursorByQuery")) { 182 ms = replaceResultMap(ms, getTableInfo(ms)); 183 } else { 184 List<ResultMap> resultMaps = ms.getResultMaps(); 185 //根据 resultMap 里面的 class 进行判断 186 for (ResultMap resultMap : resultMaps) { 187 //获取结果的类型 188 Class<?> clazz = resultMap.getType(); 189 //判断是否为表实体类 190 if (clazz.getDeclaredAnnotation(Table.class) != null) { 191 ms = replaceResultMap(ms, getTableInfo(ms)); 192 } 193 } 194 } 195 super.addMappedStatement(ms); 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 super.addMapper(type); 351 } 352 } 353 354 355 @SuppressWarnings("unchecked") 356 @Override 357 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { 358 T mapper = super.getMapper(type, sqlSession); 359 return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type} 360 , new MapperInvocationHandler(mapper, environment.getDataSource())); 361 } 362 363}