用Java评估SQL表达式?

时间:2013-05-07 16:13:17

标签: java sql

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

例如;

输入

SELECT 2*2 AS RESULT

输出

4

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

2 个答案:

答案 0 :(得分: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来显式关闭连接。

结论

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

答案 1 :(得分:1)

可以使用ZQL来实现,如下面的代码所示。但我认真建议您选择一个简单的嵌入式数据库,例如H2example here)并使用它(项目运行状况要高得多)。

使用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 Page下载,或者出于测试目的,将其添加到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>