在我们的项目中,我们需要在没有任何数据库服务器的情况下评估 SQL 语句。您能否推荐任何能够评估基于数学的 SQL 语句并返回结果的免费 Java 库?
例如;
输入
SELECT 2*2 AS RESULT
输出
4
可能会被称为int result = SQLEvaluator.evaluate("SELECT 2*2 AS RESULT");
可以使用ZQL来实现这一点,如下面的代码所示。但我强烈建议您选择一个简单的嵌入式数据库,例如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
:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.171</version>
</dependency>
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
(测试目的,因为我们不知道谁维护该存储库):
<dependencies>
<dependency>
<groupId>com.experlog</groupId>
<artifactId>zql</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>zql</id>
<name>zql</name>
<url>http://dbappserv.cis.upenn.edu:8080/artifactory/ext-releases-local</url>
</repository>
</repositories>
您可以使用嵌入式仅 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();
}
}
您可以在工厂类中使连接更加持久,并使用伪 SQL 语句shutdown
显式关闭连接。
结论
它是一个相对简单的抽象层,有助于重用。除了提供自己的课程。