Yokohama.groovy #8 に参加しました

2012/11/13 Yokohama.groovy #8 #yokohamagroovy - Togetterまとめ

諸事情によりいつものタネマキさんではなく、アジャイルサムライ読書会横浜道場でおなじみ株式会社アットウェアさんでした。
会場提供いただきありがとうございました!
20時ごろからぼちぼち集まって最終的に 5 人となりました。

今回の範囲

プログラミングGROOVY

プログラミングGROOVY

今回も引き続き、"4.1 Groovy API" より "データベース処理" を中心に写経したり議論したりしました。

感想*1

率直な感想として今回の範囲はほぼ SQL 直書きだったので、O/R マッパーが組み込まれててもいいんじゃないかなと思ったり。
好きなライブラリ使えば良いってことかもしれませんが。
GORM というライブラリがあるのですが、Grails を使うのが基本ぽかったのでもっと手軽にできる仕組みがあると便利なのかな?

でもスクリプトとして手軽に書けるし、取得したデータを加工するのは強力なので、ちょっとしたツールを作るのには良さそうです。

そんな話をしている途中で、数年前のプロジェクトで DB 移行のために、シェルスクリプトSQL ファイル準備して、それらを使うための詳細な手順書もいっぱい書いたなーというようなことを思い出してそっと記憶に蓋をしました。

その他

あと 2 回で青本が一区切りして年内の活動終了の予定です。
"進んだ話題"のメタプログラミングとか楽しみですね。

年明けから何をするかは色々アイデアがあがってるので相談しながら進めていきたいです。
そろそろ告知サイトとか立てようかという話も出てますので興味がある方はぜひぜひ。

写経の成果

環境準備など
@Grab('org.apache.derby:derby:[10.5.3,)')
@GrabConfig(systemClassLoader=true)
import groovy.sql.Sql

def sql = Sql.newInstance('jdbc:derby:memory"testdb;create=true','user','password','org.apache.derby.jdbc.EmbeddedDriver')
テーブル作成
sql.execute('''create table PERSON (
	id integer not null primary key,
	firstname varchar(20),
	lastname varchar(20),
	location_id integer,
	location_name varchar(30)
)''')
データの挿入
sql.execute('''
	insert into PERSON (id,firstname,lastname,location_id,location_name)
	values (1,'Guillaume','Laforge',10,'Paris')
''')

sql.execute('''
	insert into PERSON (id,firstname,lastname,location_id,location_name)
	values (2,'Dierk','Konig',20,'Zurich')
''')
sql.execute('''
	insert into PERSON (id,firstname,lastname,location_id,location_name)
	values (3,'Paul','King',30,'Brisbane')
''')
データの挿入 PreparedStatement を利用する場合
def personInsert = '''
	insert into PERSON (id,firstname,lastname,location_id,location_name)
	values (?, ?, ?, ?, ?)
'''

sql.execute personInsert, [1,'Guillaume','Laforge',10,'Paris']
sql.execute personInsert, [2,'Dierk','Konig',20,'Zurich']
sql.execute personInsert, [3,'Paul','King',30,'Brisbane']
データの挿入 GString の利用
def persons = [
	[id:1,first:'Guillaume', last:'Laforge',locid:10,loc:'Paris'],
	[id:2,first:'Dierk',last:'Konig',locid:20,loc:'Zufich'],
	[id:3,first:'Paul',last:'King',locid:30,loc:'Brisbane']
]

persons.each {p ->
	sql.execute """
	insert into PERSON (id,firstname,lastname,location_id,location_name)
	values (${p.id}, ${p.first},${p.last},${p.locid},${p.loc})
	"""
}
データの取得
sql.eachRow('select * from PERSON'){println it}
データの取得 eachRow() による処理
def sql = Sql.newInstance('jdbc:derby:memory"testdb;create=true','user','password','org.apache.derby.jdbc.EmbeddedDriver')

println ' Person Info '.center(25, '-')
sql.eachRow('select * from PERSON'){p->
	println "${p.id}: ${p.firstname} ${p.lastname}"
	println "Location: ${p.location_name}(${p.location_id})"
	println '-'*25
}
データの取得 query() による処理
println ' Person Info '.center(25,'-')
sql.query('select id,firstname,lastname,location_id,location_name from PERSON'){rs->
	while(rs.next()){
		println "${rs.getInt(1)}: ${rs.getString(2)} ${rs.getString(3)}"
		println "Location: ${rs.getString(5)}(${rs.getInt(4)})"
		println '-'*25
	}
}
データの取得 rows() による処理
def personList = sql.rows('select * from PERSON')

println ' Person List '.center(25,'-')

personList.each{p->
	println "${p.id}: ${p.firstname} ${p.lastname}"
	println "Location: ${p.location_name}(${p.location_id})"
	println '-'*25
}
バッチ処理 withBatch() の例
def updateCounts = sql.withBatch {stmt ->
	stmt.addBatch('''
		insert into PERSON (id,firstname,lastname,location_id,location_name)
		values (1,'Guillaume','Laforge',10,'Paris')
	''')
	stmt.addBatch('''
		insert into PERSON (id,firstname,lastname,location_id,location_name)
		values (2,'Dierk','Konig',20,'Zurich')
	''')
	stmt.addBatch('''
		insert into PERSON (id,firstname,lastname,location_id,location_name)
		values (3,'Paul','King',30,'Brisbane')
	''')
}

assert updateCounts == [1,1,1]
バッチ処理 withTransaction() の例
try{
	sql.execute('''drop table BANKACCOUNT''')
} catch(Exception e) {}

sql.execute('''create table BANKACCOUNT (
	id integer not null primary key,
	name varchar(20),
	amount decimal
)''')

sql.execute('''
	insert into BANKACCOUNT (id, name, amount)
	values (1, 'Guillaume', 10000)
''')
sql.execute('''
	insert into BANKACCOUNT (id, name, amount)
	values (2, 'Dierk', 10000)
''')

def transfer = 100

def from = sql.firstRow("select amount from BANKACCOUNT where id=1")[0]

def to = sql.firstRow("select amount from BANKACCOUNT where id=2")[0]

try{
	sql.withTransaction{
		sql.execute("""
			update BANKACCOUNT set amount=${from-transfer}
			where id=1
		""")

		sql.execute("""
			update BANKACCOUNT set ammount=${from-transfer}
			where id=2
		""")
	}
} catch(e) {}

def accounts = sql.rows('select * from BANKACCOUNT')

println accounts
DataSet による簡易 O/R マッピング
def personSet = sql.dataSet("PERSON")

personSet.add(
	id:1, firstname:"Guillaume", lastname:'Laforge',
	location_id:10, location_name:"Paris")

personSet.add(
	id:2, firstname:'Dierk', lastname:'Konig',
	location_id:20, location_name:'Zurich')

personSet.add(
	id:3, firstname:'Paul', lastname:'King',
	location_id:30, location_name:'Brisbane')

personSet.each {
	println it.firstname
}

personSet.findAll{ it.firstname=='Dierk' }.each{
	println it
}

*1:普段 Java も DB も使ってない人間の感想なので当てにはしないでください。