You can use the embedded Java database server, such as java Derby, HSQL or others, with a volatile database in memory. The advantage is a real standard compatible with the engine.
In this case, the thin shell can be equipped with the Java Scripting API, so you can work with variables and functions.
Scripting will
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); } }
SQL implementation for the Java scripting API
Create a project for sqlscript.jar with a text file:
/META-INF/services/javax.script.ScriptEngineFactory
containing:
my.sqlscript.SQLScriptEngineFactory
A powerful factory class can be detected, for example, by the name of the language. It provides ScriptEngine for evaluation.
package my.sqlscript; public class SQLScriptEngineFactory implements ScriptEngineFactory { @Override public ScriptEngine getScriptEngine() { return new SQLScriptEngine(this); } }
The engine can do the job. Here I used HSQLDB, which has a SELECT problem without FROM, but the same thing can be done even better with JavaDB / Derby pr H2SQL. Not that affordable parameter binding would also need a little plumbing.
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(); } }
You can make the connection more persistent in the factory class and have a pseudo-SQL shutdown statement to close the connection explicitly.
Conclusion
This is a relatively simple layer of abstraction that can help with reuse. Besides providing your own classes.