Groovy <1> 语言特性
前言
Groovy 是基于 JRE 的脚本语言( Script ),是由James Strachan 和 Bob McWhirter 这两位天才发明的,(JSR 241 2004 年 3 月)。

和Perl,Python等等Script的设计初衷一样,作者希望Groovy能快速简洁地完成一些工作:如访问数据库以编写报告,编写单元测试用例(Unit Test Case),快速实现产品原型(ProtoType) 等等。

同时为了降低学习曲线,Groovy 的语法和Java 近似,并吸收了 Ruby,Python 和SmallTalk 的一些特点,因此 Groovy 在某些场合可以扮演一种 “咖啡伴侣”的角色。

那么Groovy 和 Java 相比,有什么变化呢? Groovy 和大部分Scripts一样:

不用编译,通过解释运行。
允许动态类型。
合成结构容易。
因此,Groovy 是一种特别容易学习和使用的语言.

我们先借用IBM Groovy教程中的例子,下面的代码利用了Freemarker模板引擎来创建一个Template对象,然后将内容打印到标准输出。例(1)是Java代码,例(2)是Groovy代码。可以看到二者非常的类似。

//简单的 TemplateReader Java 类import java.io.File;import java.io.IOException;import freemarker.template.Configuration;import freemarker.template.Template;public class TemplateReader { public static void main(String[] args){ try{ Configuration cfg = Configuration.getDefaultConfiguration(); cfg.setDirectoryForTemplateLoading(new File("C:/dev/projects/http-tester/src/conf")); Template temp = cfg.getTemplate("vendor-request.tmpl"); System.out.println(temp.toString()); } catch(IOException e){ e.printStackTrace(); } }}


和例2 中的 Groovy 作为对比.

//用 Groovy 编写的更简单的 TemplateReader//语法目前基于Groovy 1.0-JSR3import freemarker.template.Configuration as tconfimport java.io.Filecfg = tconf.getDefaultConfiguration()cfg.setDirectoryForTemplateLoading( new File("C:/dev/projects/http-tester/src/conf")) temp = cfg.getTemplate("vendor-request.tmpl")println temp.toString()


Groovy显然精简得多:

Groovy 代码只需要更少的import 语句。此外,freemarker.template.Configuration 还使用了别名 tconf。
Groovy 允许直接使用类型为Template 的变量 tmpl 而不用声明其类型。
Groovy 不需要class 声明或者main 方法。
Groovy 不关心异常,可以不用导入Java需要的IOException。
对Groovy感兴趣吗?别忘了拜访它的主页 http://groovy.codehaus.org/ 并下载最新的Groovy发布包哦。(ray_linn)


Groovy 2 基础语法
由 ray_linn 所撰寫。最後修改者是 ray_linn 在 408 天之前。 已瀏覽 391 次。[編輯] [附加]
Groovy的基础语法
Groovy 有很多优点。它很好地融合了 Ruby、Python 和 Smalltalk 的一些最有用的功能,同时保留了基于 Java 语言的核心语法。对于Java 开发人员,Groovy 提供了更简单的替代语言,且几乎不需要学习时间。对于刚接触 Java 平台的开发人员,它可以作为有更复杂语法和要求的 Java 语言的一个容易的入口点。

语法
Groovy的语句和Java类似,但是有一些特殊的地方。例如语句的分号是可选的。如果每行一个语句,就可以省略分号;如果一行上有多个语句,则需要用分号来分隔。

x = [1, 2, 3]println xy = 5; x = y + 7println xassert x == 12

另外return关键字在方法的最后是可选的;同样,返回类型也是可选(缺省是Object)。

动态类型
像其他Script一样,Groovy 不需要显式声明类型。在 Groovy 中,一个对象的类型是在运行时动态发现的,这极大地减少了要编写的代码数量。在Groovy中,类型对于值(varibles)、属性 (properties)、方法(method)和闭包(closure)参数、返回值都是可有可无的,只有在给定值的时候,才会决定它的类型,(当然声 明了类型的除外)。例如: //Groovy 动态类型myStr = "Hello World"

由于使用了动态类型,不需要继承就可以得到多态的全部功能:

class Song{ @Property length @Property name}class Book{ def public name def public author}def doSomething(thing){ println "going to do something with a thing named = " + thing.name}



这 里定义了两个Groovy 类,Song 和 Book。这两个类都包含一个 name 属性。函数 doSomething,它以一个 thing 为参数,并试图打印这个对象的 name 属性,但doSomething 函数没有定义其输入参数的类型,所以只要对象包含一个 name 属性,那么它就可以工作。可见, Song 和 Book 的实例都可以作为 doSomething 的输入参数。

mySong = new Song(length:90, name:"Burning Down the House")myBook = new Book(name:"One Duck Stuck", author:"Phyllis Root")doSomething(mySong) //prints Burning Down the HousedoSomething(myBook) //prints One Duck Stuckdef doSth=this.&doSomethingdoSth(mySong)doSth(myBook)


在例子的最后,我们还创建了doSomething的一个函数指针 doSth,最后的执行结果与调用doSoemthing是一样的。

值 得注意的是:与Groovy Beta不同,在使用新的JSR Groovy类时,类里面的所有的变量都必须加上 def 关键字或者 private、protected 或 public 这样的修饰符。当然,也可以用 @Property 关键字声明成员变量。在Script中则不必。

字符串
Groovy中的字符串允许使用双引号和单引号。

当使用双引号时,可以在字符串内嵌入一些运算式,Groovy允许您使用 与 bash 类似的 ${expression} 语法进行替换。可以在字符串中包含任意的Groovy表达式。

name="James"println "My name is ${name},'00${6+1}'" //prints My name is James,'007'

Groovy还支持"uXXXX" 引用(其中X是16进制数),用来表示特殊字符,例如 "u0040" 与"@"字符相同。

大块文本

