简体   繁体   English

如何在 Java 中执行 SQL 脚本文件?

[英]How to Execute SQL Script File in Java?

I want to execute an SQL script file in Java without reading the entire file content into a big query and executing it.我想在 Java 中执行一个 SQL 脚本文件,而不是将整个文件内容读入一个大查询并执行它。

Is there any other standard way?有没有其他标准方法?

There is great way of executing SQL scripts from Java without reading them yourself as long as you don't mind having a dependency on Ant.只要您不介意依赖 Ant,就可以从 Java 执行 SQL 脚本,而无需自己阅读它们。 In my opinion such a dependency is very well justified in your case.在我看来,这种依赖在你的情况下是非常合理的。 Here is sample code, where SQLExec class lives in ant.jar:这是示例代码,其中 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();
}

There is no portable way of doing that.没有便携式方法可以做到这一点。 You can execute a native client as an external program to do that though:您可以将本机客户端作为外部程序执行来执行此操作:

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();
    }
  }
}
  • Code sample was extracted from here and modified to answer question assuming that the user wants to execute a PostgreSQL script file.代码示例从这里提取并修改为回答问题,假设用户想要执行 PostgreSQL 脚本文件。

Flyway library is really good for this: Flyway 库对此非常有用:

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

This scans the locations for scripts and runs them in order.这会扫描脚本的位置并按顺序运行它们。 Scripts can be versioned with V01__name.sql so if just the migrate is called then only those not already run will be run.脚本可以使用 V01__name.sql 进行版本控制,因此如果只调用 migrate,那么只会运行那些尚未运行的脚本。 Uses a table called 'schema_version' to keep track of things.使用名为“schema_version”的表来跟踪事物。 But can do other things too, see the docs: flyway .但也可以做其他事情,请参阅文档: flyway

The clean call isn't required, but useful to start from a clean DB. clean 调用不是必需的,但对于从干净的数据库开始很有用。 Also, be aware of the location (default is "classpath:db/migration"), there is no space after the ':', that one caught me out.另外,请注意位置(默认为“classpath:db/migration”),':' 后面没有空格,这让我很困惑。

No, you must read the file, split it into separate queries and then execute them individually (or using the batch API of JDBC).不,您必须读取文件,将其拆分为单独的查询,然后单独执行(或使用 JDBC 的批处理 API)。

One of the reasons is that every database defines their own way to separate SQL statements (some use ; , others / , some allow both or even to define your own separator).原因之一是每个数据库都定义了自己的分隔 SQL 语句的方式(有些使用; ,有些使用/ ,有些允许两者甚至定义自己的分隔符)。

You cannot do using JDBC as it does not support .您不能使用 JDBC,因为它不支持 . Work around would be including iBatis iBATIS is a persistence framework and call the Scriptrunner constructor as shown in iBatis documentation .解决方法是包括 iBatis iBATIS 是一个持久性框架,并调用Scriptrunner构造函数,如iBatis文档中所示。

Its not good to include a heavy weight persistence framework like ibatis in order to run a simple sql scripts any ways which you can do using command line包含像 ibatis 这样的重量级持久性框架以运行简单的 sql 脚本并不好,您可以使用命令行以任何方式执行此操作

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

Since JDBC doesn't support this option the best way to solve this question is executing command lines via the Java Program.由于 JDBC 不支持此选项,因此解决此问题的最佳方法是通过 Java 程序执行命令行。 Bellow is an example to postgresql: 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());
      }
}

The simplest external tool that I found that is also portable is jisql - https://www.xigole.com/software/jisql/jisql.jsp .我发现也是可移植的最简单的外部工具是 jisql - https://www.xigole.com/software/jisql/jisql.jsp You would run it as:你会运行它:

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;"

JDBC does not support this option (although a specific DB driver may offer this). JDBC 不支持此选项(尽管特定的 DB 驱动程序可能会提供此选项)。 Anyway, there should not be a problem with loading all file contents into memory.无论如何,将所有文件内容加载到内存中应该没有问题。

Try this code:试试这个代码:

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());  
}

If you use Spring you can use DataSourceInitializer :如果您使用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;
}

Used to set up a database during initialization and clean up a database during destruction.用于在初始化期间建立数据库并在销毁期间清理数据库。

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

The Apache iBatis solution worked like a charm. Apache iBatis 解决方案就像一个魅力。

The script example I used was exactly the script I was running from MySql workbench.我使用的脚本示例正是我从 MySql 工作台运行的脚本。

There is an article with examples here: 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 .这里有一篇带有示例的文章: https : //www.tutorialspoint.com/how-to-run-sql-script-using-jdbc# :~: text=You%20can%20execute%20.,to%20pass% 20a%20connection%20object.&text=注册%20the%20MySQL%20JDBC%20Driver,method%20of%20the%20DriverManager%20class

This is what I did:这就是我所做的:

pom.xml dependency 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>

Code to execute script:执行脚本的代码:

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.");
    }
    
}

For my simple project the user should be able to select SQL-files which get executed.对于我的简单项目,用户应该能够选择执行的 SQL 文件。 As I was not happy with the other answers and I am using Flyway anyway I took a closer look at the Flyway code.由于我对其他答案不满意,并且无论如何我都在使用 Flyway,因此我仔细查看了 Flyway 代码。 DefaultSqlScriptExecutor is doing the actual execution, so I tried to figure out how to create an instance of DefaultSqlScriptExecutor . DefaultSqlScriptExecutor正在执行实际执行,所以我试图弄清楚如何创建DefaultSqlScriptExecutor的实例。

Basically the following snippet loads a String splits it into the single statements and executes one by one.基本上,以下代码段加载一个String将其拆分为单个语句并一一执行。 Flyway also provides other LoadableResource s than StringResource eg FileSystemResource . Flyway 还提供了StringResource之外的其他LoadableResource例如FileSystemResource But I have not taken a closer look at them.但我没有仔细观察它们。

As DefaultSqlScriptExecutor and the other classes are not officially documented by Flyway use the code-snippet with care.由于DefaultSqlScriptExecutor和其他类没有被 Flyway 正式记录,请小心使用代码片段。

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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM