Практически Groovy: Программирование JDBC с помощью Groovy (исходники)

Эндрю Гловер, президент компании, Stelligent Incorporated

Сделайте еще один шаг в освоении Groovy: Эндрю Гловер покажет, как использовать GroovySql для построения простого приложения для формирования отчетов. Объединяя в себе замыкания и итераторы, GroovySql облегчает программирование Java Database Connectivity (JDBC), перенося часть работы по управлению ресурсами на саму среду Groovy.

В предыдущих выпусках серии Практически Groovy мы открыли несколько очень интересных возможностей Groovy. Из первой статьи вы узнали, как применять Groovy для более простого и быстрого функционального тестирования обычного кода Java. Во второй статье вы увидели выразительные возможности, привносимые Groovy в сборки Ant. В этот раз вы узнаете еще об одном способе практического использования Groovy - а именно о том, как применить его для быстрого создания приложения, формирующего отчеты на базе SQL.

Обычно языки сценариев великолепно подходят для быстрого создания приложений, формирующих отчеты, но создание таких приложений с помощью Groovy явно выделяется даже на их фоне. Легкий синтаксис Groovy может частично компенсировать многословие JDBC в языке Java, но наиболее сильный эффект дают замыкания (closures), которые элегантно переносят ответственность за обработку с клиента на среду, где удержать вес значительно проще.

В статье этого месяца я дам краткий обзор возможностей GroovySql и покажу, как начать работу с ними, создав простое приложение для формирования отчетов. Чтобы вынести максимум пользы из обсуждения, вы должны быть знакомы с программированием JDBC на платформе Java. Кроме того, вы, вероятно, захотите освежить в памяти введение в замыкания в Groovy, опубликованное в прошлом месяце, поскольку здесь они играют важную роль. Однако наиболее важным понятием в этом месяце будет итерирование, поскольку итераторы играют важную роль в расширении JDBC с помощью Groovy. Поэтому я начну с обзора методов итераторов в Groovy.

Введение в итераторы

Итерирование является одной из наиболее часто используемых и полезных тактик в ситуациях программирования всех типов. Итератор - это программная конструкция, которая позволяет обеспечить быстрый доступ к элементам данных, хранящимся в любой коллекции или контейнере, по одному за раз. Groovy расширяет понятие итераторов языка Java, делая их неявными и простыми в использовании. В листинге 1 показано, сколько усилий требуется для вывода на печать всех элементов коллекции String с помощью языка Java.

Листинг 1. Итераторы в обычном коде Java

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class JavaIteratorExample {
  public static void main(String[] args) {
     Collection coll = new ArrayList();
     coll.add("JMS");
     coll.add("EJB");
     coll.add("JMX");
     for(Iterator iter = coll.iterator(); iter.hasNext();){
        System.out.println(iter.next());
     }
  }
}

Из листинга 2 видно, как Groovy упрощает эти действия. Здесь я пропускаю интерфейс Iterator и использую методы типа итератора непосредственно на коллекции. Более того, методы итераторов Groovy допускают использование замыканий, которые будут вызываться для каждого цикла итерации. В листинге 2 показан приведенный выше пример на языке Java, трансформированный с помощью Groovy.

Листинг 2. Итераторы в Groovy

class IteratorExample1{
   static void main(args) {
     coll = ["JMS", "EJB", "JMX"]
     coll.each{ item / println item }
   }
}

Как вы могли увидеть, в отличие от обычного кода Java, Groovy управляет кодом итерирования, при этом позволяя мне указывать нужный режим работы. Тем самым Groovy аккуратно переносит ответственность за обработку ресурсов с меня на себя. Передача обработки ресурсов Groovy открывает широчайшие возможности. Это делает процесс программирования более простым и, следовательно, более быстрым.

Введение в GroovySql

Возможности SQL в Groovy реализованы в изящном интерфейсе API GroovySql. С помощью замыканий и итераторов GroovySql аккуратно переносит управление ресурсами JDBC с вас, разработчика, в среду Groovy. Тем самым программирование JDBC избавляется от громоздкости, и вы можете сконцентрироваться на запросах и их результатах.

На всякий случай, если вы забыли, с какими трудностями может быть связано обычное программирование JDBC на Java, я буду счастлив напомнить вам об этом! В листинге 3 вы можете увидеть простой пример программирования JDBC на языке Java.

