【发布时间】:2011-01-05 12:19:46
【问题描述】:
我想在 Java 中执行一个 SQL 脚本文件,而不是将整个文件内容读入一个大查询并执行它。
还有其他标准方式吗?
【问题讨论】:
标签: java sql-scripts
我想在 Java 中执行一个 SQL 脚本文件,而不是将整个文件内容读入一个大查询并执行它。
还有其他标准方式吗?
【问题讨论】:
标签: java sql-scripts
不,您必须读取文件,将其拆分为单独的查询,然后单独执行它们(或使用 JDBC 的批处理 API)。
其中一个原因是每个数据库都定义了自己的分隔 SQL 语句的方式(有些使用 ;,有些使用 /,有些允许两者甚至定义您自己的分隔符)。
【讨论】:
JDBC 不支持此选项(尽管特定的数据库驱动程序可能会提供此选项)。 无论如何,将所有文件内容加载到内存中应该没有问题。
【讨论】:
没有可移植的方式来做到这一点。不过,您可以将本机客户端作为外部程序执行:
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();
}
}
}
【讨论】:
您不能使用 JDBC,因为它不支持 .解决方法是包括 iBatis iBATIS 是一个持久性框架并调用 Scriptrunner 构造函数,如 iBatis 文档中所示。
包含像 ibatis 这样重量级的持久性框架来运行简单的 sql 脚本是不好的,你可以使用命令行执行任何方式
$ mysql -u root -p db_name < test.sql
【讨论】:
只要您不介意对 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();
}
【讨论】:
insert、create table。但是对于具有create or replace trigger 的脚本,它会因 java.sql.SQLSyntaxErrorException: ORA-00900: invalid SQL statement 而失败
试试这个代码:
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 语句。该问题意味着涉及大量查询或插入,因此需要通过查找分隔符将它们拆分为单独的语句。
由于 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());
}
}
【讨论】:
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”),':' 后面没有空格,这让我发现了。
【讨论】:
我发现最简单的可移植外部工具是 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,它完美无缺。
如果你使用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;
}
用于在初始化期间建立数据库并清理一个 销毁期间的数据库。
【讨论】:
DatabasePopulatorUtils.execute(resourceDatabasePopulator, dataSource)在代码中的任意位置执行脚本。
Apache iBatis 解决方案非常有效。
我使用的脚本示例正是我从 MySql 工作台运行的脚本。
这就是我所做的:
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.");
}
}
【讨论】:
对于我的简单项目,用户应该能够选择执行的 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);
}
【讨论】: