MyBatis源码简读——1.3 基础支持模块(二)

本章主要涉及内容

事务模块,配置解析模块

事务模块

事务模块主要内容在transaction 包下面

事务模块主要是两个接口以及两种实现。

事务接口

public interface Transaction {

  /**
   * 获得连接
   * Retrieve inner database connection.
   * @return DataBase connection
   * @throws SQLException
   */
  Connection getConnection() throws SQLException;

  /**
   * 提交
   * Commit inner database connection.
   * @throws SQLException
   */
  void commit() throws SQLException;

  /**
   * 回滚
   * Rollback inner database connection.
   * @throws SQLException
   */
  void rollback() throws SQLException;

  /**
   * 关闭连接
   * Close inner database connection.
   * @throws SQLException
   */
  void close() throws SQLException;

  /**
   * 获得事务超时时间
   * Get transaction timeout if set.
   * @throws SQLException
   */
  Integer getTimeout() throws SQLException;

}

事务工厂接口

/**
 * Creates {@link Transaction} instances.
 * 事务的工厂接口
 * @author Clinton Begin
 */
public interface TransactionFactory {

  /**
   * Sets transaction factory custom properties.
   * 设置工厂的属性
   * @param props
   */
  void setProperties(Properties props);

  /**
   * Creates a {@link Transaction} out of an existing connection.
   * 创建新的事务
   * 需要连接对象
   * @param conn Existing database connection
   * @return Transaction
   * @since 3.1.0
   */
  Transaction newTransaction(Connection conn);

  /**
   * Creates a {@link Transaction} out of a datasource.
   * 创建事务,需要数据源,事务级别,是否自动提交
   * @param dataSource DataSource to take the connection from
   * @param level Desired isolation level
   * @param autoCommit Desired autocommit
   * @return Transaction
   * @since 3.1.0
   */
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);

}

其两种实现类

  • jdbc:为简单的jdbc实现,源码不算复杂。
  • managed:为基于容器管理的事务实现。

需要注意的是managed包下的实现类,很多方法都是空逻辑,其具体实现方法又容器实现,一般是spring来实现。

配置解析模块

解析器主要内容在 包下。其主要内容是:

  • 为解析mybatis配置文件,以及映射文件提供支持。
  • 另一个是为处理动态SQL中的占位符提供支持

其包下的主要类

  • XPathParser:基于java的XPath的解析器,其内部维护了一个XPath
  • XNode:mybatis自定义的一种数据对象X
  • TokenHandler:一类参数的获取
  • PropertyParser:动态属性的解析器
  • GenericTokenParser:通用的 Token 解析器

首先看TokenHandler,其主要实现类为

目前配置解析模块里面主要的实现类是VariableTokenHandler(主要是获取参数分割符)。

其代码为:

  private static class VariableTokenHandler implements TokenHandler {
    // 变量 Properties 对象
    private final Properties variables;
    // 是否开启默认值功能。默认为 {@link #ENABLE_DEFAULT_VALUE}
    // <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
    private final boolean enableDefaultValue;
    // 默认值的分隔符。默认为 {@link #KEY_DEFAULT_VALUE_SEPARATOR} ,即 ":" 。
    // <property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/>
    private final String defaultValueSeparator;

    private VariableTokenHandler(Properties variables) {
      this.variables = variables;
      this.enableDefaultValue = Boolean.parseBoolean(getPropertyValue(KEY_ENABLE_DEFAULT_VALUE, ENABLE_DEFAULT_VALUE));
      this.defaultValueSeparator = getPropertyValue(KEY_DEFAULT_VALUE_SEPARATOR, DEFAULT_VALUE_SEPARATOR);
    }

    private String getPropertyValue(String key, String defaultValue) {
      return (variables == null) ? defaultValue : variables.getProperty(key, defaultValue);
    }

    @Override
    public String handleToken(String content) {
      if (variables != null) {
        String key = content;
        // 开启默认值功能
        if (enableDefaultValue) {
          final int separatorIndex = content.indexOf(defaultValueSeparator);
          String defaultValue = null;
          if (separatorIndex >= 0) {
            key = content.substring(0, separatorIndex);
            defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());
          }
          if (defaultValue != null) {
            return variables.getProperty(key, defaultValue);
          }
        }
        // 未开启默认值功能,直接替换
        if (variables.containsKey(key)) {
          return variables.getProperty(key);
        }
      }
      // 无 variables ,直接返回
      return "${" + content + "}";
    }
  }

其主要是对变量分割符的获取,

这里面出现三种情况

  • 假如使用了默认:则会使用defaultValueSeparator中的值即org.apache.ibatis.parsing.PropertyParser.default-value-separator配置中的值获得key,然后根据字符串截取获得默认值,假如默认值不为空则使用variables.getProperty(key, defaultValue)。从属性中进行一次查询,假如查到值就返回查到的内容。
  • 假如没有使用默认值,则直接使用content的内容。
  • 假如没有设置Property对象,则直接使用"${" + content + "}"。作为参数分隔符

解析器

XPathParser:基于java的XPath的解析器,其内部维护了一个XPath

其代码为:

