专业的编程技术博客社区

网站首页 > 博客文章 正文

MyBatis常用工具类二-使用ScriptRunner执行脚本

baijin 2024-10-04 13:18:07 博客文章 3 ℃ 0 评论

ScriptRunner工具类在前面的章节中我们已经使用过几次了,该工具类用于读取脚本文件中的SQL语句并执行,使用起来比较简单。下面是一个使用ScriptRunner执行SQL脚本的案例,代码如下:

   public void initData() throws  Exception {
        // 初始化数据
        Class.forName("org.hsqldb.jdbcDriver");
        // 获取Connection对象
        Connection conn = DriverManager.getConnection("jdbc:hsqldb:mem:mybatis",
                "sa", "");
        // 使用Mybatis的ScriptRunner工具类执行数据库脚本
        ScriptRunner scriptRunner = new ScriptRunner(conn);
        // 不输出sql日志
        scriptRunner.setLogWriter(null);
        scriptRunner.runScript(Resources.getResourceAsReader("create-table.sql"));
        System.out.println("------------------------");
    }

如上面的代码所示,ScriptRunner工具类的构造方法需要一个java.sql.Connection对象作为参数。创建ScriptRunner对象后,调用该对象的runScript()方法即可,该方法接收一个读取SQL脚本文件的Reader对象作为参数。

接下来我们主要分析runScript方法

 public void runScript(Reader reader) {
    // 设置事务是否自动提交
    setAutoCommit();
    try {
      // 是否一次性批量执行脚本文件中的所有SQL语句
      if (sendFullScript) {
        // 调用executeFullScript()方法一次性执行脚本文件中的所有SQL语句
        executeFullScript(reader);
      } else {
        // 调用executeLineByLine()方法逐条执行脚本中的SQL语句
        executeLineByLine(reader);
      }
    } finally {
      rollbackConnection();
    }
  }

如上面的代码所示,ScriptRunner类的runScript()方法的逻辑比较清晰,具体做了以下几件事情:

(1)调用setAutoCommit()方法,根据autoCommit属性的值设置事务是否自动提交。

(2)判断sendFullScript属性值,如果值为true,则调用executeFullScript()方法一次性读取SQL脚本文件中的所有内容,然后调用JDBC中Statement对象的execute()方法一次性执行脚本中的所有SQL语句。

(3)如果sendFullScript属性值为false,则调用executeLineByLine()方法逐行读取SQL脚本文件,以分号作为一条SQL语句结束的标志,逐条执行SQL语句。

接下来我们重点了解一下executeLineByLine()方法的实现,代码如下:

  private void executeLineByLine(Reader reader) {
    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);
    }
  }

如上面的代码所示,该方法中对脚本中的内容逐行读取,然后调用handleLine()方法处理每行读取的内容。handleLine()方法内容如下:

 private void handleLine(StringBuilder command, String line) throws SQLException {
    String trimmedLine = line.trim();
    if (lineIsComment(trimmedLine)) {
      Matcher matcher = DELIMITER_PATTERN.matcher(trimmedLine);
      if (matcher.find()) {
        delimiter = matcher.group(5);
      }
      println(trimmedLine);
    } else if (commandReadyToExecute(trimmedLine)) {
      command.append(line.substring(0, line.lastIndexOf(delimiter)));
      command.append(LINE_SEPARATOR);
      println(command);
      executeStatement(command.toString());
      command.setLength(0);
    } else if (trimmedLine.length() > 0) {
      command.append(line);
      command.append(LINE_SEPARATOR);
    }
  }

如上面的代码所示,handleLine()方法的逻辑如下:
(1)调用lineIsComment()方法判断本行内容是否为注释,如果为注释内容,则打印注释内容。
(2)调用commandReadyToExecute()方法判断本行中是否包含分号。
(3)如果本行包含分号,则说明该行是一条完整SQL的结尾。需要截取分号之前的SQL内容,与前面读取到的不包含分号的行一起组成一条完整的SQL语句执行。
(4)若该行中不包含分号,则说明该条SQL语句未结束,追加本行内容到之前读取的内容中,继续读取下一行。



Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表