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