На этой неделе мы рассмотрим некоторые удивительные сценарии SQL на стороне сервера с Nashorn и Java 8. В Интернете можно найти лишь несколько вещей, касающихся использования JDBC в Nashorn. Но зачем использовать JDBC и заботиться о болезненном управлении ресурсами и составлении строк SQL, когда вы можете использовать jOOQ ? Все работает из коробки!
Давайте настроим небольшой пример файла JavaScript так:
var someDatabaseFun = function() { var Properties = Java.type("java.util.Properties"); var Driver = Java.type("org.h2.Driver"); var driver = new Driver(); var properties = new Properties(); properties.setProperty("user", "sa"); properties.setProperty("password", ""); try { var conn = driver.connect("jdbc:h2:~/test", properties); // Database code here } finally { try { if (conn) conn.close(); } catch (e) {} } } someDatabaseFun();
Это почти все, что вам нужно для взаимодействия с JDBC и базой данных H2. Таким образом, мы могли бы выполнять операторы SQL с JDBC следующим образом:
try { var stmt = conn.prepareStatement( "select table_schema, table_name " + "from information_schema.tables"); var rs = stmt.executeQuery(); while (rs.next()) { print(rs.getString("TABLE_SCHEMA") + "." + rs.getString("TABLE_NAME")) } } finally { if (rs) try { rs.close(); } catch(e) {} if (stmt) try { stmt.close(); } catch(e) {} }
Большая часть раздувания связана с обработкой ресурсов JDBC, поскольку, к сожалению, у нас нет оператора try-with-resources в JavaScript. Выше генерируется следующий вывод:
INFORMATION_SCHEMA.FUNCTION_COLUMNS INFORMATION_SCHEMA.CONSTANTS INFORMATION_SCHEMA.SEQUENCES INFORMATION_SCHEMA.RIGHTS INFORMATION_SCHEMA.TRIGGERS INFORMATION_SCHEMA.CATALOGS INFORMATION_SCHEMA.CROSS_REFERENCES INFORMATION_SCHEMA.SETTINGS INFORMATION_SCHEMA.FUNCTION_ALIASES INFORMATION_SCHEMA.VIEWS INFORMATION_SCHEMA.TYPE_INFO INFORMATION_SCHEMA.CONSTRAINTS ...
Давайте посмотрим, сможем ли мы выполнить тот же запрос, используя jOOQ :
var DSL = Java.type("org.jooq.impl.DSL"); print( DSL.using(conn) .fetch("select table_schema, table_name " + "from information_schema.tables") );
Вот как вы можете выполнять простые операторы SQL в jOOQ, с гораздо меньшим раздуванием, чем в JDBC. Вывод примерно такой же:
+------------------+--------------------+ |TABLE_SCHEMA |TABLE_NAME | +------------------+--------------------+ |INFORMATION_SCHEMA|FUNCTION_COLUMNS | |INFORMATION_SCHEMA|CONSTANTS | |INFORMATION_SCHEMA|SEQUENCES | |INFORMATION_SCHEMA|RIGHTS | |INFORMATION_SCHEMA|TRIGGERS | |INFORMATION_SCHEMA|CATALOGS | |INFORMATION_SCHEMA|CROSS_REFERENCES | |INFORMATION_SCHEMA|SETTINGS | |INFORMATION_SCHEMA|FUNCTION_ALIASES | ...
Но сила jOOQ не в его простых возможностях SQL, а в API DSL, который абстрагирует все тонкости SQL, специфичные для поставщика, и позволяет быстро составлять запросы (а также DML). Рассмотрим следующий оператор SQL:
// Let's assume these objects were generated // by the jOOQ source code generator var Tables = Java.type("org.jooq.db.h2.information_schema.Tables"); var t = Tables.TABLES; var c = Tables.COLUMNS; // This is the equivalent of Java's static imports var count = DSL.count; var row = DSL.row; // We can now execute the following query: print( DSL.using(conn) .select( t.TABLE_SCHEMA, t.TABLE_NAME, c.COLUMN_NAME) .from(t) .join(c) .on(row(t.TABLE_SCHEMA, t.TABLE_NAME) .eq(c.TABLE_SCHEMA, c.TABLE_NAME)) .orderBy( t.TABLE_SCHEMA.asc(), t.TABLE_NAME.asc(), c.ORDINAL_POSITION.asc()) .fetch() );
Обратите внимание, что в приведенном выше запросе очевидно нет безопасности типов, так как это JavaScript. Но я думаю, что создатели IntelliJ, Eclipse или NetBeans в конечном итоге обнаружат зависимости Nashorn от Java-программ и обеспечат автоматическое завершение и выделение синтаксиса, поскольку некоторые вещи можно анализировать статически.
Все становится еще лучше, если вы используете Java 8 Streams API от Nashorn. Давайте рассмотрим следующий запрос:
DSL.using(conn) .select( t.TABLE_SCHEMA, t.TABLE_NAME, count().as("CNT")) .from(t) .join(c) .on(row(t.TABLE_SCHEMA, t.TABLE_NAME) .eq(c.TABLE_SCHEMA, c.TABLE_NAME)) .groupBy(t.TABLE_SCHEMA, t.TABLE_NAME) .orderBy( t.TABLE_SCHEMA.asc(), t.TABLE_NAME.asc()) // This fetches a List<Map<String, Object>> as // your ResultSet representation .fetchMaps() // This is Java 8's standard Collection.stream() .stream() // And now, r is like any other JavaScript object // or record! .forEach(function (r) { print(r.TABLE_SCHEMA + '.' + r.TABLE_NAME + ' has ' + r.CNT + ' columns.'); });
Выше генерирует этот вывод:
INFORMATION_SCHEMA.CATALOGS has 1 columns. INFORMATION_SCHEMA.COLLATIONS has 2 columns. INFORMATION_SCHEMA.COLUMNS has 23 columns. INFORMATION_SCHEMA.COLUMN_PRIVILEGES has 8 columns. INFORMATION_SCHEMA.CONSTANTS has 7 columns. INFORMATION_SCHEMA.CONSTRAINTS has 13 columns. INFORMATION_SCHEMA.CROSS_REFERENCES has 14 columns. INFORMATION_SCHEMA.DOMAINS has 14 columns. ...
Если ваша база данных поддерживает массивы, вы даже можете получить доступ к таким столбцам массива по индексу, например,
r.COLUMN_NAME[3]
Так что, если вы являетесь поклонником JavaScript на стороне сервера, скачайте jOOQ сегодня и начните писать потрясающий SQL на JavaScript сейчас! Чтобы узнать больше о Nashorn, прочитайте эту статью здесь .