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