Java 执行 SQL 脚本文件

假定Java 程序中要定期执行 SQL 语句,因需求变更应修改原有 SQL 语句或者加上更多的语句时,不得不修改源代码,然后再次编译。 要是把 SQL 语句写在单独的 SQL 脚本文件中,由 Java 程序来定时加载执行,那么每次改动时仅仅修改 SQL 脚本文件就行了。
Java 没有提供现成的东西,所以自己写了一个这样的 SQL 脚本执行类 SqlFileExecutor。支持通用的 SQL 脚本文件,"--" 作为注释前导符,分号 ";" 分隔语句。不支持 MySQL 的 /*...*/ 形式的注释格式。对于 Windows 和 Linux/Unix 下编辑的脚本文件都测试通过。 这两个系统文件中的换行符不一样,Windows 是 "\r\n",Linux/Unix 是 "\n"。
  1package com.unmi.db;
  2
  3import java.io.FileInputStream;
  4import java.io.InputStream;
  5import java.sql.Connection;
  6import java.sql.Statement;
  7import java.util.ArrayList;
  8import java.util.Arrays;
  9import java.util.List;
 10
 11/**
 12 * 读取 SQL 脚本并执行
 13 * @author Unmi
 14 */
 15public class SqlFileExecutor {
 16
 17    /**
 18     * 读取 SQL 文件,获取 SQL 语句
 19     * @param sqlFile SQL 脚本文件
 20     * @return List<sql> 返回所有 SQL 语句的 List
 21     * @throws Exception
 22     */
 23    private List<String> loadSql(String sqlFile) throws Exception {
 24        List<String> sqlList = new ArrayList<String>();
 25
 26        try {
 27            InputStream sqlFileIn = new FileInputStream(sqlFile);
 28
 29            StringBuffer sqlSb = new StringBuffer();
 30            byte[] buff = new byte[1024];
 31            int byteRead = 0;
 32            while ((byteRead = sqlFileIn.read(buff)) != -1) {
 33                sqlSb.append(new String(buff, 0, byteRead));
 34            }
 35
 36            // Windows 下换行是 \r\n, Linux 下是 \n
 37            String[] sqlArr = sqlSb.toString().split("(;\\s*\\r\\n)|(;\\s*\\n)");
 38            for (int i = 0; i < sqlArr.length; i++) {
 39                String sql = sqlArr[i].replaceAll("--.*", "").trim();
 40                if (!sql.equals("")) {
 41                    sqlList.add(sql);
 42                }
 43            }
 44            return sqlList;
 45        } catch (Exception ex) {
 46            throw new Exception(ex.getMessage());
 47        }
 48    }
 49
 50    /**
 51     * 传入连接来执行 SQL 脚本文件,这样可与其外的数据库操作同处一个事物中
 52     * @param conn 传入数据库连接
 53     * @param sqlFile SQL 脚本文件
 54     * @throws Exception
 55     */
 56    public void execute(Connection conn, String sqlFile) throws Exception {
 57        Statement stmt = null;
 58        List<String> sqlList = loadSql(sqlFile);
 59        stmt = conn.createStatement();
 60        for (String sql : sqlList) {
 61            stmt.addBatch(sql);
 62        }
 63        int[] rows = stmt.executeBatch();
 64        System.out.println("Row count:" + Arrays.toString(rows));
 65    }
 66
 67    /**
 68     * 自建连接,独立事物中执行 SQL 文件
 69     * @param sqlFile SQL 脚本文件
 70     * @throws Exception
 71     */
 72    public void execute(String sqlFile) throws Exception {
 73        Connection conn = DBCenter.getConnection();
 74        Statement stmt = null;
 75        List<String> sqlList = loadSql(sqlFile);
 76        try {
 77            conn.setAutoCommit(false);
 78            stmt = conn.createStatement();
 79            for (String sql : sqlList) {
 80                stmt.addBatch(sql);
 81            }
 82            int[] rows = stmt.executeBatch();
 83            System.out.println("Row count:" + Arrays.toString(rows));
 84            DBCenter.commit(conn);
 85        } catch (Exception ex) {
 86            DBCenter.rollback(conn);
 87            throw ex;
 88        } finally {
 89            DBCenter.close(null, stmt, conn);
 90        }
 91    }
 92
 93    public static void main(String[] args) throws Exception {
 94        List<String> sqlList = new SqlFileExecutor().loadSql(args[0]);
 95        System.out.println("size:" + sqlList.size());
 96        for (String sql : sqlList) {
 97            System.out.println(sql);
 98        }
 99    }
100}

程序功能:加载 SQL 脚本文件,去除注释行,分离出 SQL 语句;提供两个 execute() 执行方法,分别是传数据库连接与不传连接的。 传入连接的方法可以让它与外部数据库操作同处一个事物中,当然如果能用其他机制保证事物的原子性也行。 另一个是自己管理数据库资源,例子中的 DBCenter 类留给读者自己来实现,包括获取、关闭连接,提交、回滚事物方法。 永久链接 https://yanbin.blog/java-execute-sql-script-file/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。