mybatis的MappedStatement線程安全探究
序
本文主要研究一下mybatis MappedStatement
MappedStatement
org/apache/ibatis/mapping/MappedStatement.java
public final class MappedStatement { private String resource; private Configuration configuration; private String id; private Integer fetchSize; private Integer timeout; private StatementType statementType; private ResultSetType resultSetType; private SqlSource sqlSource; private Cache cache; private ParameterMap parameterMap; private List<ResultMap> resultMaps; private boolean flushCacheRequired; private boolean useCache; private boolean resultOrdered; private SqlCommandType sqlCommandType; private KeyGenerator keyGenerator; private String[] keyProperties; private String[] keyColumns; private boolean hasNestedResultMaps; private String databaseId; private Log statementLog; private LanguageDriver lang; private String[] resultSets; private boolean dirtySelect; //...... }
MappedStatement定義了SqlSource
MappedStatement.Builder
public static class Builder { private final MappedStatement mappedStatement = new MappedStatement(); public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) { mappedStatement.configuration = configuration; mappedStatement.id = id; mappedStatement.sqlSource = sqlSource; mappedStatement.statementType = StatementType.PREPARED; mappedStatement.resultSetType = ResultSetType.DEFAULT; mappedStatement.parameterMap = new ParameterMap.Builder(configuration, "defaultParameterMap", null, new ArrayList<>()).build(); mappedStatement.resultMaps = new ArrayList<>(); mappedStatement.sqlCommandType = sqlCommandType; mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; String logId = id; if (configuration.getLogPrefix() != null) { logId = configuration.getLogPrefix() + id; } mappedStatement.statementLog = LogFactory.getLog(logId); mappedStatement.lang = configuration.getDefaultScriptingLanguageInstance(); } //...... }
MappedStatement定義了一個(gè)Builder用于構(gòu)造MappedStatement
MapperBuilderAssistant
org/apache/ibatis/builder/MapperBuilderAssistant.java
public class MapperBuilderAssistant extends BaseBuilder { public MappedStatement addMappedStatement(String id, SqlSource sqlSource, StatementType statementType, SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class<?> parameterType, String resultMap, Class<?> resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache, boolean resultOrdered, KeyGenerator keyGenerator, String keyProperty, String keyColumn, String databaseId, LanguageDriver lang, String resultSets, boolean dirtySelect) { if (unresolvedCacheRef) { throw new IncompleteElementException("Cache-ref not yet resolved"); } id = applyCurrentNamespace(id, false); MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType) .resource(resource).fetchSize(fetchSize).timeout(timeout).statementType(statementType) .keyGenerator(keyGenerator).keyProperty(keyProperty).keyColumn(keyColumn).databaseId(databaseId).lang(lang) .resultOrdered(resultOrdered).resultSets(resultSets) .resultMaps(getStatementResultMaps(resultMap, resultType, id)).resultSetType(resultSetType) .flushCacheRequired(flushCache).useCache(useCache).cache(currentCache).dirtySelect(dirtySelect); ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id); if (statementParameterMap != null) { statementBuilder.parameterMap(statementParameterMap); } MappedStatement statement = statementBuilder.build(); configuration.addMappedStatement(statement); return statement; } //...... }
MapperBuilderAssistant定義了addMappedStatement來(lái)專門用于創(chuàng)建和往configuration添加MappedStatement
Configuration
org/apache/ibatis/session/Configuration.java
public class Configuration { protected Environment environment; protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>( "Mapped Statements collection") .conflictMessageProducer((savedValue, targetValue) -> ". please check " + savedValue.getResource() + " and " + targetValue.getResource()); //...... public void addMappedStatement(MappedStatement ms) { mappedStatements.put(ms.getId(), ms); } //...... }
Configuration則定義了以statementId為key,value為MappedStatement的StrictMap
Configuration.StrictMap
protected static class StrictMap<V> extends ConcurrentHashMap<String, V> { private static final long serialVersionUID = -4950446264854982944L; private final String name; private BiFunction<V, V, String> conflictMessageProducer; public StrictMap(String name, int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); this.name = name; } public StrictMap(String name, int initialCapacity) { super(initialCapacity); this.name = name; } //...... }
StrictMap繼承了ConcurrentHashMap
SqlSource
org/apache/ibatis/mapping/SqlSource.java
/** * Represents the content of a mapped statement read from an XML file or an annotation. It creates the SQL that will be * passed to the database out of the input parameter received from the user. * * @author Clinton Begin */ public interface SqlSource { BoundSql getBoundSql(Object parameterObject); }
而SqlSource接口則定義了getBoundSql方法,根據(jù)入?yún)arameterObject來(lái)獲取BoundSql
SqlSource有DynamicSqlSource、ProviderSqlSource、RawSqlSource、StaticSqlSource這四種實(shí)現(xiàn)
BoundSql
org/apache/ibatis/mapping/BoundSql.java
public class BoundSql { private final String sql; private final List<ParameterMapping> parameterMappings; private final Object parameterObject; private final Map<String, Object> additionalParameters; private final MetaObject metaParameters; //...... }
BoundSql則代表了處理動(dòng)態(tài)內(nèi)容之后的SQL,該SQL可能還包含占位符
MappedStatement.getBoundSql
public BoundSql getBoundSql(Object parameterObject) { BoundSql boundSql = sqlSource.getBoundSql(parameterObject); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings == null || parameterMappings.isEmpty()) { boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject); } // check for nested result maps in parameter mappings (issue #30) for (ParameterMapping pm : boundSql.getParameterMappings()) { String rmId = pm.getResultMapId(); if (rmId != null) { ResultMap rm = configuration.getResultMap(rmId); if (rm != null) { hasNestedResultMaps |= rm.hasNestedResultMaps(); } } } return boundSql; }
MappedStatement的getBoundSql方法,在從sqlSource獲取到的boundSql的parameterMappings為空時(shí),會(huì)根據(jù)自己的ParameterMap的getParameterMappings來(lái)重新構(gòu)建boundSql
DefaultSqlSession
org/apache/ibatis/session/defaults/DefaultSqlSession.java
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { MappedStatement ms = configuration.getMappedStatement(statement); return executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
DefaultSqlSession的selectList方法則是根據(jù)statement從configuration獲取到MappedStatement然后傳遞給executor
BaseExecutor
org/apache/ibatis/executor/BaseExecutor.java
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameter); CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); return query(ms, parameter, rowBounds, resultHandler, key, boundSql); }
BaseExecutor的query從MappedStatement獲取到了BoundSql,然后一路傳遞下去
小結(jié)
mybatis的MappedStatement是根據(jù)statementId從configuration獲取的,這個(gè)是在啟動(dòng)的時(shí)候掃描注冊(cè)上去的,因此如果通過(guò)反射改了MappedStatement會(huì)造成全局的影響,也可能有并發(fā)修改的問(wèn)題;而BoundSql則是每次根據(jù)parameter從MappedStatement獲取的,而MappedStatement則是從sqlSource獲取到的BoundSql,因?yàn)槊看稳雲(yún)⒍疾煌?,所以這個(gè)BoundSql是每次執(zhí)行都會(huì)new的,因而如果要在攔截器進(jìn)行sql改動(dòng),改動(dòng)BoundSql即可。
以上就是mybatis的MappedStatement線程安全探究的詳細(xì)內(nèi)容,更多關(guān)于mybatis MappedStatement線程安全的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
淺談java字符串比較到底應(yīng)該用==還是equals
這篇文章主要介紹了淺談java字符串比較到底應(yīng)該用==還是equals,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12Mybatis的TypeHandler加解密數(shù)據(jù)實(shí)現(xiàn)
在我們數(shù)據(jù)庫(kù)中有些時(shí)候會(huì)保存一些用戶的敏感信息,所以就需要對(duì)這些數(shù)據(jù)進(jìn)行加密,那么本文就介紹了Mybatis的TypeHandler加解密數(shù)據(jù)實(shí)現(xiàn),感興趣的可以了解一下2021-06-06Spring?@Cacheable注解類內(nèi)部調(diào)用失效的解決方案
這篇文章主要介紹了Spring?@Cacheable注解類內(nèi)部調(diào)用失效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01關(guān)于Spring @Bean 相同加載順序不同結(jié)果不同的問(wèn)題記錄
本文主要探討了在Spring 5.1.3.RELEASE版本下,當(dāng)有兩個(gè)全注解類定義相同類型的Bean時(shí),由于加載順序不同,最終生成的Bean實(shí)例也會(huì)不同,文章通過(guò)分析ConfigurationClassPostProcessor的執(zhí)行過(guò)程,解釋了BeanDefinition的加載和覆蓋機(jī)制,感興趣的朋友一起看看吧2025-02-02Java每7天日志自動(dòng)清理的項(xiàng)目實(shí)踐
在實(shí)際項(xiàng)目中由于服務(wù)器內(nèi)存有限,人工清理常會(huì)忘記,本文主要介紹了Java每7天日志自動(dòng)清理的項(xiàng)目實(shí)踐,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01SpringBoot實(shí)現(xiàn)PPT格式文件上傳并在線預(yù)覽功能
本文介紹SpringBoot實(shí)現(xiàn)PPT格式文件上傳并在線預(yù)覽功能,通過(guò)上傳接口,可在C盤的tempfile目錄下找到上傳的文件,預(yù)覽時(shí)會(huì)在同級(jí)目錄下創(chuàng)建一個(gè)相同文件名后綴為pdf的文件,每次預(yù)覽會(huì)先查找文件是否存在,存在則直接預(yù)覽,不存在則會(huì)走上面的處理,需要的朋友可以參考下2022-02-02Java中List常用操作比f(wàn)or循環(huán)更優(yōu)雅的寫法示例
List是Java中比較常用的集合類,關(guān)于List接口有很多實(shí)現(xiàn)類,下面這篇文章主要給大家介紹了關(guān)于Java中List常用操作比f(wàn)or循環(huán)更優(yōu)雅的寫法,需要的朋友可以參考下2021-11-11java中BeanNotOfRequiredTypeException的問(wèn)題解決(@Autowired和@Resourc
本文主要介紹了java中BeanNotOfRequiredTypeException的問(wèn)題解決(@Autowired和@Resource注解的不同),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07