【问题标题】:How to Execute SQL Script File in Java?如何在 Java 中执行 SQL 脚本文件?
【发布时间】:2011-01-05 12:19:46
【问题描述】:

我想在 Java 中执行一个 SQL 脚本文件,而不是将整个文件内容读入一个大查询并执行它。

还有其他标准方式吗?

【问题讨论】:

    标签: java sql-scripts


    【解决方案1】:

    不,您必须读取文件,将其拆分为单独的查询,然后单独执行它们(或使用 JDBC 的批处理 API)。

    其中一个原因是每个数据库都定义了自己的分隔 SQL 语句的方式(有些使用 ;,有些使用 /,有些允许两者甚至定义您自己的分隔符)。

    【讨论】:

      【解决方案2】:

      JDBC 不支持此选项(尽管特定的数据库驱动程序可能会提供此选项)。 无论如何,将所有文件内容加载到内存中应该没有问题。

      【讨论】:

        【解决方案3】:

        没有可移植的方式来做到这一点。不过,您可以将本机客户端作为外部程序执行:

        import java.io.*;
        public class CmdExec {
        
          public static void main(String argv[]) {
            try {
              String line;
              Process p = Runtime.getRuntime().exec
                ("psql -U username -d dbname -h serverhost -f scripfile.sql");
              BufferedReader input =
                new BufferedReader
                  (new InputStreamReader(p.getInputStream()));
              while ((line = input.readLine()) != null) {
                System.out.println(line);
              }
              input.close();
            }
            catch (Exception err) {
              err.printStackTrace();
            }
          }
        }
        
        • 代码示例是从here 中提取的,并经过修改以回答假设用户想要执行 PostgreSQL 脚本文件的问题。

        【讨论】:

          【解决方案4】:

          您不能使用 JDBC,因为它不支持 .解决方法是包括 iBatis iBATIS 是一个持久性框架并调用 Scriptrunner 构造函数,如 iBatis 文档中所示。

          包含像 ibatis 这样重量级的持久性框架来运行简单的 sql 脚本是不好的,你可以使用命令行执行任何方式

          $ mysql -u root -p db_name < test.sql
          

          【讨论】:

            【解决方案5】:

            只要您不介意对 Ant 的依赖,有很多方法可以从 Java 执行 SQL 脚本而无需自己阅读它们。在我看来,这种依赖在你的情况下是非常合理的。这是示例代码,其中 SQLExec 类位于 ant.jar 中:

            private void executeSql(String sqlFilePath) {
                final class SqlExecuter extends SQLExec {
                    public SqlExecuter() {
                        Project project = new Project();
                        project.init();
                        setProject(project);
                        setTaskType("sql");
                        setTaskName("sql");
                    }
                }
            
                SqlExecuter executer = new SqlExecuter();
                executer.setSrc(new File(sqlFilePath));
                executer.setDriver(args.getDriver());
                executer.setPassword(args.getPwd());
                executer.setUserid(args.getUser());
                executer.setUrl(args.getUrl());
                executer.execute();
            }
            

            【讨论】:

            • 我对我的几个 Oracle SQL 脚本进行了尝试,它适用于 insertcreate table。但是对于具有create or replace trigger 的脚本,它会因 java.sql.SQLSyntaxErrorException: ORA-00900: invalid SQL statement 而失败
            • 也许应该考虑你的分隔符
            【解决方案6】:

            试试这个代码:

            String strProc =
                     "DECLARE \n" +
                     "   sys_date DATE;"+
                     "" +
                     "BEGIN\n" +
                     "" +
                     "   SELECT SYSDATE INTO sys_date FROM dual;\n" +
                     "" +
                     "END;\n";
            
            try{
                DriverManager.registerDriver ( new oracle.jdbc.driver.OracleDriver () );
                Connection connection = DriverManager.getConnection ("jdbc:oracle:thin:@your_db_IP:1521:your_db_SID","user","password");  
                PreparedStatement psProcToexecute = connection.prepareStatement(strProc);
                psProcToexecute.execute();
            }catch (Exception e) {
                System.out.println(e.toString());  
            }
            

            【讨论】:

            • 您的示例过于简单。 Statement 通常一次只能执行一条 SQL 语句。该问题意味着涉及大量查询或插入,因此需要通过查找分隔符将它们拆分为单独的语句。
            • 同意,此解决方案不适用于多语句脚本。按照下面的建议使用 jisql。
            【解决方案7】:

            由于 JDBC 不支持此选项,解决此问题的最佳方法是通过 Java 程序执行命令行。 Bellow 是 postgresql 的一个例子:

            private void executeSqlFile() {
                 try {
                     Runtime rt = Runtime.getRuntime();
                     String executeSqlCommand = "psql -U (user) -h (domain) -f (script_name) (dbName)";
                     Process pr = rt.exec();
                     int exitVal = pr.waitFor();
                     System.out.println("Exited with error code " + exitVal);
                  } catch (Exception e) {
                    System.out.println(e.toString());
                  }
            }
            

            【讨论】:

              【解决方案8】:

              Flyway 库非常适合这个:

                  Flyway flyway = new Flyway();
                  flyway.setDataSource(dbConfig.getUrl(), dbConfig.getUsername(), dbConfig.getPassword());
                  flyway.setLocations("classpath:db/scripts");
                  flyway.clean();
                  flyway.migrate();
              

              这会扫描脚本的位置并按顺序运行它们。可以使用 V01__name.sql 对脚本进行版本控制,因此如果只调用 migrate ,那么只有那些尚未运行的才会运行。使用名为“schema_version”的表来跟踪事物。但也可以做其他事情,请参阅文档:flyway

              clean 调用不是必需的,但对于从干净的 DB 开始很有用。 另外,请注意位置(默认为“classpath:db/migration”),':' 后面没有空格,这让我发现了。

              【讨论】:

                【解决方案9】:

                我发现最简单的可移植外部工具是 jisql - https://www.xigole.com/software/jisql/jisql.jsp 。 你可以这样运行它:

                java -classpath lib/jisql.jar:\
                          lib/jopt-simple-3.2.jar:\
                          lib/javacsv.jar:\
                           /home/scott/postgresql/postgresql-8.4-701.jdbc4.jar 
                    com.xigole.util.sql.Jisql -user scott -password blah     \
                    -driver postgresql                                       \
                    -cstring jdbc:postgresql://localhost:5432/scott -c \;    \
                    -query "select * from test;"
                

                【讨论】:

                • 我使用了java -classpath lib/jisql-2.0.11.jar:lib/jopt-simple-3.2.jar:lib/javacsv.jar:../ojdbc7.jar com.xigole.util.sql.Jisql -user ecm -password TODO -driver oracle.jdbc.OracleDriver -cstring jdbc:oracle:thin:@localhost:1521:XE -c \; -input myoracle.sql,它完美无缺。
                【解决方案10】:

                如果你使用Spring,你可以使用DataSourceInitializer

                @Bean
                public DataSourceInitializer dataSourceInitializer(@Qualifier("dataSource") final DataSource dataSource) {
                    ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();
                    resourceDatabasePopulator.addScript(new ClassPathResource("/data.sql"));
                    DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
                    dataSourceInitializer.setDataSource(dataSource);
                    dataSourceInitializer.setDatabasePopulator(resourceDatabasePopulator);
                    return dataSourceInitializer;
                }
                

                用于在初始化期间建立数据库并清理一个 销毁期间的数据库。

                https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/datasource/init/DataSourceInitializer.html

                【讨论】:

                • 这是Spring用户最好的解决方案,使用DatabasePopulatorUtils.execute(resourceDatabasePopulator, dataSource)在代码中的任意位置执行脚本。
                【解决方案11】:

                Apache iBatis 解决方案非常有效。

                我使用的脚本示例正是我从 MySql 工作台运行的脚本。

                这里有一篇带有示例的文章: https://www.tutorialspoint.com/how-to-run-sql-script-using-jdbc#:~:text=You%20can%20execute%20.,to%20pass%20a%20connection%20object.&text=Register%20the%20MySQL%20JDBC%20Driver,method%20of%20the%20DriverManager%20class.

                这就是我所做的:

                pom.xml 依赖

                <!-- IBATIS SQL Script runner from Apache (https://mvnrepository.com/artifact/org.apache.ibatis/ibatis-core) -->
                <dependency>
                    <groupId>org.apache.ibatis</groupId>
                    <artifactId>ibatis-core</artifactId>
                    <version>3.0</version>
                </dependency>
                

                执行脚本的代码:

                import java.io.BufferedReader;
                import java.io.File;
                import java.io.FileReader;
                import java.io.Reader;
                import java.sql.Connection;   
                import org.apache.ibatis.jdbc.ScriptRunner;   
                import lombok.extern.slf4j.Slf4j;
                
                @Slf4j
                public class SqlScriptExecutor {
                
                    public static void executeSqlScript(File file, Connection conn) throws Exception {
                        Reader reader = new BufferedReader(new FileReader(file));
                        log.info("Running script from file: " + file.getCanonicalPath());
                        ScriptRunner sr = new ScriptRunner(conn);
                        sr.setAutoCommit(true);
                        sr.setStopOnError(true);
                        sr.runScript(reader);
                        log.info("Done.");
                    }
                    
                }
                

                【讨论】:

                  【解决方案12】:

                  对于我的简单项目,用户应该能够选择执行的 SQL 文件。 由于我对其他答案不满意并且无论如何我都在使用 Flyway,因此我仔细查看了 Flyway 代码。 DefaultSqlScriptExecutor 正在实际执行,所以我试图弄清楚如何创建DefaultSqlScriptExecutor 的实例。

                  基本上下面的 sn -p 加载一个String 将其拆分为单个语句并一一执行。 Flyway 还提供除 StringResource 之外的其他 LoadableResources,例如FileSystemResource。但我没有仔细研究它们。

                  由于 DefaultSqlScriptExecutor 和 Flyway 未正式记录其他类,请谨慎使用 code-sn-p。

                  public static void execSqlQueries(String sqlQueries, Configuration flyWayConf) throws SQLException {
                    // create dependencies FlyWay needs to execute the SQL queries
                    JdbcConnectionFactory jdbcConnectionFactory = new JdbcConnectionFactory(flyWayConf.getDataSource(),
                        flyWayConf.getConnectRetries(),
                        null);
                    DatabaseType databaseType = jdbcConnectionFactory.getDatabaseType();
                    ParsingContext parsingContext = new ParsingContext();
                    SqlScriptFactory sqlScriptFactory = databaseType.createSqlScriptFactory(flyWayConf, parsingContext);
                    Connection conn = flyWayConf.getDataSource().getConnection();
                    JdbcTemplate jdbcTemp = new JdbcTemplate(conn);
                    ResourceProvider resProv = flyWayConf.getResourceProvider();
                    DefaultSqlScriptExecutor scriptExec = new DefaultSqlScriptExecutor(jdbcTemp, null, false, false, false, null);
                    
                    // Prepare and execute the actual queries
                    StringResource sqlRes = new StringResource(sqlQueries);
                    SqlScript sqlScript = sqlScriptFactory.createSqlScript(sqlRes, true, resProv);
                    scriptExec.execute(sqlScript);
                  }
                  

                  【讨论】:

                    最近更新 更多