mybatis的MappedStatement线程安全探究

 更新时间:2023年08月29日 09:59:37   作者:codecraft  
这篇文章主要为大家介绍了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定义了一个Builder用于构造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来专门用于创建和往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方法,根据入参parameterObject来获取BoundSql
SqlSource有DynamicSqlSource、ProviderSqlSource、RawSqlSource、StaticSqlSource这四种实现

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则代表了处理动态内容之后的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为空时,会根据自己的ParameterMap的getParameterMappings来重新构建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方法则是根据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,然后一路传递下去

小结

mybatis的MappedStatement是根据statementId从configuration获取的,这个是在启动的时候扫描注册上去的,因此如果通过反射改了MappedStatement会造成全局的影响,也可能有并发修改的问题;而BoundSql则是每次根据parameter从MappedStatement获取的,而MappedStatement则是从sqlSource获取到的BoundSql,因为每次入参都不同,所以这个BoundSql是每次执行都会new的,因而如果要在拦截器进行sql改动,改动BoundSql即可。

以上就是mybatis的MappedStatement线程安全探究的详细内容,更多关于mybatis MappedStatement线程安全的资料请关注脚本之家其它相关文章!

相关文章

  • Java多线程深入理解

    Java多线程深入理解

    这篇文章主要介绍了java多线程编程实例,分享了几则多线程的实例代码,具有一定参考价值,加深多线程编程的理解还是很有帮助的,需要的朋友可以参考下
    2021-07-07
  • 浅谈java字符串比较到底应该用==还是equals

    浅谈java字符串比较到底应该用==还是equals

    这篇文章主要介绍了浅谈java字符串比较到底应该用==还是equals,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • Mybatis的TypeHandler加解密数据实现

    Mybatis的TypeHandler加解密数据实现

    在我们数据库中有些时候会保存一些用户的敏感信息,所以就需要对这些数据进行加密,那么本文就介绍了Mybatis的TypeHandler加解密数据实现,感兴趣的可以了解一下
    2021-06-06
  • Spring @Cacheable注解类内部调用失效的解决方案

    Spring @Cacheable注解类内部调用失效的解决方案

    这篇文章主要介绍了Spring @Cacheable注解类内部调用失效的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • 关于Spring @Bean 相同加载顺序不同结果不同的问题记录

    关于Spring @Bean 相同加载顺序不同结果不同的问题记录

    本文主要探讨了在Spring 5.1.3.RELEASE版本下,当有两个全注解类定义相同类型的Bean时,由于加载顺序不同,最终生成的Bean实例也会不同,文章通过分析ConfigurationClassPostProcessor的执行过程,解释了BeanDefinition的加载和覆盖机制,感兴趣的朋友一起看看吧
    2025-02-02
  • Java每7天日志自动清理的项目实践

    Java每7天日志自动清理的项目实践

    在实际项目中由于服务器内存有限,人工清理常会忘记,本文主要介绍了Java每7天日志自动清理的项目实践,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • SpringBoot实现PPT格式文件上传并在线预览功能

    SpringBoot实现PPT格式文件上传并在线预览功能

    本文介绍SpringBoot实现PPT格式文件上传并在线预览功能,通过上传接口,可在C盘的tempfile目录下找到上传的文件,预览时会在同级目录下创建一个相同文件名后缀为pdf的文件,每次预览会先查找文件是否存在,存在则直接预览,不存在则会走上面的处理,需要的朋友可以参考下
    2022-02-02
  • Java中List常用操作比for循环更优雅的写法示例

    Java中List常用操作比for循环更优雅的写法示例

    List是Java中比较常用的集合类,关于List接口有很多实现类,下面这篇文章主要给大家介绍了关于Java中List常用操作比for循环更优雅的写法,需要的朋友可以参考下
    2021-11-11
  • JAVA查询MongoDB的几种方法小结

    JAVA查询MongoDB的几种方法小结

    本文主要介绍了JAVA查询MongoDB的几种方法小结,通过阅读本文,读者可以了解如何使用Java查询MongoDB,并在实际应用中应用这些技能,具有一定的参考价值,感兴趣的可以了解一下
    2023-08-08
  • java中BeanNotOfRequiredTypeException的问题解决(@Autowired和@Resource注解的不同)

    java中BeanNotOfRequiredTypeException的问题解决(@Autowired和@Resourc

    本文主要介绍了java中BeanNotOfRequiredTypeException的问题解决(@Autowired和@Resource注解的不同),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07

最新评论