MyBatis源码解读(一)使用ScriptRunner执行SQL脚本文件

如果让你去实现 运行脚本文件中的SQL 功能你会怎样实现呢?

简介

ScriptRunner工具类用于读取脚本文件中的SQL语句并执行。

开发环境

  • JDK1.8
  • MyBatis3.5.7
  • MySQL

简单示例

Maven 依赖

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.29</version>
</dependency>

废话不多说,下面是一个使用ScriptRunner执行SQL脚本的简单示例,代码如下:

try {
    // 数据库连接
    Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1/mybatis?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC", "root", "abc123");
    // 创建ScriptRunner对象
    ScriptRunner scriptRunner = new ScriptRunner(connection);
    // 读取classpath路径下的文件,返回Reader对象
    Reader reader = Resources.getResourceAsReader("schema.sql");
    // 执行SQL脚本
    scriptRunner.runScript(reader);
} catch (Exception e) {
    e.printStackTrace();
}

简单细说一下,使用ScriptRunner执行SQL脚本的操作步骤:

(1)创建数据库连接,创建ScriptRunner对象时要用;
(2)调用构造方法创建ScriptRunner对象,此构造方法需要一个java.sql.Connection对象作为参数,在(1)中我们已经创建;
(3)读取 classpath 路径下的脚本文件内容,返回Reader对象;
(4)调用ScriptRunner对象的runScript()方法执行脚本,参数为(3)中创建的Reader对象。

runScript()方法源码:

public void runScript(Reader reader) {
  // 设置事务是否自动提交
  setAutoCommit();

  try {
    // 是否一次性批量执行文件中的所有SQL语句
    if (sendFullScript) {
      // 一次性批量执行文件中的所有SQL语句
      executeFullScript(reader);
    } else {
      // 逐条执行文件中的所有SQL语句
      executeLineByLine(reader);
    }
  } finally {
    rollbackConnection();
  }
}

runScript()方法调用setAutoCommit(),根据autoCommit属性的值设置事务是否自动提交,然后判断sendFullScript属性的值。如果sendFullScript值为 true,则一次性批量执行脚本文件中的所有 SQL 语句;如果sendFullScript值为 false,则逐条执行脚本文件中的 SQL 语句。

sendFullScript属性的值默认为 false,这里是逐条执行脚本文件中的 SQL 语句。

executeLineByLine()方法源码:

private void executeLineByLine(Reader reader) {
  // 要执行的SQL语句
  StringBuilder command = new StringBuilder();
  try {
    BufferedReader lineReader = new BufferedReader(reader);
    String line;
    while ((line = lineReader.readLine()) != null) {
      // 处理每行内容
      handleLine(command, line);
    }
    commitConnection();
    checkForMissingLineTerminator(command);
  } catch (Exception e) {
    String message = "Error executing: " + command + ".  Cause: " + e;
    printlnError(message);
    throw new RuntimeSqlException(message, e);
  }
}

executeLineByLine()方法逐行读取脚本文件的内容,然后调用handleLine()方法处理每行内容。

handleLine()方法源码:

private void handleLine(StringBuilder command, String line) throws SQLException {
  String trimmedLine = line.trim();
  if (lineIsComment(trimmedLine)) { // 判断该行是否是SQL注释
    Matcher matcher = DELIMITER_PATTERN.matcher(trimmedLine);
    if (matcher.find()) {
      delimiter = matcher.group(5);
    }
    println(trimmedLine);
  } else if (commandReadyToExecute(trimmedLine)) { // 判断该行是否包含分号
    // 获取该行分号之前的内容,追加到当前SQL语句
    command.append(line, 0, line.lastIndexOf(delimiter));
    // 追加行分隔符
    command.append(LINE_SEPARATOR);
    // 打印当前SQL语句
    println(command);
    // 执行当前SQL语句
    executeStatement(command.toString());
    // 清空SQL语句
    command.setLength(0);
  } else if (trimmedLine.length() > 0) { // 该行不包含分号,即当前SQL语句未结束
    // 追加当前行内容
    command.append(line);
    // 追加行分隔符
    command.append(LINE_SEPARATOR);
  }
}

handleLine()方法的逻辑:

handleLine() 方法的逻辑

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值