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