使用Groovy和Gradle輕鬆進行數據庫操做

Groovy:「時髦」語言

並不是全部人都認爲Java 編程語言很性感。可是,從最保守的企業到最古怪的初創企業,Java 虛擬機都是無處不在的主導力量。現在,有許多可替代的語言可編譯爲Java字節碼。有基於JVM的Python,Ruby版本和JavaScript的多種實現。有全新的語言,例如 JetBrains的Kotlin和RedHat的Ceylon。Clojure最近從新喚起了對Lisp和Scala的興趣很大程度上是2000年服務器端向功能編程的轉變的緣由。java

Groovy是全部人的祖父,今天幾乎無處不在。當它在13年前首次出現時,Groovy馬上受到歡迎。該語言和相關的Grails Web框架將Ruby on Rails的新興流行與Java開發人員的極淺學習曲線結合在一塊兒。在幾乎一晚上之間,Groovy徹底取代了之前的JVM腳本替代品BeanShell。mysql

對Rails模型的熱情最終減弱了,強類型的語言再次成爲趨勢。坦率地說,許多僅僅由於它是「新的」而蜂擁至Groovy的人仍在繼續開發新事物。可是,Groovy並無消失。相反,它已經成爲「企業時髦」語言的成熟角色。隨處可見。JVM上幾乎全部公開腳本接口的應用程序都以Groovy爲頭等公民而這樣作。Groovy是與QA很是流行的自動化測試空間,被深深植入到Spring框架,而且是快速增加的基礎搖籃構建系統。sql

咱們沒有像之前那樣大肆宣傳Groovy,可是它在Java生態系統中已經根深蒂固,而且還在不斷擴展。這是一個穩定,安全的選擇,爲此,很容易找到人才(或快速在職培訓)。儘管今天有更多時髦的流行語要放在您的簡歷上,可是Groovy很快就消失out盡的風險彷佛很小。Groovy「行之有效」,是每一個Java開發人員都應該在其工具箱中使用的很是方便的工具。數據庫

Gradle做爲Groovy App Server

除了歷史,讓咱們談論一個最近的用例,它使我沒法使用Groovy技能。我須要爲在多種環境中運行的許多應用程序快速創建一個「鍵值」配置參數註冊表。我想在源代碼管理中將這些參數捕獲爲屬性文件的集合。每一個應用程序一個文件,嵌套在每一個環境的子目錄中:編程

…
    qa-env/
        application-a.properties
        application-b.properties
…
    staging-env/
        application-a.properties
        application-b.properties
…

每當在源代碼管理中提交對這些屬性文件的更改時,我都但願Jenkins(或其餘連續集成服務器)將其值與運行時「註冊表」同步。該註冊表最終可能會變成etcd或Consul和Vault之類的東西,可是咱們可使用傳統的MySQL數據庫快速開始工做。安全

因爲這些天咱們的大多數持續集成構建做業都是基於Gradle的,而且因爲Gradle是Groovy本機的,所以咱們能夠將這種「同步」做業烘焙到Gradle構建中。經過基於JavaExec的任務(指向Groovy腳本),您能夠將Gradle用做Groovy應用服務器!服務器

  • build.gradle
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'

編寫執行某些任意Groovy代碼的Gradle構建腳本至關簡單。因爲現在運行Gradle的首選方法是經過精簡包裝器腳本,所以無需安裝Gradle,就能夠直接從源代碼控制存儲庫將此解決方案傳遞到任何地方。閉包

換句話說,只要提交了源代碼控制存儲庫,就可使 Jenkins運行Groovy腳本。併發

Groovy SQL

如今,對於真正整潔的部分,Groovy「同步」腳本自己。該腳本掃描任意數量的每一個環境目錄,掃描每一個目錄中的任意數量的每一個應用程序屬性文件,並將這些屬性與MySQL數據庫表同步。app

// 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:

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類的擴展,這些真正地消除了許多常見任務的樣板和複雜性。

技術類文章精選

非技術文章精選

相關文章
相關標籤/搜索