假定Java 程序中要定期执行 SQL 语句,因需求变更应修改原有 SQL 语句或者加上更多的语句时,不得不修改源代码,然后再次编译。要是把 SQL 语句写在单独的 SQL 脚本文件中,由 Java 程序来定时加载执行,那么每次改动时仅仅修改 SQL 脚本文件就行了。
Java 没有提供现成的东西,所以自己写了一个这样的 SQL 脚本执行类 SqlFileExecutor。支持通用的 SQL 脚本文件,"--" 作为注释前导符,分号 ";" 分隔语句。不支持 MySQL 的 /*...*/ 形式的注释格式。对于 Windows 和 Linux/Unix 下编辑的脚本文件都测试通过。这两个系统文件中的换行符不一样,Windows 是 "\r\n",Linux/Unix 是 "\n"。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
package com.unmi.db; import java.io.FileInputStream; import java.io.InputStream; import java.sql.Connection; import java.sql.Statement; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * 读取 SQL 脚本并执行 * @author Unmi */ public class SqlFileExecutor { /** * 读取 SQL 文件,获取 SQL 语句 * @param sqlFile SQL 脚本文件 * @return List<sql> 返回所有 SQL 语句的 List * @throws Exception */ private List<String> loadSql(String sqlFile) throws Exception { List<String> sqlList = new ArrayList<String>(); try { InputStream sqlFileIn = new FileInputStream(sqlFile); StringBuffer sqlSb = new StringBuffer(); byte[] buff = new byte[1024]; int byteRead = 0; while ((byteRead = sqlFileIn.read(buff)) != -1) { sqlSb.append(new String(buff, 0, byteRead)); } // Windows 下换行是 \r\n, Linux 下是 \n String[] sqlArr = sqlSb.toString().split("(;\\s*\\r\\n)|(;\\s*\\n)"); for (int i = 0; i < sqlArr.length; i++) { String sql = sqlArr[i].replaceAll("--.*", "").trim(); if (!sql.equals("")) { sqlList.add(sql); } } return sqlList; } catch (Exception ex) { throw new Exception(ex.getMessage()); } } /** * 传入连接来执行 SQL 脚本文件,这样可与其外的数据库操作同处一个事物中 * @param conn 传入数据库连接 * @param sqlFile SQL 脚本文件 * @throws Exception */ public void execute(Connection conn, String sqlFile) throws Exception { Statement stmt = null; List<String> sqlList = loadSql(sqlFile); stmt = conn.createStatement(); for (String sql : sqlList) { stmt.addBatch(sql); } int[] rows = stmt.executeBatch(); System.out.println("Row count:" + Arrays.toString(rows)); } /** * 自建连接,独立事物中执行 SQL 文件 * @param sqlFile SQL 脚本文件 * @throws Exception */ public void execute(String sqlFile) throws Exception { Connection conn = DBCenter.getConnection(); Statement stmt = null; List<String> sqlList = loadSql(sqlFile); try { conn.setAutoCommit(false); stmt = conn.createStatement(); for (String sql : sqlList) { stmt.addBatch(sql); } int[] rows = stmt.executeBatch(); System.out.println("Row count:" + Arrays.toString(rows)); DBCenter.commit(conn); } catch (Exception ex) { DBCenter.rollback(conn); throw ex; } finally { DBCenter.close(null, stmt, conn); } } public static void main(String[] args) throws Exception { List<String> sqlList = new SqlFileExecutor().loadSql(args[0]); System.out.println("size:" + sqlList.size()); for (String sql : sqlList) { System.out.println(sql); } } } |
程序功能:加载 SQL 脚本文件,去除注释行,分离出 SQL 语句;提供两个 execute() 执行方法,分别是传数据库连接与不传连接的。传入连接的方法可以让它与外部数据库操作同处一个事物中,当然如果能用其他机制保证事物的原子性也行。另一个是自己管理数据库资源,例子中的 DBCenter 类留给读者自己来实现,包括获取、关闭连接,提交、回滚事物方法。
本文链接 https://yanbin.blog/java-execute-sql-script-file/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
Connection conn = DBCenter.getConnection();这个DBCenter的类你是怎么写的
现在手头没有了,就是些管理连接,处理结果集之类的工具方法。
您好,我想定时执行 export to c:\pro.ixf of ixf select * from pro 。这样的 sql 。是db2的。可以吗?这个是单表导出数据的写法。
这是与数据库相关的命令了,如果是 Statement.execute() 能执行的 SQL 语句就行
try iBATIS ^^
确实,刚开始着手解决这个问题的时候考虑过用 iBatis。考虑到 iBatis 对一条语句一个配置的确很合适,但要一个脚本文件中批处理的执行 SQL 语句就有欠妥。也参考过 ANT 中的 任务的实现,但也是针对一条语句的执行。
@隔叶黄莺
ant执行一个sql文件完全没有问题...
所有的sql放到一个文件中...
谢谢楼上的提醒,我再次查了一下 ant 文档,执行 SQL 脚本文件,和多条 SQL 语句都行,它有的例子是:
<sql driver="org.database.jdbcDriver" url="jdbc:database-url" userid="sa" password="pass" src="data.sql"/>
和
<sql driver="org.database.jdbcDriver" url="jdbc:database-url" userid="sa" password="pass" >
insert
into table some_table values(1,2,3,4);
truncate table some_other_table; </sql>
所以如果是写在代码里,完全可以参考 ANT 源代码,它的实现考虑的更完善。把东西写下来,众人众说,总能起到抛砖引玉的作用。
建议试试groovy来进行这项工作,so easy
脚本语言个人还是习惯 Perl,它那强大的正则表达式是无与伦比的。有说 Perl 不适合于大项目,确实,但如果是大项目,我又会选择用 Java。groovy 还不太习惯,虽说它写的东西也是编译成 JVM 可执行的字节码,还能使用到 Java 的任何特性,但觉得有点麻烦,语法也不适应,Ruby就更不用说了,还会激起民族情绪。Perl 写界面也行,可以用好几个界面组件库,如 TK、wxWidgets 或 QT。
还会进一步发掘 Ant 是如何提供执行 SQL 脚本文件支持的。
请问SQL脚本中的语句有错误该怎么处理
那就是事物务理要做的事情了
如果我想执行的是一个建立存储过程的SQL脚本呢?
存储过程建立本身不是一句一句执行的,请问能否使用程序进行整个存储过程SQL执行呢?
loadSql 中的 InputStream sqlFileIn 忘记关闭了
加入有这样一个sql:
INSERT INTO ×××(title, content) VALUES('这是标题', '内容;内容');
因为“'内容;内容'”中有“;”这个程序会出错的
为什么我用了楼主的方法,一到执行那条语句就出错了?
楼主有 测试过么,期待回复
@Lc
用分号分隔的,所以会出错,这是我当时的一个项目中因为需求简单才自己写了一个这样的程序,如果要功能强的话,最好参考下 ant 和 dbUnit 的做法。