使用MyBatis中的ScriptRunner来执行sql文件脚本,实现启动自动部署数据库

最近项目中想要添入启动制动完成数据库配置的功能,刚开始想到的ANT方式,但是放弃了(凡是放弃的,根本原因是:我不会........);所以最后采用了ScriptRunner来执行,这个方法无论是DML还是DDL都可以执行,但是有几点比较坑~~,咱们后话说。

首先我们介绍下当前的环境:SpringBoot2.0.2+MySql;

首先建立数据库连接,配置基本的url,drive,username和password,不多说,基础写法:

Connection conn = null;
try{
    Class.forName(driver);
    conn = DriverManager.getConnection(url, userName, password);
}catch(Exception e){
    conn.rollback();
    _log.error("数据回滚成功");
}

然后我们将Connection设置成不自动提交,这样才可以使用conn.rollback回滚功能;这点要注意。

接下来声明一个ScriptRunner来建立基本的读取规则;基本设置代码注释见:

        ScriptRunner runner = new ScriptRunner(conn);
	// 设置不自动提交
	runner.setAutoCommit(false);
	/*
	 * setStopOnError参数作用:遇见错误是否停止;
	 * (1)false,遇见错误不会停止,会继续执行,会打印异常信息,并不会抛出异常,当前方法无法捕捉异常无法进行回滚操作,
	 * 无法保证在一个事务内执行; (2)true,遇见错误会停止执行,打印并抛出异常,捕捉异常,并进行回滚,保证在一个事务内执行;
	 */
	runner.setStopOnError(true);
	/*
	 * 按照那种方式执行 方式一:true则获取整个脚本并执行; 方式二:false则按照自定义的分隔符每行执行;
	 */
	runner.setSendFullScript(false);
	// 设置是否输出日志,null不输出日志,不设置自动将日志输出到控制台
	runner.setLogWriter(null);

接下来使用方法runScript来读取SQL脚本就可以了,具体的执行,在runner点方法可以查看,我在这里使用runScript来读取执行File文件,例如:

runner.runScript(Resources.getResourceAsReader("table/****/****.sql"));

这里要注意,我这里使用Resources.getResourceAsReader(String source);这个方法,这个方法可以返回一个File文件,不过,他只能读取classpath路径下的文件,也就是说,如果你只是单纯的将脚本放到工程的根目录下是无法读取到的,这里可以将sql文件夹添加到ClassPath路径下,具体的添加方法如下:

在使用Eclipse的通知可以右键工程-->Preferences-->Java Build Path-->Source下,如图:

点击Add Folder....按钮,如图,将SQL文件夹前面打上对号,表示将此文件及子文件加入classpath:

此时根据路径就可以实现读取脚本。

如果要执行多个SQL文件,可以重复使用runScript方法来执行,我这里因为写死了,并且有先后规定,所以写了个switch下面的是完整的方法:

 

package com.dcjt518.oms.common.conf;

import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.jdbc.ScriptRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @{自动读取SQL语句}
 * @author 创建人: LuNanTing
 * @Title 标 题: DataSourceTest.java
 * @update 修改人:LuNanTing
 * @date 修改事件:2018年11月1日
 */
public class DataScriptExecution {

	private String driver;
	private String url;
	private String userName;
	private String password;
	private final Logger _log = LoggerFactory.getLogger(DataScriptExecution.class);
	Exception error = null;

	public void test() {
		driver = "com.mysql.jdbc.Driver";
		url = "jdbc:mysql://127.0.0.1:3306/DcOmsTest?characterEncoding=utf-8&useUnicode=true";
		userName = "root";
		password = "123456";
		Connection conn = null;
		try {
			Class.forName(driver);
			conn = DriverManager.getConnection(url, userName, password);
			// 设置不自动提交
			conn.setAutoCommit(false);
			ScriptRunner runner = new ScriptRunner(conn);
			// 设置不自动提交
			runner.setAutoCommit(false);
			/*
			 * setStopOnError参数作用:遇见错误是否停止;
			 * (1)false,遇见错误不会停止,会继续执行,会打印异常信息,并不会抛出异常,当前方法无法捕捉异常无法进行回滚操作,
			 * 无法保证在一个事务内执行; (2)true,遇见错误会停止执行,打印并抛出异常,捕捉异常,并进行回滚,保证在一个事务内执行;
			 */
			runner.setStopOnError(true);
			/*
			 * 按照那种方式执行 方式一:true则获取整个脚本并执行; 方式二:false则按照自定义的分隔符每行执行;
			 */
			runner.setSendFullScript(false);

			// 设置是否输出日志,null不输出日志,不设置自动将日志输出到控制台
			runner.setLogWriter(null);
			// 如果又多个sql文件,可以写多个runner.runScript(xxx),
			Resources.setCharset(Charset.forName("UTF8"));
			int i = 0;
			while (i != -1) {
				switch (i) {
					case 0 :
						i++;
						// 定义命令间的分隔符
						runner.setDelimiter(";");
						runner.setFullLineDelimiter(false);
						runner.runScript(Resources.getResourceAsReader("table/****/test1.sql"));
						break;
					case 1 :
						i++;
						// 定义命令间的分隔符
						runner.setDelimiter(";");
						runner.setFullLineDelimiter(false);
						runner.runScript(Resources.getResourceAsReader("table/****/test2.sql"));
						break;
					case 2 :
						i++;
						// 定义命令间的分隔符
						runner.setDelimiter(";;");
						runner.setFullLineDelimiter(false);
						runner.runScript(Resources.getResourceAsReader("table/****/test3.sql"));
						break;
					case 3 :
						i++;
						runner.setDelimiter(";;");
						runner.setFullLineDelimiter(false);
						runner.runScript(Resources.getResourceAsReader("table/****/test4.sql"));
						break;
					case 4 :
						i++;
						runner.setDelimiter(";;");
						runner.setFullLineDelimiter(false);
						runner.runScript(Resources.getResourceAsReader("table/****/test5.sql"));
						break;
					case 5 :
						i++;
						runner.setDelimiter(";;");
						runner.setFullLineDelimiter(false);
						runner.runScript(Resources.getResourceAsReader("table/****/test6.sql"));
						break;
					case 6 :
						i++;
						runner.setDelimiter(";;");
						runner.setFullLineDelimiter(false);
						runner.runScript(Resources.getResourceAsReader("table/****/test7.sql"));
						break;
					case 7 :
						i++;
						runner.setDelimiter(";;");
						runner.setFullLineDelimiter(false);
						runner.runScript(Resources.getResourceAsReader("table/****/test8.sql"));
						break;
					case 8 :
						i++;
						runner.setDelimiter(";;");
						runner.setFullLineDelimiter(false);
						runner.runScript(Resources.getResourceAsReader("table/****/test9.sql"));
						break;
					case 9 :
						i++;
						runner.setDelimiter(";;");
						runner.setFullLineDelimiter(false);
						runner.runScript(Resources.getResourceAsReader("table/****/test10.sql"));
						break;
					default :
						i = -1;
						continue;
				}
			}
		} catch (Exception e) {
			try {
				conn.rollback();
				_log.error("数据回滚成功");
			} catch (SQLException e1) {
				_log.error("数据回滚失败,系统错误");
			}
			_log.error("执行sql文件进行数据库创建失败....", e);
			error = e;
		} finally {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
				error = e;
			}
		}
		if (error != null) {
			try {
				throw error;
			} catch (Exception e) {
				_log.error(error.getLocalizedMessage());
			}
		} else {
			_log.info("SQL脚本执行完成");
		}
	}
}

 相信大家也看出来了,在每次执行的时候,我都要重新定义分隔符,尤其是存储过程中,他是无法按照普通的DDL来执行的,因为,在MySql中默认分隔符是;,这就导致每次执行存储过程的时候就会报错,最后error定位在分隔符上,无奈只有改变存储过程的分割方式,让他遇到;;来结束,但这样的脚本只能在项目中执行,如果放到Navicat中是没有办法执行的,于是在存储过程的脚本头部加入//delimiter ;; ;这样双方都能看得懂,也能单独执行了。但如果使用的SQLServer就没有这样麻烦的事情,因为SQLServer使用的分隔符是GO这个命令,就没有上述那么麻烦。这里需要注意。

说到这里了,既然希望能项目部署就可以运行,当然是要在Spring容器刚刚启动的时候,或者服务容器启动而Spring容器还未启动的时候执行,这里就可以用到Spring中的@WebListener注解,他是服务容器启动监听方法,在contextInitialized方法中调用上边的方法,完美实现了启动部署数据库。

希望各位大牛能指出错误地方!!!谢谢!!!!

 

补在最后,有朋友说不太清楚这里的runner.setDelimiter(";;");的问题,这个主要是设置sql文件分割执行的分隔符,也就是在上面的代码中,一般的sql语句我用的是“;”来分割,但是存储过程的话用";;",基本的sql我就不举例了,我随便贴一个存储过程的写法:

delimiter ;; ;
drop procedure if exists alter_table;
;;
create  procedure `alter_table`()
begin
		-- 申明当前执行语句实在那个数据库
	declare database_obj varchar(64);
	
	
	-- 查询当前sql是在哪个库执行
	select database() into  database_obj
end;
;;
call alter_table();
;;

 

  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值