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()
方法的逻辑:
- 点赞
- 收藏
- 关注作者
评论(0)