如何使用Hibernate执行大量原生SQL?多语句SQL执行报错解决
我正在开发一款Web应用,允许用户通过ER图生成数据库并执行基础CRUD操作。目前已经实现了生成可用SQL代码的功能,但现在需要用Java执行这些SQL。我的技术栈是Java 1.8、Oracle 11gR2、Hibernate 5.4.9.Final。
以下是我从文件加载SQL并执行原生查询的代码:
Session session = hibernateFactory.openSession(); String query = null; try { query = new String(Files.readAllBytes(Paths.get(ClassLoader.getSystemResource("test.sql").toURI()))); } catch (IOException | URISyntaxException e) { e.printStackTrace(); } String finalQuery = query; session.doWork(connection -> connection.prepareStatement(finalQuery).execute());
遇到的问题:单表的SQL可以正常执行,但添加另一表的SQL后就会报错,看起来是Hibernate不支持SQL中的分号“;”。请问有没有办法修复并强制执行完整的SQL脚本,还是必须拆分语句逐个执行?
你遇到的这个问题本质上不是Hibernate的限制,而是底层JDBC的约束——JDBC的PreparedStatement默认不支持一次性执行多条用分号分隔的SQL语句,Oracle 11g的驱动在这方面限制更严格,所以直接传入带分号的完整脚本就会报错。
下面给你两种可行的解决思路:
1. 手动拆分SQL语句执行(最稳妥,无需额外依赖)
你可以把完整的SQL脚本按分号分割,过滤掉空行和注释后逐个执行。这里要注意一个细节:如果SQL语句中包含字符串里的分号(比如INSERT INTO users (name) VALUES ('O''Neil;')),直接用;分割会出错,所以需要用正则表达式来跳过字符串内的分号。
修改后的代码示例:
Session session = hibernateFactory.openSession(); String query = null; try { query = new String(Files.readAllBytes(Paths.get(ClassLoader.getSystemResource("test.sql").toURI()))); } catch (IOException | URISyntaxException e) { e.printStackTrace(); } if (query != null) { // 正则表达式:只分割不在字符串内的分号 String[] statements = query.split(";(?=(?:[^']*'[^']*')*[^']*$)"); session.doWork(connection -> { try (Statement stmt = connection.createStatement()) { for (String stmtStr : statements) { String trimmedStmt = stmtStr.trim(); // 跳过空行和单行/多行注释 if (!trimmedStmt.isEmpty() && !trimmedStmt.startsWith("--") && !trimmedStmt.startsWith("/*")) { stmt.execute(trimmedStmt); } } } catch (SQLException e) { e.printStackTrace(); // 这里可以根据业务需求添加异常处理逻辑,比如回滚事务 } }); }
2. 使用第三方工具类执行完整脚本(更省心)
如果不想自己处理拆分逻辑,可以用Apache Commons DbUtils库中的ScriptRunner类,它专门用来执行SQL脚本,能自动处理分号、注释、空行等情况,非常适合你的场景。
步骤1:添加依赖
如果用Maven,在pom.xml中加入:
<dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.7</version> </dependency>
步骤2:修改执行代码
Session session = hibernateFactory.openSession(); try { String sqlContent = new String(Files.readAllBytes(Paths.get(ClassLoader.getSystemResource("test.sql").toURI()))); session.doWork(connection -> { ScriptRunner scriptRunner = new ScriptRunner(connection); // 设置为false表示按分号分割语句执行,true则把整个脚本当作单条语句 scriptRunner.setSendFullScript(false); // 可选:设置是否打印执行日志 scriptRunner.setLogWriter(new PrintWriter(System.out)); // 执行脚本 scriptRunner.runScript(new StringReader(sqlContent)); }); } catch (IOException | URISyntaxException | SQLException e) { e.printStackTrace(); }
总结
对于Oracle 11g这类数据库,**拆分语句执行或者用ScriptRunner**是最可靠的方案。不建议尝试开启驱动的多语句支持(比如MySQL的allowMultiQueries=true),因为Oracle驱动本身对这个特性支持有限,而且跨数据库兼容性差。
内容的提问来源于stack exchange,提问作者Brarord