Листинг 3. Программирование JDBC на обычном Java

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCExample1 {
  public static void main(String[] args) {
    Connection con = null;
    Statement stmt = null;
    ResultSet rs = null;
    try{
      Class.forName("org.gjt.mm.mysql.Driver");
      con = DriverManager.getConnection("jdbc:mysql://localhost:3306/words",
           "words", "words");
      stmt = con.createStatement();
      rs = stmt.executeQuery("select * from word");
      while (rs.next()) {
        System.out.println("word id: " + rs.getLong(1) +
            " spelling: " + rs.getString(2) +
            " part of speech: " + rs.getString(3));
      }
    }catch(SQLException e){
      e.printStackTrace();
    }catch(ClassNotFoundException e){
      e.printStackTrace();
    }finally{
      try{rs.close();}catch(Exception e){}
      try{stmt.close();}catch(Exception e){}
      try{con.close();}catch(Exception e){}
   }
  }
}

Блеск. В листинге 3 содержится около 40 строк кода только для просмотра содержимого таблицы! Как вы думаете, сколько строк понадобится при использовании GroovySql? Если вы думаете, что их будет больше 10, вы ошибетесь. Посмотрите, как изящно Groovy позволяет мне сконцентрироваться на задаче - выполнении простого запроса - и управляет нужными ресурсами вместо меня, как видно в листинге 4.

Листинг 4. Добро пожаловать в GroovySql!

import groovy.sql.Sql
class GroovySqlExample1{
  static void main(args) {
    sql = Sql.newInstance("jdbc:mysql://localhost:3306/words", "words",
           "words", "org.gjt.mm.mysql.Driver")
    sql.eachRow("select * from word"){ row /
       println row.word_id + " " + row.spelling + " " + row.part_of_speech
    }
  }
}

Неплохо. Используя всего несколько строк, я создал код такой же функции, как и в листинге 3, не выполняя закрытия Connection, закрытия ResultSet или других привычных тяжеловесных процедур программирования JDBC. Если вы спросите моего мнения, я скажу, что это отличная вещь и достаточно простая. Теперь позвольте мне показать вам, как именно я сделал это.

Выполнение простого запроса

В первой строчке листинга 4 я создал экземпляр класса Sql Groovy, который используется для соединения с нужной базой данных. В этом случае я создал экземпляр Sql, указав на базу данных MySQL, работающую на моей машине. Пока все достаточно просто, не так ли? Но реальный нокаут нас ждет в следующей части, где итераторы и замыкания за один-два удара покажут всю свою мощь.

Можно рассматривать метод eachRow как итератор по результату переданного запроса. На более низком уровне можно представить себе возвращаемый объект JDBC ResultSet и его содержимое, передаваемое в цикл for. Таким образом, для каждой итерации выполняется переданное мной замыкание. Если в таблице word базы данных всего три строки, замыкание выполняется три раза, выводя значения word_id, spelling и part_of_speech.

Код упрощается еще больше путем отбрасывания из уравнения именованной переменной row и использования одной из скрытых переменных Groovy: it, которая становится экземпляром итератора. Если я сделаю это, указанный выше код будет записан так, как это показано в листинге 5.

Листинг 5. Переменная it Groovy в GroovySql

import groovy.sql.Sql
class GroovySqlExample1{
  static void main(args) {
    sql = Sql.newInstance("jdbc:mysql://localhost:3306/words", "words",
           "words", "org.gjt.mm.mysql.Driver")
    sql.eachRow("select * from word"){ println it.spelling +  " ${it.part_of_speech}"}
  }
}

В этом коде я смог отбросить переменную row и использовать вместо нее it. Кроме того, я могу ссылаться на переменную it в операторе String как это было с ${it.part_of_speech}.

Выполнение более сложных запросов

Предыдущие примеры были достаточно просты, но GroovySql так же хорошо работает при более сложных запросах, манипулирующих данными, например, insert, update и delete. Для них вам также не обязательно использовать итераторы, вместо них объект Sql Groovy предоставляет методы execute и executeUpdate. Эти методы напоминают обычный класс statement JDBC, у которого также есть методы execute и executeUpdate.

В листинге 6 вы можете видеть простой запрос insert, который использует подмену переменных с помощью синтаксиса ${}. Этот код просто вставляет новую строку в таблицу word.

