4

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

例如;

输入

SELECT 2*2 AS RESULT

输出

4

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

4

2 回答 2

2

可以使用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

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.3.171</version>
</dependency>

使用 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(测试目的,因为我们不知道谁维护该存储库):

<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>
于 2013-05-07T16:59:47.367 回答
1

您可以使用嵌入式仅 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显式关闭连接。

结论

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

于 2013-05-07T16:57:27.837 回答