如果有一大块文本(例如 HTML 和 XML)不想编码,你可以使用Here-docs. here-docs 是创建格式化字符串的一种便利机制。它需要类似 Python 的三重引号(""")开头,并以三重引号结尾。

name = "James"text = """hello there ${name} how are you today?"""assert text != nullprintln(text)

在Groovy-JSR中,不再支持下面这种多行字符串,个人觉得似乎与Here-docs功能重叠:

foo = "hello there how are things?"println(foo)

对字符串的操作

contains 字符串中是否包含子字符串,'groovy'.contains('oo')将返回true;
count 返回字符串中子字符串出现的次数,'groooovy'.count('oo')将返回3.
tokenize 根据分隔符将字符串分解成子串,'apple^banana^grap'.tokenize('^')返回['apple','banana','grape']。
减操作 'groovy'-'oo',结果是'grvy'。
乘操作 'oo'*3,结果是'oooooo'。
Groovy主要结构
接下来将展示Groovy的一些结构,包逻辑分支,类、闭包等等。
逻辑分支
if-else语句

Groovy提供Java相同的if-else语句。

x = falsey = falseif ( !x ) { x = true}assert x == trueif ( x ) { x = false} else{ y = true}assert x == y

Groovy也支持三元操作符。

y = 5x = (y > 1) ? "worked" : "failed"assert x == "worked"

switch语句
Groovy的switch语句兼容Java代码,但是更灵活,Groovy的switch语句能够处理各种类型的switch值,可以做各种类型的匹配:

case值为类名,匹配switch值为类实例
case值为正则表达式,匹配switch值的字符串匹配该正则表达式
case值为集合,匹配switch值包含在集合中,包括ranges
除了上面的,case值与switch值相等才匹配。

x = 1.23result = ""switch ( x ) { case "foo": result = "found foo" // lets fall through case "bar": result += "bar" case [4, 5, 6, 'inList']: result = "list" break case 12..30: result = "range" break case Integer: result = "integer" break case Number: result = "number" break default: result = "default"}assert result == "number"


Switch 语句的工作原理:switch语句在做case值匹配时,会调用isCase(switchValue)方法,Groovy提供了各种类型,如类,正则表 达式、集合等等的重载。可以创建自定义的匹配类,增加isCase(switchValue)方法来提供自定义的匹配类型。

循环
while和do 循环

Groovy支持Java相同的while循环,但目前暂不支持do循环

x = 0y = 5while ( y-- > 0 ){ x++}assert x == 5

for循环

Groovy的for循环更简单,而且能够和各种类型的数组、集合、Map、范围等一起工作,我们稍候会详细介绍这些内容。

/

/ iterate over a rangex = 0for ( i in 0..9 ) { x += i}assert x == 45// iterate over a listx = 0for ( i in [0, 1, 2, 3, 4] ) { x += i}assert x == 10// iterate over an arrayarray = (0..4).toArray()x = 0for ( i in array ) { x += i}assert x == 10// iterate over a mapmap = ['abc':1, 'def':2, 'xyz':3]x = 0for ( e in map ) { x += e.value}assert x == 6// iterate over values in a mapx = 0for ( v in map.values() ) { x += v}assert x == 6// iterate over the characters in a stringtext = "abc"list = []for (c in text) { list.add(c)}assert list == ["a", "b", "c"]



运行Groovy脚本
你可以象使用Perl一样编写Groovy脚本,不需要class,不需要Main入口点,也不需要声明变量;此外,你还可以用def语句来定义自己的函数,并在脚本中使用它。

像许多脚本语言一样,Groovy 是 在运行时解释的,无编译的代码在构建-运行周期中可以提供很多好处。运行时编译使 Groovy 成为快速原型设计、构建不同的实用程序和测试框架的理想平台。通过以下代码可以很简单的运行Groovy.

groovy HelloWorld.groovy

除了利用解释器来运行Groovy脚本外,Groovy 提供了两种不同的解释器Shell,使所有有效的 Groovy 表达式可以交互地执行:

运行groovysh启动命令Shell,可以输入Groovy语句直接执行
运行groovyConsole启动Swing方式的Groovy控制台,这是一个简单的Groovy编辑器
Groovy 脚本实际上是字节码级别的 Java 类。因此,还可以用 groovyc 编译 Groovy 脚本。可以通过命令行或者 Ant 使用 groovyc 以生成脚本的类文件。这些类可以用普通 java 命令运行,只要 classpath 包括 groovy.jar和asm.jar。


Groovy 3 类和闭包
由 ray_linn 所撰寫。最後修改者是 ray_linn 在 386 天之前。 已瀏覽 282 次。[編輯] [附加]灵活性是更有效地开发代码的主要因素。Groovy语言是建立在总有一天 Java 平台要包括一种敏捷开发语言这一信念上的 (Richard Monson-Haefel),因此Groovy的语法是始终围绕灵活性这个特性进行设计的。 因为 Groovy 除了是种面向过程和面向对象的语言外,它还包含了实现函数式编程一些东西。
函数编程
我们知道例如Lisp、Scheme、Haskell、ML (或其他一些语言)采用了函数式编程。但“到底什么是函数式编程 (FP)?”不幸的是,即使是函数程序员他们自己也很难对 FP 究竟是什么有个一致的认识。

David Mertz 认为函数式编程粗略地描绘为至少具有以下几个特征:

函数是第一类(对象)。即,可以对“数据”进行的每样操作都可以使用函数本身做到(例如将一个函数传递给另一个函数)。
将递归用作主要的控制结构。在某些语言中,不存在其它“循环”构造。
重点集中在列表 LISt 处理(例如,名称 Lisp )。列表经常和子列表的递归一起使用以替代循环。
“纯”函数语言能够避免副作用。这不包括在命令语言中最普遍的模式,即指定第一个,然后将另一个值指定给同一个变量来跟踪程序状态。
FP 不鼓励或根本不允许出现 语句,取而代之是使用表达式求值(换句话说,即函数加上自变量)。在很纯粹的情况下,一个程序就是一个表达式(加上支持的定义)。
FP 关心的是计算 什么而不是 如何计算。
许多 FP 利用了“更高等级”函数(换句话说,就是函数对一些函数操作,而这些函数又对其它函数操作)。
函数编程的提倡者认为所有这些特征都导致更快速的开发更短以及错误更少的代码。而且,计算机科学、逻辑和数学领域的高级理论学家发现证明函数语言和程序的正式性能比命令语言和程序容易得多。

无独有偶,新发布的C# 3.0 规格书也加入了函数式编程的味道,从某些方面看起来,C# 3.0和Groovy 有点神似。


在字节码水平,Groovy 类是真正的 Java 类。Groovy的类与Java类相似:
方法可以基于类(static)或实例
可以为public、protected或private
支持常用的Java修饰符,如synchronized
请注意,Groovy 1.0-JSR3与Groovy 1.0-Beta3在类定义上语法差别较大:

Groovy JSR 变量前都必须加上 def 关键字或者 private、protected 或 public 这样的修饰符;Groovy Beta3的类定义中的变量相当灵活,不需要任何关键字;(类定义外的变量不受影响。)
声明属性可以用 @Property 关键字声明成员变量 ;有别于Beta3,这里P是大写的!
变量默认为Private,方法默认为Public,除非使用了特定的访问修饰符;Groovy Beta3 类中定义的所有内容都默认为 public,除非定义了特定的访问修饰符;
例子 1 显示了在 Groovy 语言中如何声明一个Greeter的类,它有两个public的字段name和age。注意我们是如何为它们赋值的。

//无构造子的Groovy Greeterclass Greeter{ public name public agedef SayHello(){ println "Hello,${name},I am Groovy" println "I am ${age} years old!" }def static Introduce(){ println "I am groovy greeter"; }}Greeter.Introduce()Greeter greeter=new Greeter(name:"Toms",age:2)greeter.SayHello()

上面的例子等价于使用传统构造子的Greeter,但是更灵活。

//使用传统构造子的Groovy Greeterclass Greeter{ def name def age Greeter(name,age){ this.name=name this.age=age }def SayHello(){ println "Hello,${name},I am Groovy" println "I am ${age} years old!" }def static Introduce(){ println "I am groovy greeter"; }}Greeter.Introduce()Greeter greeter=new Greeter("Toms",2)greeter.SayHello()

接下来是一个复杂点的例子,其中类 Dog 有一个 getFullName 方法,它实际上返回一个表示 Dog 的全名的 String。

class Dog{ public namedef bark(){ println "RUFF! RUFF!" }def getFullName(master){ name + " " + master.lname }def obeyMaster(){ println "I hear you and will not obey." }}

我们可以看到,动态类型可以应用到字段和方法,因此参数类型是可选的(但是指定方法的参数类型和返回类型可以在通常的Java代码中更好的工作)。注意,最后return语句是可选(只要最后一条语句被执行,它的值就会被返回),缺省返回的是Object。

例子 4 是有两个属性 fname 和 lname 和一个方法的类 DogOwner。

class DogOwner{ @Property fname @Property lnamedef trainPet(pet){ pet.obeyMaster() }}

方法调用的语法和Java类似,支持静态和实例方法。在例 5 中,我们用 Groovy对Dog和DogOwner的实例设置属性并调用了方法。

//使用 Groovy 类myDog = new Dog()myDog.name = "Mollie"myDog.bark()myDog.obeyMaster()me = new DogOwner()me.fname = "Ralf"me.lname = "Waldo"me.trainPet myDog //不需要括号str = myDog.getFullName(me)println str // prints Mollie Waldo

我们看到在 Dog 类的 getFullName 方法返回一个 String 对象,在这里它是 “Mollie Waldo”。 在Groovy中方法的调用可以省略括号,只要有参数,并且没有歧义。

传递命名参数 在调用方法时,可以传递命名参数,参数的名字和值用分号分隔(象Map语法),参数名是唯一标识字符串。

bean = new Expando(name:"James", location:"London", id:007)println "Hey " + bean.nameassert bean.id == 007

注意:当前这种方法传递只实现具有Map的方法调用或JavaBean。

闭包
Groovy 中最令人兴奋和最强大的功能是支持闭包。闭包(Closure)类似于 Java 语言中的匿名内部类。闭包和匿名内部类都是可执行的一段代码,不过这两者之间有一些细微的不同。状态是自动传入传出闭包的。闭包可以有名字。它们可以重复 使用。而且,最重要且对 Groovy 同样成立的是,闭包远比匿名内部类要灵活得多!闭包用以下方法定义:

{[comma-separated-Arguments-list ->] statements }

闭包用“{}”括起,“->”前面是参数,后面是处理语句。下面的例子演示了如何定义并使用一个闭包。第一个闭包演示了在字符串中使用参数的形式:${param};第二个闭包演示了多参数形式:用逗号隔离参数。

closure = { name -> println("hello ${name}") }closure.call("world!") closure = { greeting, name -> println(greeting + name) }closure("hello ", "world!")

有趣的是,闭包至少会有一个参数 “it”,它其实是参数表中的第一个参数,所以如果闭包定义中只有一个参数,可以省略不写,而使用缺省的参数“it”。对于没有参数定义的闭包,“it”的值就是 null 。

closure = { println "hello " + it }closure("world!")

例子 9 展示了闭包的强大功能。新改进的 Dog 类包括一个 train 方法,它实际上执行创建了 Dog 实例的闭包。

class Dog{def train(action){ action.call() }}sit = { println "Sit, Sit! Sit! Good dog"}myDog = new Dog()myDog.train(sit) //prints Sit, Sit! Sit! Good dogmollie = new Dog()mollie.train { println "Down! DOWN!" } //prints DOWN! DOWN!

在 上面的例子里,我们还看到,闭包通过 call() 方法被调用。(在底层,call() 方法实现了对闭包隐含的doCall()方法的调用)。Groovy 中的大多数对象具有像 each 和 find 这样的以闭包为参数的方法。用闭包来迭代对象会产生几种令人兴奋的可能性:

[2, 4, 6, 8, 3].find { x | if (x == 3){ println x }}

其实就是:(你也许已经发现,可以用it代替x)

myFinder={ x | if(x==3) println x}[2,4,6,8,3].find(myFinder)

这里我们使用了一个 Groovy-Beta3 闭包定义,只是为了让您能看懂老版本Groovy的一些例子。在新的Groovy JSR中对闭包的定义略有改动,因为 | 字符同时也是 Java 中的位操作符;Groovy JSR 建议使用 Nice (另外一种JRE语言)样式的 -> 分隔符代替它。

这种以闭包为参数的代码在Groovy里十分普遍:

//each(1..100).each{ println it}//times 1000.times{ println it} //values go from 0 to 9999//upto1.upto(1000){ println it}//step 1.step(1001,1){ println it} //values go from 1 to 1000;
更多关于闭包的内容,可以访问 http://groovy.codehaus.org/Closures,不过有些内容可能不适用于Groovy JSR3,因为Groovy还在不断发展中。


Groovy 4 集合
由 ray_linn 所撰寫。最後修改者是 ray_linn 在 218 天之前。 已瀏覽 279 次。[編輯] [附加]将对象组织到像列表和映射这样的数据结构中是一项基本的编码任务。像大多数语言一样,Groovy 定义了一个丰富的库以管理这些类型的集合。
列表
创建一个列表与在 Java 语言中创建一个数组很类似。

collect = ['groovy', 29, 'Hello', 'Groovy']empty=[]assert empty.size() == 0//添加元素empty.add 1assert empty.size() == 2

在上面的例子里,列表 collect 的第二项自动装箱为一个 Integer 类型。此外“[]”表示一个空列表,这个例子还演示了如何向列表中添加元素。

Groovy 还为集合增加了几个新方法,这些方法使得对列表的一些操作变得更加容易,如统计值出现的次数、将整个列表结合到一起、对列表排序等等。可以在例2 中看到这些集合方法的使用。

collect = [5, 9, 2, 2, 4, 5, 6] println collect.join(' - ') // prints 5 - 9 - 2 - 2 - 4 - 5 - 6println collect.count(2) // prints 2println collect.sort() // prints [2, 2, 4, 5, 5, 6, 9]

Maps
像列表一样,映射也是一种在 Groovy 中非常容易处理的数据结构。例 3 中的映射包含两个对象,键是 name 和 date。注意可以用不同的方式取得值。 可以用“[:]”创造一个空的映射。

myMap = ["name" : "Groovy", "date" : new Date()]println myMap["date"]println myMap.date

Map可以象beans一样操作,但key值(类似属性名)必须为有效的String标识。下面的例子详细演示了Map的用法。

map = ["name":"Gromit", "likes":"cheese", "id":1234]assert map.name == "Gromit"assert map.id == 1234//Create an empty mapsemptyMap = [:]assert emptyMap.size() == 0emptyMap.foo = 5 assert emptyMap.size() == 1assert emptyMap.foo == 5emptyMap.put("bar",6)assert emptyMap.get("bar") == 6players = ['baseball':'Albert Pujols', 'golf':'Tiger Woods']println players['golf'] // prints Tiger Woodsprintln players.golf // prints Tiger Woodsfor (player in players) { println "${player.value} plays ${player.key}"}// This has the same result as the previous loop.players.each { player |println "${player.value} plays ${player.key}"}

范围
范围(Range)是Groovy的一大亮点Range允许创建连续值的列表。范围是一个很直观的概念,并且容易理解,利用它可以包含地或者排除地创建一 组有序值。使用“..”的Range是包括两个边界,使用“..<”(Groovy Beta3使用 “...”)的Range只包括开始边界,而不包括结束边界。
而且由于Range扩展java.util.List,所以Range可以作为List使用。

myRange = 29..<32myInclusiveRange = 2..5println myRange.size() // prints 3println myRange[0] // prints 29println myRange.contains(32) //prints falseprintln myInclusiveRange.contains(5) //prints true// an inclusive range and operationsrange = 'a'..'d'assert range.size() == 4assert range.get(2) == 'c'assert range instanceof java.util.Listassert range.contains('a')assert range.contains('d')assert ! range.contains('e')

迭代器
迭代是各种编程环境中最常见、最有用的技术。迭代器可以让您迅速地访问任何集合或容器中的数据,每次一个数据。Groovy 把迭代器变成隐含的,使用起来更简单,从而改善了 Java 语言的迭代器概念。
我们可以直接在集合上使用类似迭代器的方法。而且, Groovy 的迭代器方法接受闭包,每个迭代中都会调用闭包。

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

范围可以用于循环遍历。例如,将 rang 定义为一个排除范围,循环打印 a、b、c 和 d。

aRange = 'a'..<'e'for (i in aRange){ println i}

集合的其他功能
如果不熟悉 Python 和其他脚本语言,那么您在 Groovy 集合中发现的一些其他功能会让您印象深刻。例如,创建了集合后,可以用负数在列表中反向计数:

aList = ['python', 'ruby', 'groovy']println aList[-1] // prints groovyprintln aList[-3] // prints python

Groovy 还让您可以用范围分割列表。分割可获得列表的准确子集:

fullName = "Andrew James Glover"mName = fullName[7..<13]println "middle name: " + mName // prints James

Ruby的语法

集合类似于 Ruby如果愿意的话,还可以将 Groovy 集合作为 Ruby 集合。可以用类似 Ruby 的语法,以 << 语法附加元素、用 + 串接和用 - 对集合取差,甚至还可以用 * 语法处理集合的重复。 注意,还可以用 == 比较集合。

collec = [1, 2, 3, 4, 5]collec << 6 //appended 6 to collecacol = ['a','b','c'] * 3 //acol now has 9 elementscoll = [10, 11]coll2 = [12, 13]coll3 = coll + coll2 //10,11,12,13difCol = [1,2,3] - [1,2] //difCol is 3assert [1, 2, 3] == [1, 2, 3] //true

集合的索引

可以在字符串、Lists、Maps...中使用下标进行索引

text = "nice cheese gromit!"x = text[2]assert x == "c"assert x.class == Stringsub = text[5..10]assert sub == 'cheese'map = ["name":"Gromit", "likes":"cheese", "id":1234]assert map['name'] == "Gromit"list = [10, 11, 12]answer = list[2]assert answer == 12 list = 100..200sub = list[1, 3, 20..25, 33]assert sub == [101, 103, 120, 121, 122, 123, 124, 125, 133]

可以使用下标操作符更新项目:

list = ["a", "b", "c"]list[2] = "d"list[0] = list[1]list[3] = 5assert list == ["b", "b", "d", 5]

可以使用负索引从最后开始计数:

text = "nice cheese gromit!"x = text[-1]assert x == "!"name = text[-7..-2]assert name == "gromit"

也可以使用向后范围(开始索引大于结束索引),返回的结果是反转的

text = "nice cheese gromit!"name = text[3..1]assert name == "eci"


Groovy 5 JDBC编程
由 ray_linn 所撰寫。最後修改者是 ray_linn 在 214 天之前。 已瀏覽 338 次。[編輯] [附加]与Groovy的脚本特性相适应,Groovy JDBC 包含了一个精巧简单的GroovySql API,由于使用闭包和迭代器,GroovySql 把 JDBC 的资源管理职责从开发人员转移到 Groovy 框架内,从而消除了 JDBC 编程的繁琐,使得变成人员可以把注意力放在处理查询结果上。
GroovySql 简介
在 Java 中,我们是这样利用JDBC来打印Music CD的样品:

package cc.ejb.groovy;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;public class CdList { public static void main(String[] args) { Connection con = null; Statement stmt = null; ResultSet rs = null; try{ Class.forName("com.mysql.jdbc.Driver"); con = DriverManager.getConnection("jdbc:mysql://localhost:3306/samples","root", ""); stmt = con.createStatement(); rs = stmt.executeQuery("select * from cdbean"); while (rs.next()) { System.out.println("id:" + rs.getLong(1) + " artist:" + rs.getString(2) + " type:" + 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){} } }}



利用迭代器和闭包,我们只需要短短的几行:

import groovy.sql.Sqlsql = Sql.newInstance("jdbc:mysql://localhost:3306/samples", "root","", "com.mysql.jdbc.Driver")sql.eachRow("select * from cdbean"){ println "id:${it.id} artist:${it.artist} type:${it.type}"}

在上面这段脚本中,你看不到try...catch,也不用捕获任何exception,甚至你不用关闭 Connection,也不用关闭 ResultSet,或者在 JDBC 编程中可以找到的任何其他熟悉的重要特性。

脚本中eachRow 方法可以当成查询结果上的迭代器。每个迭代都会执行传递进去的闭包,并在闭包中打印出每一次迭代的内容,id,artist,type等等。

为了运行上面的例子,必须将MySql JDBC Driver添加到classpath中。

执行更复杂的查询
Groovy 的 Sql 对象另外提供了 execute 和 executeUpdate 方法用于处理更复杂的数据操作(例如 insert、 update 和 delete )。
在下面的脚本中,您看到一个简单的 insert,它再次以 ${} 语法使用变量替换。这个代码只是向 cdbean 表插入一个新行。

id = 999title ="Concept"artist = "Nefarious"type = "Blues"sql.execute("insert into cdbean (id, title,artist, type, notes) values(${id},${title}, ${artist}, ${type},NULL)")

Groovy 还提供 execute 方法的一个重载版本,它接收一列值,这些值与查询中发现的 ? 元素对应。下面的例子查询了刚插入 cdbean 表中的数据。在底层,GroovySql 创建了普通 Java 语言 java.sql.PreparedStatement 的一个实例。

val = sql.execute("select * from cdbean where id = ?", [999])

更新的方式基本相同,也使用 executeUpdate 方法。executeUpdate 方法接收一列值,与查询中的 ? 元素对应。

id= 556 artist = "Nefarious" sql.executeUpdate("update cdbean set id = ? where artist = ?", [id, artist])

删除实际上与插入相同,当然,语法不同,如例 9 所示。

sql.execute("delete from cdbean where id = ?" , [556])

数据集(DataSet)
构建于 GroovySql 简单性的基础之上,GroovySql 支持 DataSet 类型的概念,这基本上是数据库表的对象表示。使用 DataSet,您可以在行中遍历,也可以添加新行。实际上,用数据集是方便地表示表格的公共数据集合的方式。

但是,目前 GroovySql DataSet 类型的不足之处是它们没有代表关系;它们只是与数据库表的一对一映射。

import groovy.sql.Sqlclass CdDataSetExample{ static void main(args) { def sql = Sql.newInstance("jdbc:mysql://localhost:3306/samples", "root", "", "com.mysql.jdbc.Driver") def myCD = sql.dataSet("cdbean") myCD.each{ cd -> println "${cd.id}+ ${cd.title}"} myCD.add(id:"9999", title:"clerisy", artist:"Mary",type:"POP") }}


GroovySql 的 DataSet 类型可以容易地用 each 方法对表的内容进行遍历,容易地用 add 方法添加新行,add 方法接受一个 map 表示需要的数据。

使用存储过程和负索引
存储过程调用和负索引(negative indexing)可能是数据操纵的重要方面。GroovySql 使存储过程调用简单得就像在 Sql 类上使用 call 方法一样。对于负索引, GroovySql 提供了自己增强的 ResultSet 类型,它工作起来非常像 Groovy 中的 collections。例如,如果您想获取结果集中的最后一个项目,您可以像例子 所示的那样做:

sql.eachRow("select * from cdbean where id=556"){ cd -> println "-1 = " + cd.getAt(-2) //prints type println "2 = " + cd.getAt(3)//prints type }


编写一个简单的报告应用程序
我们将利用 GroovySql 编写一个报告所需数据库统计信息的应用程序,您的目标数据库是 MySQL,它恰好支持用查询发现状态信息这一概念。以下是您有兴趣的状态信息:

运行时间。
处理的全部查询数量。
特定查询的比例,例如 insert、update 和 select。

用 GroovySql 从 MySQL 数据库得到这个信息太容易了。 import groovy.sql.Sqlclass StatusReport{ static void main(args) { def uptime,questions,insertnum,selectnum,updatenum def sql = Sql.newInstance("jdbc:mysql://localhost:3306/samples", "root", "", "com.mysql.jdbc.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)) }}


结束语
GroovySql 这个干净漂亮的 API 把闭包和迭代器与 Groovy 轻松的语法结合在一起,有助于在 Java 平台上进行快速数据库应用程序开发