Листинг 6. Вставка с помощью GroovySql

 wid = 999
 spelling = "Nefarious"
 pospeech = "Adjective"
 sql.execute("insert into word (word_id, spelling, part_of_speech) 
   values (${wid}, ${spelling}, ${pospeech})")

В Groovy также реализована измененная версия метода execute, который создает список значений, соответствующих любым элементам ? в запросе. В листинге 7 я просто запрашиваю определенную строку таблицы word. За кулисами GroovySql создает экземпляр обычного языка Java java.sql.PreparedStatement.

Листинг 7. PreparedStatements с помощью GroovySql

val = sql.execute("select * from word where word_id = ?", [5])

Изменения (update) проходят в основном таким же образом, используя метод executeUpdate. Кроме того, обратите внимание, что в листинге 8 метод executeUpdate принимает список значений, которые будут сопоставлены соответствующим элементам ? запроса.

Листинг 8. Изменения с помощью GroovySql

 nid = 5
 spelling = "Nefarious"
 sql.executeUpdate("update word set word_id = ? where spelling = ?", [nid, spelling])

Удаление, по существу, выполняется так же, как и вставка, за тем лишь исключением, конечно, другого синтаксиса запроса, как видно из листинга 9.

Листинг 9. Удаление с помощью GroovySql

 sql.execute("delete from word where word_id = ?" , [5])

Упрощение работы с данными

В любом интерфейсе API или утилите, цель которой состоит в упрощении программирования JDBC, хорошо бы иметь набор мощных функций для работы с данными, и в этом разделе я покажу вам еще три такие функции.

Массивы данных

При всей своей простоте GroovySql поддерживает нотацию типа DataSet, которая фактически является представлением таблиц базы данных в виде объектов. С помощью DataSet вы можете проводить итерирование по строкам и добавлять новые строки. В действительности использование массивов данных является удобным способом представления коллекций данных в виде, характерном для таблиц.

Однако у типа DataSet в GroovySql есть один недостаток - он не отражает отношений; он является простым отображением "один к одному" на таблицу базы данных. В листинге 10 я создаю DataSet из таблицы word.

Листинг 10. Массивы данных в GroovySql

import groovy.sql.Sql
class GroovyDatasetsExample1{
  static void main(args) {
    sql = Sql.newInstance("jdbc:mysql://localhost:3306/words", "words",
          "words", "org.gjt.mm.mysql.Driver")
    words = sql.dataSet("word")
    words.each{ word /
     println word.word_id + " " + word.spelling
    }
    words.add(word_id:"9999", spelling:"clerisy", part_of_speech:"Noun")
  }
}

Как вы можете видеть, тип DataSet GroovySql облегчает итерирование по элементам таблицы с помощью метода each и добавление новых строк с помощью метода add, который использует map, представляющую нужные данные.

Использование хранимых процедур и отрицательной индексации

Хранимые процедуры и отрицательная индексация могут быть важными аспектами работы с данными. GroovySql упрощает вызов хранимых процедур до простого вызова метода call класса Sql. Для отрицательной индексации GroovySql предоставляет расширенный тип ResultSet, который работает как коллекции в Groovy. Например, если вы желаете получить последний элемент результирующего множества, вы можете сделать так, как показано в листинге 11.

Листинг 11. Отрицательная индексация в GroovySql

 sql.eachRow("select * from word"){ grs /
   println "-1  = " + grs.getAt(-1) //prints spelling
   println "2  = " + grs.getAt(2) //prints spelling
 }

Как видно из листинга 11, получение последнего элемента результирующего множества выполняется простым указанием индекса -1. При желании я также мог обратиться бы к этому элементу по индексу 2.

Это опять простейшие примеры, но они должны помочь вам лучше понять возможности GroovySql. Я закончу урок этого месяца реальным примером, демонстрирующим все описанные ранее возможности.

Написание простого приложения для формирования отчетов

Обычно приложения, формирующие отчеты, извлекают информацию из базы данных. В обычной бизнес-среде вам может потребоваться написать приложение, формирующее отчеты о текущих продажах через Интернет для команды менеджеров по продажам или отчеты с ежедневными контрольными показателями по производительности отдельных компонентов системы, например, базы данных, для разработчиков.

Для нужд нашего простого примера давайте предположим, что вы только что внедрили корпоративное Web-приложение. Конечно же, оно работает безупречно, потому что во время работы над ним вы написали множество функциональных тестов (на Groovy); однако вам нужно создать отчет о состоянии базы данных с целью её настройки. Вы хотите знать, как пользователи работают с приложением, чтобы предупреждать проблемы с производительностью и устранять их.

Обычно количество «примочек», которые вы можете добавить в подобное приложение, ограничивается имеющимся временем. Но новые знания о GroovySql помогут вам создать нужное приложение за несколько минут, оставив вам время на дополнительные функции, если вы этого пожелаете.

Подробности

Целевой базой данных в нашем случае является MySQL, которая удачным образом поддерживает получение информации о состоянии с помощью запроса. Вас интересует следующая информация о состоянии:

  • Время безотказной работы
  • Общее количество обработанных запросов
  • Процентные доли определенных видов запросов, таких как insert, update и select

Получение этой информации из базы данных MySQL с помощью GroovySql становится крайне простым. Поскольку приложение готовится для команды разработчиков, вы, вероятно, начнете с простого отчета, запускающегося из командной строки, но на следующем шаге вы можете с легкостью преобразовать его в отчет для Web. Сценарий использования для нашего примера отчетности может выглядеть следующим образом:

1. Подключаемся к работающей базе данных нашего приложения
2. Подаем запросы show status и регистрируем:
a. Время безотказной работы
b. Общее количество запросов
c. Общее количество insert 
d. Общее количество update 
e. Общее количество select 
3. Используя эти данные, рассчитываем:
a. количество запросов в минуту
b. процентную долю числа запросов insert 
c. процентную долю числа запросов update 
d. процентную долю числа запросов select 

В листинге 12 представлен конечный результат: приложение, которое создает отчет с нужной статистикой базы данных. Первые строки кода устанавливают соединение с рабочей базой данных, вслед за чем идет ряд запросов show status, которые позволяют рассчитать показатели количества запросов в минуту и разбить их по типам. Обратите внимание, как переменные типа uptime возникают в момент их определения.

Листинг 12. Отчет о состоянии базы данных с помощью GroovySql

import groovy.sql.Sql
class DBStatusReport{
  static void main(args) {
     sql = Sql.newInstance("jdbc:mysql://yourserver.anywhere/tiger", "scott",
        "tiger", "org.gjt.mm.mysql.Driver")
     sql.eachRow("show status"){ status /
        if(status.variable_name == "Uptime"){
           uptime =  status[1]
        }else if (status.variable_name == "Questions"){
           questions =  status[1]
        }
     }
     println "Uptime for Database: " + uptime
     println "Number of Queries: " + questions
     println "Queries per Minute =
      " + Integer.valueOf(questions) / Integer.valueOf(uptime)
     sql.eachRow("show status like 'Com_%'"){ status /
        if(status.variable_name == "Com_insert"){
           insertnum =  Integer.valueOf(status[1])
        }else if (status.variable_name == "Com_select"){
           selectnum =  Integer.valueOf(status[1])
        }else if (status.variable_name == "Com_update"){
           updatenum =  Integer.valueOf(status[1])
       }
    }
    println "% Queries Inserts = " + 100 * (insertnum / Integer.valueOf(uptime))
    println "% Queries Selects = " + 100 * (selectnum / Integer.valueOf(uptime))
    println "% Queries Updates = " + 100 * (updatenum / Integer.valueOf(uptime))
    }
}

Повторение сегодняшнего урока

В выпуске Практически Groovy за этот месяц вы могли видеть, как GroovySql позволяет упростить программирование JDBC. Этот прекрасный API-интерфейс сочетает замыкания и итераторы с мягким синтаксисом Groovy, позволяя облегчить быструю разработку приложений баз данных на платформе Java. Более того, GroovySql перекладывает работу по управлению ресурсами с разработчика на среду Groovy, позволяя вам сконцентрироваться на более важных проблемах запросов и их результатов. Однако я не требую верить мне на слово. В следующий раз, когда вам придется погружаться в каторгу JDBC, попробуйте вместо этого использовать немного волшебства GroovySql. После этого отправьте мне письмо по электронной почте и расскажите о вашем опыте.

В следующем месяце в выпуске Практически Groovy, я подробно расскажу о среде шаблонов Groovy. Как вы увидите, создавать визуальные компоненты приложений с помощью этой умной среды очень просто.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=8667