01 lipca 2009

Z rozdziału 10. o obsłudze baz danych w "Programming Groovy"

Rozdział 10. "Working with Databases" w "Programming Groovy: Dynamic Productivity for the Java Developer" Venkata Subramaniama przedstawia temat obsługi bazy danych. Krótki acz treściwy rozdział i wierzę, że relacja będzie co najmniej tak treściwa, a jednocześnie nie bardziej obszerna jak relacjonowany materiał.

Groovy SQL (GSQL) przesłania (przykrywa) JDBC udostępniając dodatkowy zestaw metod dostępowych do danych w bazie danych. Z pomocą GSQL możemy tworzyć zapytania SQL i korzystać z wbudowanych iteratorów do obsługi ich wyników.

Podłączamy się do bazy danych z pomocą groovy.sql.Sql.newInstance() podając namiary na bazę danych. Alternatywnie, jeśli mamy już obiekty java.sql.Connection lub java.sql.DataSource, możemy użyć je jako parametry wejściowe dla odpowiednich konstruktorów klasy groovy.sql.Sql zamiast oprzeć się na wspomnianej newInstance(). Informacje o aktywnym połączeniu otrzymujemy przez getConnection() lub po prostu przez atrybut connection na obiekcie Sql. Zamykamy "kramik" metodą close().

Po uwadze Krzyśka postanawiam zrezygnować z groovysh na rzecz groovyConsole, aby zrzuty poszczególnych skryptów Groovy były możliwe do czytania.

Do poprawnego uruchomienia skryptów do pracy z wybraną bazą danych potrzebujemy sterownika bazodanowego - w moim przypadku będzie to mysql-connector-java-5.1.7-bin.jar. Podobno dodanie jara do ścieżki klas groovyConsole jest możliwe przez menu Add Jar to ClassPath, ale u mnie nie dawało to oczekiwanych rezultatów i wciąż tylko miałem NCDFE.

Skończyło się na uruchomieniu groovyConsole z parametrem -cp.
 groovyConsole -cp "C:\apps\mysql-connector-java\mysql-connector-java-5.1.7-bin.jar"
Tym razem wszystko zagrało i mogłem uruchomić swój pierwszy skrypt z wyświetlenie rekordów z bazy
 def sql = groovy.sql.Sql.newInstance('jdbc:mysql://localhost:3306/groovymysqldb', 'root', 'passw0rd', 'com.mysql.jdbc.Driver')
println sql.connection.catalog
wyświetla oczekiwane "groovymysqldb".

Można również oprogramować dodanie jara do ścieżki klas.
 import groovy.sql.*

this.class.classLoader.rootLoader.addURL(new URL("file:C:/apps/mysql-connector-java/mysql-connector-java-5.1.7-bin.jar"))
def sql = Sql.newInstance('jdbc:mysql://localhost:3306/groovymysqldb', 'root', 'passw0rd', 'com.mysql.jdbc.Driver')
Baza została świeżo założona na potrzeby doświadczeń z GSQL, więc zaczynamy od utworzenia tabeli i kilku przykładowych rekordów za pomocą klasy DataSet. Wywołanie Sql.dataSet() z parametrem wskazującym na tabelę zwraca proxy (typu DataSet) do danych bez ich pobierania. Tworzenie nowych rekordów w bazie jest możliwe przez DataSet.add(), której parametrami wejściowymi są pary kolumna:wartość. Możemy również skorzystać z bardziej tradycyjnych metod jak Sql.execute() oraz Sql.executeInsert() podając na ich wejściu polecenia INSERT.
 import groovy.sql.*

def sql = Sql.newInstance('jdbc:mysql://localhost:3306/groovymysqldb', 'root', 'passw0rd', 'com.mysql.jdbc.Driver')
println "Korzystam z bazy: ${sql.connection.catalog}"
sql.execute("DROP TABLE pracownicy")
sql.execute("CREATE TABLE pracownicy(imie varchar(25), nazwisko varchar(25))")
def ds = sql.dataSet('pracownicy')
ds.add(imie: 'Jacek', nazwisko: 'Laskowski')
Zapytanie typu SELECT uruchamiamy metodą eachRow(), która na wejściu oczekuje zapytania (jako tekst) oraz domknięcia, które jest uruchamiane, dla każdego rekordu.
 // wraz z tym, co wyżej
println "[Imie]\t[Nazwisko]"
sql.eachRow('SELECT * from pracownicy') {
printf "%s\t%s\n", it.imie, it.nazwisko
}
W wyniku dostajemy:
 Korzystam z bazy: groovymysqldb
[Imie] [Nazwisko]
Jacek Laskowski
GroovyResultSet, który jest przekazywany domknięciu, pozwala na dostęp do kolumn po nazwie lub indeks.

Inna wersja eachRow(), poza samym zapytaniem, przyjmuje dwa domknięcia - pierwszy do obsługi metadanych bazy i jest wykonywana raz, a drugi, jak poprzednio, do obsługi rekordów. Przykładu nie będzie - należy zajrzeć do książki, bądź do dokumentacji Groovy (można zacząć od lektury Database features)

Możemy również skrócić obsługę wierszy do poziomu obsługi listy z rows(). Odczyt danych jest również możliwy przez each() (podobne do Sql.eachRow()). Dodając do tego możliwość zawężania wyników (filtrowania ich) dzięki findAll() otrzymujemy niezwykle efektywne narzędzie do wyciągania danych z bazy.

Mamy również do dyspozycji Sql.call() do uruchamiania procedur składowanych oraz Sql.withStatement(), które akceptuje domknięcie uruchamiane przed wykonaniem zapytania - interesująca opcja, jeśli chcemy wpływać na ostateczną postać zapytań zanim zostaną wysłane do bazy danych.

W połączeniu z mechanizmem budowniczych w Groovy (ang. Groovy builders) tworzenie XMLi z danych z danych w bazie to "kaszka z mleczkiem". Smacznego! :)
 // wraz z tym, co wyżej
bldr = new groovy.xml.MarkupBuilder()
bldr.pracownicy {
sql.eachRow("SELECT * from pracownicy") {
pracownik(imie: it.imie, nazwisko: it.nazwisko)
}
}
W wyniku mamy:
 Korzystam z bazy: groovymysqldb
<pracownicy>
<pracownik imie='Jacek' nazwisko='Laskowski' />
</pracownicy>
W sekcji 10.6 "Accessing Microsoft Excel" autor przedstawia sposób na tworzenie plików XLS na podstawie danych z bazy (przez sterownik JDBC-ODBC). Nic odkrywczego z punktu widzenia samego użycia JDBC-ODBC, ale prostota Groovy bije po oczach. Za Krzyśkiem w jego komentarzu do poprzedniego wpisu o Groovy "nie wszystko to zasługa dynamizmu!". Parafrazując szefo pingwinów: "I to jest coś, z czym mogę się pogodzić", bo wychodzę z założenia, że wiele można zrobić, tylko trzeba mieć na to chęć i czas. Skoro robię to w Groovy i uderza mnie jego prostota, to chcę, czy nie, kładę to na barki jego cech języka dynamicznego. Czasami sobie myślę, że warto by wprowadzić swego rodzaju ożywienie w naszych dyskusjach na blogach i odpowiadać na wpisy za pomocą...(kontr)aplikacji. Gdybym tak mógł poczytać wpisy, które pokazywałyby cechy Scali, F# czy Ocaml jako reakcję na te o Groovy byłoby bajecznie. Wchodzisz Krzysiek w temat? Rękawica leży i czeka na podniesienie :)