public XPathParser(String xml, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    this.document = createDocument(new InputSource(new StringReader(xml)));
  }

private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
    this.validation = validation;
    this.entityResolver = entityResolver;
    this.variables = variables;
    XPathFactory factory = XPathFactory.newInstance();
    this.xpath = factory.newXPath();
  }

 private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    try {
      // 创建 DocumentBuilderFactory 对象
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      // 是否验证XML
      factory.setValidating(validation);

      factory.setNamespaceAware(false);
      factory.setIgnoringComments(true);
      factory.setIgnoringElementContentWhitespace(false);
      factory.setCoalescing(false);
      factory.setExpandEntityReferences(true);
      // 创建 DocumentBuilder 对象
      DocumentBuilder builder = factory.newDocumentBuilder();
      // 设置实体解析器
      builder.setEntityResolver(entityResolver);
      // 错误处理模块
      builder.setErrorHandler(new ErrorHandler() {
        @Override
        public void error(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void fatalError(SAXParseException exception) throws SAXException {
          throw exception;
        }

        @Override
        public void warning(SAXParseException exception) throws SAXException {
        }
      });
      // 解析文件
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }

主要逻辑,就是为XPathParser初始化了,是否验证XML,Properties参数对象,XML解析器,以及根据XML内容解析成Document 。

其内部方法,主要是将数据解析成XNode对象

public List<XNode> evalNodes(Object root, String expression) {
    List<XNode> xnodes = new ArrayList<>();
    NodeList nodes = (NodeList) evaluate(expression, root, XPathConstants.NODESET);
    for (int i = 0; i < nodes.getLength(); i++) {
      xnodes.add(new XNode(this, nodes.item(i), variables));
    }
    return xnodes;
  }

PropertyParser:动态属性的解析器

public static String parse(String string, Properties variables) {
    VariableTokenHandler handler = new VariableTokenHandler(variables);
    GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
    return parser.parse(string);
  }

对SQL进行解析,将SQL中${}中的内容解析成合适的占位符。

GenericTokenParser:通用的 Token 解析器

主要逻辑就是解析以 openToken 开始,以 closeToken 结束的 Token ,并提交给 handler 进行处理

public String parse(String text) {
    if (text == null || text.isEmpty()) {
      return "";
    }
    // search open token
    // 寻找开始的 openToken 的位置
    int start = text.indexOf(openToken);
    // 找不到,直接返回
    if (start == -1) {
      return text;
    }
    char[] src = text.toCharArray();
    // 起始查找位置
    int offset = 0;
    // 结果
    final StringBuilder builder = new StringBuilder();
    // 匹配到 openToken 和 closeToken 之间的表达式
    StringBuilder expression = null;
    // 循环匹配
    while (start > -1) {
      // 转义字符
      if (start > 0 && src[start - 1] == '\\') {
        // this open token is escaped. remove the backslash and continue.
        // 因为 openToken 前面一个位置是 \ 转义字符,所以忽略 \
        // 添加 [offset, start - offset - 1] 和 openToken 的内容,添加到 builder 中
        builder.append(src, offset, start - offset - 1).append(openToken);
        offset = start + openToken.length();
      } else {// 非转义字符
        // found open token. let's search close token.
        if (expression == null) {
          expression = new StringBuilder();
        } else {
          expression.setLength(0);
        }
        // 添加 offset 和 openToken 之间的内容,添加到 builder 中
        builder.append(src, offset, start - offset);
        // 修改 offset
        offset = start + openToken.length();
        // 寻找结束的 closeToken 的位置
        int end = text.indexOf(closeToken, offset);
        // 转义
        while (end > -1) {
          if (end > offset && src[end - 1] == '\\') {
            // this close token is escaped. remove the backslash and continue.
            // 因为 endToken 前面一个位置是 \ 转义字符,所以忽略 \
            // 添加 [offset, end - offset - 1] 和 endToken 的内容,添加到 builder 中
            expression.append(src, offset, end - offset - 1).append(closeToken);
            offset = end + closeToken.length();
            end = text.indexOf(closeToken, offset);
          } else { // 非转义
            expression.append(src, offset, end - offset);
            offset = end + closeToken.length();
            break;
          }
        }
        if (end == -1) {
          // close token was not found.
          builder.append(src, start, src.length - start);
          offset = src.length;
        } else {
          builder.append(handler.handleToken(expression.toString()));
          offset = end + closeToken.length();
        }
      }
      // 继续,寻找开始的 openToken 的位置
      start = text.indexOf(openToken, offset);
    }
    // 拼接剩余的部分
    if (offset < src.length) {
      builder.append(src, offset, src.length - offset);
    }
    return builder.toString();
  }

XNode

是mybatis对数据结构的封装

/**
 * mybatis自定义的XNode对象,用来保存解析的对象
 * @author Clinton Begin
 */
public class XNode {

// 子节点
  private final Node node;
// 名称
  private final String name;
  private final String body;
// 对应数据对象和解析器
  private final Properties attributes;
  private final Properties variables;
  private final XPathParser xpathParser;

//......
}

里面提供很多对XNode查询和操作的方法,方法比较多名但单个内容不算太麻烦,这里有兴趣可以看一看。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大·风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值