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