Статьи

Простое управление базой данных с помощью Groovy и Gradle

Groovy: язык «Enterprise Hipster»

Не все считают язык программирования Java сексуальным. Однако виртуальная машина Java является доминирующей силой повсюду, от самых консервативных предприятий до самых причудливых стартапов. Сегодня существует множество альтернативных языков, которые компилируются в байт-код Java. Существуют версии Python , Ruby и несколько реализаций JavaScript на основе JVM. Есть совершенно новые языки, такие как Kotlin от JetBrains и Ceylon от RedHat. Clojure недавно вновь пробудил интерес к Lisp, и, конечно, Scala в значительной степени ответственен за переход 2000 года к функциональному программированию на стороне сервера.

Groovy , дедушка их всех, сегодня почти невидим в своей повсеместности. Когда он впервые появился 13 лет назад, Groovy стал хитом. Язык и связанная с ним веб-инфраструктура Grails объединили растущую популярность Ruby on Rails с чрезвычайно мелкой кривой обучения для разработчиков Java. Практически за одну ночь Groovy полностью заменил BeanShell , предыдущую альтернативу JVM-сценариев.

Энтузиазм по поводу модели Rails со временем ослаб, и строго типизированные языки снова стали тенденцией. Откровенно говоря, многие люди, которые стекались в Groovy просто потому, что он был «новым», продолжали переходить к более новым вещам. Тем не менее, Groovy не исчезла. Скорее, он превратился в зрелую роль языка «корпоративного хипстера». Вы найдете это везде. Практически любое приложение в JVM, которое предоставляет интерфейс сценариев, делает это с Groovy как первоклассным гражданином. Groovy чрезвычайно популярен в QA в области автоматизированного тестирования , глубоко внедрен в среду Spring и является основой для быстрорастущей системы сборки Gradle .

Мы не так сильно рекламируем Groovy, как раньше, но он абсолютно укоренился в экосистеме Java и продолжает расширяться. Это стабильная, безопасная ставка, для которой легко найти талант (или быстро обучить его на работе). Несмотря на то, что сегодня в вашем резюме есть более модные модные слова, кажется, небольшой риск того, что Groovy вспыхнет и уйдет в ближайшее время. Groovy «просто работает» и является действительно удобным инструментом, который должен иметь каждый Java-разработчик в своем наборе инструментов.

Gradle как сервер приложений Groovy

За исключением истории, давайте поговорим о недавнем сценарии использования, который привел меня в пыль от моих навыков Groovy. Мне нужно было быстро настроить реестр параметров конфигурации «ключ-значение» для ряда приложений, работающих в разных средах. Я хотел записать эти параметры в систему управления версиями как набор файлов свойств. Один файл для каждого приложения, вложенный в подкаталог для каждой среды:

  • QA-ENV /
    • application-a.properties
    • application-b.properties
  • постановка-ENV /
    • application-a.properties
    • application-b.properties

Всякий раз, когда изменение этих файлов свойств фиксируется в управлении исходным кодом, я хотел бы, чтобы Jenkins (или какой-либо другой сервер непрерывной интеграции) синхронизировал его значения с «реестром» времени выполнения. Этот реестр может в конечном итоге стать чем-то вроде etcd или Consul и Vault , но мы можем быстро начать работу с традиционной базой данных MySQL.

Поскольку большинство наших заданий на непрерывную интеграционную сборку в настоящее время основано на Gradle , а поскольку Gradle является родным для Groovy, мы можем превратить это «синхронизирующее» задание в сборку Gradle. С помощью задачи на основе JavaExec, указывающей на скрипт Groovy, вы можете использовать Gradle в качестве сервера приложений Groovy!

build.gradle

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
apply plugin: 'groovy'
 
repositories {
    mavenCentral()
    mavenLocal()
}
 
// [1] Declare a localGroovy() dependency, to use
//     the Groovy library that ships with Gradle.
dependencies {
    compile localGroovy()
    compile("mysql:mysql-connector-java:5.1.35")
    compile("com.h2database:h2:1.4.187")
    testCompile("junit:junit:4.12")
}
 
// [2] Create a task of type 'JavaExec', referencing
//     a Groovy script and any input arguments.
task runScript(type: JavaExec) {
    description 'Run a Groovy script to sync the environment config registry with the properties files in source control'
    classpath = sourceSets.main.runtimeClasspath
    main 'com.mypackage.SyncScript'
    args Arrays.asList('jdbc:mysql://registry/db', 'com.mysql.jdbc.Driver', 'user', 'password').toArray()
}
 
// [3] Tell Gradle to invoke your Groovy script task.
defaultTasks 'runScript'

