用Java评估SQL表达式?

在我们的项目中,我们需要评估没有任何数据库服务器的SQL语句。 您能否建议任何能够评估基于数学的SQL语句并返回结果的免费Java库?

例如;

输入

SELECT 2*2 AS RESULT

产量

4

可能会被称为int result = SQLEvaluator.evaluate("SELECT 2*2 AS RESULT");

使用ZQL可以实现这一点,如下面的代码所示。 但我认真建议你选择一个简单的嵌入式数据库,如H2 ( 这里的例子 ),然后使用它(项目运行状况要高得多)。

使用H2:

 public class H2ExpEval { public static void main(String... args) throws Exception { evaluateUsingH2("SELECT 2+2"); evaluateUsingH2("SELECT 3+7-5"); evaluateUsingH2("SELECT 2*2*2+1"); } private static void evaluateUsingH2(String sql) throws Exception { Class.forName("org.h2.Driver"); // opens an in-memory database: no files are saved and it's all quicker Connection conn = DriverManager.getConnection("jdbc:h2:mem:"); Statement stat = conn.createStatement(); ResultSet rs = stat.executeQuery(sql); if (rs.next()) { System.out.println(rs.getString(1)); } stat.close(); conn.close(); } } 

输出:

 4 5 9 

要使用它,请将其添加到您的pom.xml

  com.h2database h2 1.3.171  

使用ZQL:

 public class ZqlEvalDemo { public static void main(String args[]) throws Exception { System.out.println(evaluate("SELECT 2+2 FROM RESULT;")); System.out.println(evaluate("SELECT 3+7-5 FROM RESULT;")); System.out.println(evaluate("SELECT 2*2*2+1 FROM RESULT;")); } private static ZqlParser p = new ZqlParser(); private static Object evaluate(String s) throws Exception { p.initParser(new java.io.ByteArrayInputStream(s.getBytes())); ZStatement st = p.readStatement(); ZSelectItem zSelectItem = ((ZQuery) st).getSelect().get(0); ZExpression exp = (ZExpression) zSelectItem.getExpression(); return new ZEval().evalExpValue(new ZTuple(), exp); } } 

输出:

 4.0 5.0 9.0 

对于依赖项,可以从ZQL页面下载,或者为了测试目的,将其添加到您的pom.xml (测试目的,因为我们不知道谁维护该存储库):

   com.experlog zql 1.0     zql zql http://dbappserv.cis.upenn.edu:8080/artifactory/ext-releases-local   

您可以使用嵌入式Java专用数据库服务器,如Java Derby,HSQL或其他具有非持久性内存数据库的服务器。 优点是存在符合标准的真实引擎。

在那里,可以使用Java Scripting API提供瘦包装器,因此您也可以使用变量和函数。


使用脚本将是

 public static void main(String[] args) { ScriptEngineManager manager = new ScriptEngineManager(); for (ScriptEngineFactory factory : manager.getEngineFactories()) { System.out.printf("language: %s, engine: %s%n", factory.getLanguageName(), factory.getEngineName()); } ScriptEngine engine = manager.getEngineByName("SQL"); try { Object result = engine.eval("SELECT 1+2;"); } catch (ScriptException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); } } 

为Java Scripting API实现SQL

使用文本文件为sqlscript.jar创建项目:

 /META-INF/services/javax.script.ScriptEngineFactory 

含:

 my.sqlscript.SQLScriptEngineFactory 

例如,可以通过语言名称来发现引擎工厂类。 它提供了一个ScriptEngine用于评估。

 package my.sqlscript; public class SQLScriptEngineFactory implements ScriptEngineFactory { @Override public ScriptEngine getScriptEngine() { return new SQLScriptEngine(this); } } 

引擎可以完成工作。 在这里我使用了HSQLDB,它在没有FROM的情况下遇到了SELECT问题,但使用JavaDB / Derby pr H2SQL可以做得更好。 并不是说可用的参数绑定也需要一点管道。

 public class SQLScriptEngine extends AbstractScriptEngine { private final SQLScriptEngineFactory factory; public SQLScriptEngine(SQLScriptEngineFactory factory) { this.factory = factory; } @Override public Object eval(String script, ScriptContext context) throws ScriptException { StringBuilder sb = new StringBuilder(); // Multi-column/multi-row result Object singleValue = null; // Single value result Server hsqlServer = new Server(); try { File dbFile = File.createTempFile("sqlscript", ".db"); String dbURL = dbFile.toURI().toURL().toString(); hsqlServer.setLogWriter(null); hsqlServer.setSilent(true); hsqlServer.setDatabaseName(0, "db1"); hsqlServer.setDatabasePath(0, dbURL); } catch (IOException | MalformedURLException ex) { throw new ScriptException(ex); } hsqlServer.start(); try { Class.forName("org.hsqldb.jdbcDriver"); Connection connection = DriverManager.getConnection("jdbc:hsqldb:hsql://localhost/db1", "sa", ""); try (PreparedStatement statement = connection.prepareStatement(script); ResultSet rs = statement.executeQuery();) { ResultSetMetaData meta = rs.getMetaData(); int columns = meta.getColumnCount(); int row = 1; while (rs.next()) { for (int column = 1; column <= columns; ++column) { Object value = rs.getObject(column); singleValue = row == 1 && column == 1? value : null; sb.append(value); if (column < columns) { sb.append("\t"); } } sb.append("\n"); ++row; } } } catch (SQLException | ClassNotFoundException e2) { Logger.getLogger(SQLScriptEngine.class.getName()).log(Level.SEVERE, null, e2); } hsqlServer.stop(); return singleValue != null ? singleValue : sb.toString(); } } 

您可以在工厂类中使连接更持久,并且shutdown伪SQL语句以显式关闭连接。

结论

它是一个相对简单的抽象层,可以帮助重用。 除了提供自己的课程。