Presto! Довольно просто написать скрипт сборки Gradle, который выполняет произвольный код Groovy. Поскольку в наши дни предпочтительным способом запуска Gradle является тонкий сценарий-обертка , вы можете доставить это решение прямо из репозитория с исходным кодом в любое место без установки Gradle.

Другими словами, вышесказанное — это все, что нужно, чтобы Jenkins запускал скрипт Groovy всякий раз, когда происходит фиксация его репозитория управления исходным кодом.

Groovy SQL

Теперь для действительно аккуратной части, самого скрипта Groovy «sync». Этот сценарий сканирует произвольное количество каталогов для среды, сканирует произвольное количество файлов свойств для приложения в каждом каталоге и синхронизирует эти свойства с таблицей базы данных MySQL.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
// Iterate through each per-environment directory
new File('config').eachDir { File environmentDirectory ->
 
    // Iterate through each per-application properties file
    environmentDirectory.eachFileMatch FileType.FILES, ~/.+\.properties/, { File applicationFile ->
 
        def environment = environmentDirectory.name
        def application = applicationFile.name.replace('.properties', '')
        println "Processing properties for env: '$environment', application: '$application'"
 
        // Parse the file into a java.util.Properties object
        def properties = new Properties()
        applicationFile.withInputStream { stream -> properties.load(stream) }
 
        ...
         
    }
}

Java 8 Streams сделали такие вещи более дружественными и удобочитаемыми в среде чистого Java, но они все еще не могут коснуться простоты расширений Groovy для таких классов, как File. Дополнительные методы eachDir () и eachFileMatch () позволяют легко перебирать все каталоги и сканировать файлы с расширением «.properties». Метод withInputStream () помогает нам загружать содержимое каждого файла в объект java.util.Properties с одной строкой.

Помимо расширений java.io.File, Groovy предлагает свой собственный класс groovy.sql.Sql для упрощения операций JDBC. Это уменьшает значительную часть шаблона, необходимого для построения запроса к базе данных, и позволяет нам обрабатывать его ResultSet в закрытии:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
database = groovy.sql.Sql.newInstance(jdbcUrl, jdbcUsername, jdbcPassword, jdbcDriver)
database.resultSetConcurrency = ResultSet.CONCUR_UPDATABLE
 
// Iterate through the properties, and sync MySQL
properties.entrySet().each {
    def name = it.key
    def value = it.value
    def existingRecordQuery = '''
SELECT environment, service, property_name, property_value FROM environment_properties
WHERE environment = ? AND service = ? AND property_name = ?
'''
    database.query(existingRecordQuery, [environment, service, name]) { ResultSet rs ->
        if (rs.next()) {
            def existingValue = rs.getString('property_value')
            if (existingValue.equals(value)) {
                // Existing property value is unchanged.  No-op.
            } else {
                // Existing property value has changed.  Update.
                rs.updateString('property_value', value)
                rs.updateRow()
            }
        } else {
            // New property.  Insert.
            rs.moveToInsertRow()
            rs.updateString('environment', environment)
            rs.updateString('service', service)
            rs.updateString('property_name', name)
            rs.updateString('property_value', value)
            rs.insertRow()
        }
    }
}
 
// TODO: Remove from the database properties that have
//       been removed from the properties file.

Здесь происходит несколько интересных вещей:

  • В строке 2 мы изменяем параметр параллелизма на ResultSet.CONCUR_UPDATABLE. Многие разработчики Java не знают, что Java даже поддерживает это !
  • Этот параметр позволяет обновлять, вставлять или удалять строки в объекте ResultSet без необходимости создания дополнительных операторов JDBC. Посмотрите примеры этого в строках 20 и 29. Многое в удобстве ORM с простотой необработанного JDBC!
  • Как видно из строк 8-11, Groovy допускает многострочные строковые литералы с тройными кавычками. Это делает намного более читабельным включение длинных строк SQL в ваш исходный код.
  • В строке 12 мы видим, что groovy.sql.Sql позволяет вам выполнить оператор и обработать его результат в закрытии. Одно удобство заключается в том, что базовый оператор JDBC в конце автоматически закрывается.

Вывод

Этот конкретный вариант использования очень специфичен, но он демонстрирует несколько концепций, которые широко полезны в отдельности. Groovy — очень мощный язык, который может быть полезен в среде, где нет других альтернатив. Он является родным для Gradle, который быстро становится наиболее доминирующим инструментом сборки экосистемы Java, поэтому Groovy легко использовать с помощью сервера непрерывной интеграции. Наконец, Groovy поставляется с полной библиотекой классов и расширениями базовых классов Java, которые действительно убирают большую часть стандартного набора и сложности общих задач.