SlideShare a Scribd company logo
1 of 44
Download to read offline
Working with Databases and Groovy
Dr Paul King
Groovy Lead for Object Computing Inc.
@paulk_asert
http://slideshare.net/paulk_asert/groovy-databases
https://github.com/paulk-asert/groovy-databases
Contents
• groovy.sql.Sql
• Connecting to a database
• Writing to a database
• Reading from a database
• Stored Procedures
• Advanced Reading/Writing
• groovy.sql.DataSet
• MongoDB
• Neo4j
2
groovy.sql.Sql
• Groovy-friendly higher-level API sitting over JDBC
3
Connecting to a database…
• Sql.newInstance
• assumes driver is on the classpath
• other newInstance variants available
4
import groovy.sql.Sql
def url = 'jdbc:hsqldb:mem:marathon'
def user = 'sa'
def password = ''
def driver = 'org.hsqldb.jdbcDriver'
def sql = Sql.newInstance(url, user, password, driver)
// use 'sql' instance
sql.close()
…Connecting to a database…
• Some variations
5
Advanced
def sql = Sql.newInstance(
url: 'jdbc:hsqldb:mem:marathon',
user: 'sa',
password: '',
driver: 'org.hsqldb.jdbcDriver',
cacheStatements: true,
resultSetConcurrency: CONCUR_READ_ONLY
)
Sql.withInstance(url, user, password, driver) {
// use 'sql' instance
}
// no close() required
@Grab('org.hsqldb:hsqldb:2.3.3')
@GrabConfig(systemClassLoader=true)
import groovy.sql.Sql
// ...
…Connecting to a database…
• new Sql() constructor
• if you have a DataSource or existing Connection or Sql instance
• Likely to be the preferred approach with JDK9 Jigsaw
6
import groovy.sql.Sql
import org.hsqldb.jdbc.JDBCDataSource
def dataSource = new JDBCDataSource(
database: 'jdbc:hsqldb:mem:marathon',
user: 'sa', password: '')
def sql = new Sql(dataSource)
// use 'sql' instance
sql.close()
…Connecting to a database
• new Sql() constructor (cont'd)
7
@Grab('org.hsqldb:hsqldb:2.3.3')
@Grab('commons-dbcp:commons-dbcp:1.4')
import groovy.sql.Sql
import org.apache.commons.dbcp.BasicDataSource
def url = 'jdbc:hsqldb:mem:marathon'
def driver = 'org.hsqldb.jdbcDriver'
def dataSource = new BasicDataSource(
driverClassName: driver, url: url,
username: 'sa', password: '')
def sql = new Sql(dataSource)
// use 'sql' instance
sql.close()
Advanced
Writing to a database…
8
def DDL = '''
DROP TABLE Athlete IF EXISTS;
CREATE TABLE Athlete (
athleteId INTEGER GENERATED BY DEFAULT AS IDENTITY,
firstname VARCHAR(64),
lastname VARCHAR(64),
dateOfBirth DATE,
);
'''
sql.execute(DDL)
sql.execute '''
INSERT INTO Athlete (firstname, lastname, dateOfBirth)
VALUES ('Paul', 'Tergat', '1969-06-17')
'''
…Writing to a database
9
def data = [first: 'Khalid', last: 'Khannouchi', birth: '1971-12-22']
sql.execute """
INSERT INTO Athlete (firstname, lastname, dateOfBirth)
VALUES (${data.first},${data.last},${data.birth})
"""
String athleteInsert = 'INSERT INTO Athlete (firstname, lastname) VALUES (?,?)'
def keys = sql.executeInsert athleteInsert, ['Ronaldo', 'da Costa']
assert keys[0] == [2]
def rowsUpdated = sql.executeUpdate '''
UPDATE Athlete SET dateOfBirth='1970-06-07' where lastname='da Costa'
'''
assert rowsUpdated == 1
sql.execute "delete from Athlete where lastname = 'Tergat'"
Advanced
Reading from a database…
10
sql.query('SELECT firstname, lastname FROM Athlete') { resultSet ->
while (resultSet.next()) {
print resultSet.getString(1)
print ' '
println resultSet.getString('lastname')
}
}
sql.eachRow('SELECT firstname, lastname FROM Athlete') { row ->
println row[0] + ' ' + row.lastname
}
…Reading from a database
11
def first = sql.firstRow('SELECT lastname, dateOfBirth FROM Athlete')
def firstString = first.toMapString().toLowerCase()
assert firstString == '[lastname:tergat, dateofbirth:1969-06-17]'
List athletes = sql.rows('SELECT firstname, lastname FROM Athlete')
println "There are ${athletes.size()} Athletes:"
println athletes.collect { "$it.FIRSTNAME ${it[-1]}" }.join(", ")
assert sql.firstRow('SELECT COUNT(*) as num FROM Athlete').num == 3
Invoking Stored Procedures…
12
def FULL_DLL = '''
DROP TABLE Athlete IF EXISTS;
CREATE TABLE Athlete (
athleteId INTEGER GENERATED BY DEFAULT AS IDENTITY,
firstname VARCHAR(64),
lastname VARCHAR(64),
dateOfBirth DATE
);
DROP INDEX idx IF EXISTS;
CREATE INDEX idx ON Athlete (athleteId);
DROP TABLE Run IF EXISTS;
CREATE TABLE Run (
runId INTEGER GENERATED BY DEFAULT AS IDENTITY,
distance INTEGER, -- in meters
time INTEGER, -- in seconds
venue VARCHAR(64),
when TIMESTAMP,
fkAthlete INTEGER,
CONSTRAINT fk FOREIGN KEY (fkAthlete)
REFERENCES Athlete (athleteId) ON DELETE CASCADE
);
'''
sql.execute FULL_DLL
Advanced
Athlete
athleteId
firstname
lastname
dateOfBirth
Run
runId
distance
time
venue
when
fkAthlete
…Invoking Stored Procedures…
• And assume some data has been populated …
• We've refactored our previous insert code into some helper methods.
13
insertAthlete('Paul', 'Tergat', '1969-06-17')
insertAthlete('Khalid', 'Khannouchi', '1971-12-22')
insertAthlete('Ronaldo', 'da Costa', '1970-06-07')
insertRun(2, 4, 55, 'Berlin', '2003-09-28', 'Tergat')
insertRun(2, 5, 38, 'London', '2002-04-14', 'Khannouchi')
insertRun(2, 5, 42, 'Chicago', '1999-10-24', 'Khannouchi')
insertRun(2, 6, 05, 'Berlin', '1998-09-20', 'da Costa')
…Invoking Stored Procedures…
14
db.execute '''
CREATE FUNCTION SELECT_ATHLETE_RUN ()
RETURNS TABLE (lastname VARCHAR(64), venue VARCHAR(64), whenRun DATE)
READS SQL DATA
RETURN TABLE (
select Athlete.lastname, Run.venue, Run.whenRun
from Athlete, Run
where Athlete.athleteId = Run.fkAthlete
order by whenRun
)
'''
db.eachRow('CALL SELECT_ATHLETE_RUN()') {
println "$it.lastname $it.venue $it.whenRun"
} da Costa Berlin 1998-09-20
Khannouchi Chicago 1999-10-24
Khannouchi London 2002-04-14
Tergat Berlin 2003-09-28
…Invoking Stored Procedures…
15
db.execute '''
CREATE FUNCTION FULL_NAME (p_lastname VARCHAR(64))
RETURNS VARCHAR(100)
READS SQL DATA
BEGIN ATOMIC
declare ans VARCHAR(100);
SELECT CONCAT(firstname, ' ', lastname) INTO ans
FROM Athlete WHERE lastname = p_lastname;
return ans;
END
'''
assert db.firstRow("{? = call FULL_NAME(?)}", ['Tergat'])[0] == 'Paul Tergat'
…Invoking Stored Procedures
16
db.execute '''
CREATE PROCEDURE CONCAT_NAME (OUT fullname VARCHAR(100),
IN first VARCHAR(50), IN last VARCHAR(50))
BEGIN ATOMIC
SET fullname = CONCAT(first, ' ', last);
END
'''
db.call("{call CONCAT_NAME(?, ?, ?)}", [Sql.VARCHAR, 'Paul', 'Tergat']) {
fullname -> assert fullname == 'Paul Tergat'
}
Advanced Reading…
• Rowset metadata
17
def dump(sql, tablename) {
println " CONTENT OF TABLE ${tablename} ".center(40, '-')
sql.eachRow('SELECT * FROM ' + tablename) { rs ->
def meta = rs.getMetaData()
if (meta.columnCount <= 0) return
for (i in 0..<meta.columnCount) {
print "${i}: ${meta.getColumnLabel(i + 1)}".padRight(20) // counts from 1
print rs[i]?.toString() // counts from 0
print "n"
}
println '-' * 40
}
}
…Advanced Reading…
18
def dump(sql, tablename) {
println " CONTENT OF TABLE ${tablename} ".center(40, '-')
sql.eachRow('SELECT * FROM ' + tablename) { rs ->
def meta = rs.getMetaData()
if (meta.columnCount <= 0) return
for (i in 0..<meta.columnCount) {
print "${i}: ${meta.getColumnLabel(i + 1)}".padRight(20) // counts from 1
print rs[i]?.toString() // counts from 0
print "n"
}
println '-' * 40
}
} ----------------------- CONTENT OF TABLE Athlete -----------------------
ATHLETEID FIRSTNAME LASTNAME DATEOFBIRTH
------------------------------------------------------------------------
1 Paul Tergat 1969-06-17
2 Khalid Khannouchi 1971-12-22
3 Ronaldo da Costa 1970-06-07
…Advanced Reading…
19
def dump2(sql, tablename) {
def printColNames = { meta ->
def width = meta.columnCount * 18
println " CONTENT OF TABLE ${tablename} ".center(width, '-')
(1..meta.columnCount).each {
print meta.getColumnLabel(it).padRight(18)
}
println()
println '-' * width
}
def printRow = { row ->
row.toRowResult().values().each {
print it.toString().padRight(18) }
println()
}
sql.eachRow('SELECT * FROM ' + tablename, printColNames, printRow)
}
metadata
closure
…Advanced Reading
• Pagination
20
qry = 'SELECT * FROM Athlete'
assert sql.rows(qry, 1, 4)*.lastname ==
['Tergat', 'Khannouchi', 'da Costa', 'Gebrselassie']
assert sql.rows(qry, 5, 4)*.lastname ==
['Makau', 'Radcliffe', 'Ndereba', 'Takahashi']
assert sql.rows(qry, 9, 4)*.lastname ==
['Loroupe', 'Kristiansen']
Advanced Writing…
• Simple transaction support
21
sql.withTransaction {
insertAthlete('Haile', 'Gebrselassie', '1973-04-18')
insertAthlete('Patrick', 'Makau', '1985-03-02')
}
Advanced
…Advanced Writing…
• Batching of ad-hoc SQL statements
22
sql.execute "delete from Athlete where lastname = 'Tergat'"
sql.withBatch { stmt ->
stmt.addBatch '''
INSERT INTO Athlete (firstname, lastname, dateOfBirth)
VALUES ('Paul', 'Tergat', '1969-06-17')'''
stmt.addBatch """
INSERT INTO Run (distance, time, venue, when, fkAthlete)
SELECT 42195, ${2*60*60+4*60+55}, 'Berlin', '2003-09-28',
athleteId FROM Athlete WHERE lastname='Tergat'"""
}
//22/04/2013 6:34:59 AM groovy.sql.BatchingStatementWrapper processResult
//FINE: Successfully executed batch with 2 command(s)
…Advanced Writing…
• Batching with a prepared statement
23
def qry = 'INSERT INTO Athlete (firstname, lastname, dateOfBirth) VALUES (?,?,?);'
sql.withBatch(3, qry) { ps ->
ps.addBatch('Paula', 'Radcliffe', '1973-12-17')
ps.addBatch('Catherine', 'Ndereba', '1972-07-21')
ps.addBatch('Naoko', 'Takahashi', '1972-05-06')
ps.addBatch('Tegla', 'Loroupe', '1973-05-09')
ps.addBatch('Ingrid', 'Kristiansen', '1956-03-21')
}
// If logging is turned on:
// 20/04/2013 2:18:10 AM groovy.sql.BatchingStatementWrapper processResult
// FINE: Successfully executed batch with 3 command(s)
// 20/04/2013 2:18:10 AM groovy.sql.BatchingStatementWrapper processResult
// FINE: Successfully executed batch with 2 command(s)
…Advanced Writing
• Named parameters (two variants) and named-ordinal parameters
24
@Canonical class Athlete { String first, last, dob }
def ndereba = new Athlete('Catherine', 'Ndereba', '1972-07-21')
def takahashi = new Athlete('Naoko', 'Takahashi')
def takahashiExtra = [dob: '1972-05-06']
def loroupe = [first: 'Tegla', last: 'Loroupe', dob: '1973-05-09']
def insertPrefix = 'INSERT INTO Athlete (firstname, lastname, dateOfBirth) VALUES '
sql.execute insertPrefix + ' (?.first,?.last,?.dob)', ndereba
sql.execute insertPrefix + ' (?1.first,?1.last,?2.dob)', takahashi, takahashiExtra
sql.execute insertPrefix + ' (:first,:last,:dob)', loroupe
sql.execute insertPrefix + ' (:first,:last,:dob)', first: 'Ingrid',
last: 'Kristiansen', dob: '1956-03-21'
Topics
• groovy.sql.Sql
• Connecting to a database
• Writing to a database
• Reading from a database
• Stored Procedures
• Advanced Reading/Writing
 groovy.sql.DataSet
• MongoDB
• Neo4j
groovy.sql.DataSet
• Allows any table within an RDBMS to appear as if it
was a Java-like collection of POGOs
• Can be thought of as a poor man's object-relational
mapping system
• Has some smarts for lazy execution of database
queries
• Not meant to be as sophisticated as hibernate
26
DataSet example…
27
def athletes = sql.dataSet('Athlete')
athletes.each { println it.firstname }
athletes.add(
firstname: 'Paula',
lastname: 'Radcliffe',
dateOfBirth: '1973-12-17'
)
athletes.each { println it.firstname }
def runs = sql.dataSet('AthleteRun').findAll { it.firstname == 'Khalid' }
runs.each { println "$it.lastname $it.venue" }
Paul
Khalid
Ronaldo
Paul
Khalid
Ronaldo
Paula
Khannouchi London
Khannouchi Chicago
…DataSet example
28
def query = athletes.findAll { it.firstname >= 'P' }
query = query.findAll { it.dateOfBirth > '1970-01-01' }
query = query.sort { it.dateOfBirth }
query = query.reverse()
println query.sql
println query.parameters
println query.rows()*.firstname // One SQL query here!
select * from Athlete where firstname >= ? and dateOfBirth > ? order by dateOfBirth DESC
[P, 1970-01-01]
[Paula, Ronaldo]
Overcoming groovy.sql.DataSet limitations
29
def DLL_WITH_VIEW = '''
DROP TABLE Athlete IF EXISTS;
CREATE TABLE Athlete (
athleteId INTEGER GENERATED BY DEFAULT AS IDENTITY,
firstname VARCHAR(64),
lastname VARCHAR(64),
dateOfBirth DATE
);
DROP INDEX idx IF EXISTS;
CREATE INDEX idx ON Athlete (athleteId);
DROP TABLE Run IF EXISTS;
CREATE TABLE Run (
runId INTEGER GENERATED BY DEFAULT AS IDENTITY,
distance INTEGER, -- in meters
time INTEGER, -- in seconds
venue VARCHAR(64),
when TIMESTAMP,
fkAthlete INTEGER,
CONSTRAINT fk FOREIGN KEY (fkAthlete)
REFERENCES Athlete (athleteId) ON DELETE CASCADE
);
DROP VIEW AthleteRun IF EXISTS;
CREATE VIEW AthleteRun AS
SELECT * FROM Athlete LEFT OUTER JOIN Run
ON fkAthlete=athleteId;
'''
db.execute DLL_WITH_VIEW
Topics
• groovy.sql.Sql
• Connecting to a database
• Writing to a database
• Reading from a database
• Stored Procedures
• Advanced Reading/Writing
• groovy.sql.DataSet
 MongoDB
• Neo4j
MongoDB…
31
@Grab('com.gmongo:gmongo:1.3')
import com.gmongo.GMongo
import com.mongodb.util.JSON
import groovy.transform.Field
@Field db = new GMongo().getDB('athletes')
db.athletes.drop()
db.athletes << [first: 'Paul', last: 'Tergat', dob: '1969-06-17', runs: [
[distance: 42195, time: 2 * 60 * 60 + 4 * 60 + 55,
venue: 'Berlin', when: '2003-09-28']
]]
def insertAthlete(first, last, dob) {
db.athletes << [first: first, last: last, dob: dob]
}
…MongoDB…
32
def insertRun(h, m, s, venue, date, lastname) {
db.athletes.update(
[last: lastname],
[$addToSet: [runs: [distance: 42195,
time: h * 60 * 60 + m * 60 + s,
venue: venue, when: date]]]
)
}
insertAthlete('Khalid', 'Khannouchi', '1971-12-22')
insertAthlete('Ronaldo', 'da Costa', '1970-06-07')
insertRun(2, 5, 38, 'London', '2002-04-14', 'Khannouchi')
insertRun(2, 5, 42, 'Chicago', '1999-10-24', 'Khannouchi')
insertRun(2, 6, 05, 'Berlin', '1998-09-20', 'da Costa')
…MongoDB…
33
def radcliffe = """{
first: 'Paula',
last: 'Radcliffe',
dob: '1973-12-17',
runs: [
{distance: 42195, time: ${2 * 60 * 60 + 15 * 60 + 25},
venue: 'London', when: '2003-04-13'}
]
}"""
db.athletes << JSON.parse(radcliffe)
assert db.athletes.count == 4
db.athletes.find().each {
println "$it._id $it.last ${it.runs.size()}"
}
516b15fc2b10a15fa09331f2 Tergat 1
516b15fc2b10a15fa09331f3 Khannouchi 2
516b15fc2b10a15fa09331f4 da Costa 1
516b15fc2b10a15fa09331f5 Radcliffe 1
…MongoDB
34
def londonAthletes = db.athletes.find('runs.venue': 'London')*.first
assert londonAthletes == ['Khalid', 'Paula']
def youngAthletes = db.athletes.aggregate(
[$project: [first: 1, dob: 1]],
[$match: [dob: [$gte: '1970-01-01']]],
[$sort: [dob: -1]]
)
assert youngAthletes.results()*.first == ['Paula', 'Khalid', 'Ronaldo']
Topics
• groovy.sql.Sql
• Connecting to a database
• Writing to a database
• Reading from a database
• Stored Procedures
• Advanced Reading/Writing
• groovy.sql.DataSet
• MongoDB
 Neo4j
Neo4j…
36
…Neo4j…
37
@Grab('org.neo4j:neo4j-kernel:2.1.4')
@Grab('com.tinkerpop.gremlin:gremlin-groovy:2.5.0')
//@Grab('com.tinkerpop.blueprints:blueprints-neo4j-graph:2.5.0')
@Grab('com.tinkerpop.blueprints:blueprints-neo4j-graph:2.5.0;transitive=false')
@Grab('com.tinkerpop.blueprints:blueprints-core:2.5.0')
//@Grab('codehaus-stax:stax:1.1.1')
//@GrabResolver('https://repository.jboss.org/nexus/content/repositories/thirdparty-releases')
@GrabExclude('org.codehaus.groovy:groovy')
import com.tinkerpop.blueprints.Graph
import com.tinkerpop.blueprints.impls.neo4j.Neo4jGraph
//import com.tinkerpop.blueprints.util.io.graphml.GraphMLWriter
import com.tinkerpop.gremlin.groovy.Gremlin
import groovy.transform.Field
import org.neo4j.graphdb.traversal.Evaluators
import org.neo4j.kernel.EmbeddedGraphDatabase
import org.neo4j.graphdb.*
import org.neo4j.kernel.Traversal
import org.neo4j.kernel.Uniqueness
@Field graphDb = new EmbeddedGraphDatabase("athletes")
enum MyRelationshipTypes implements RelationshipType { ran, supercedes }
…Neo4j…
38
// some optional metaclass syntactic sugar
EmbeddedGraphDatabase.metaClass {
createNode { Map properties ->
def n = delegate.createNode()
properties.each { k, v -> n[k] = v }
n
}
}
Node.metaClass {
propertyMissing { String name, val -> delegate.setProperty(name, val) }
propertyMissing { String name -> delegate.getProperty(name) }
methodMissing { String name, args ->
delegate.createRelationshipTo(args[0], MyRelationshipTypes."$name")
}
}
Relationship.metaClass {
propertyMissing { String name, val -> delegate.setProperty(name, val) }
propertyMissing { String name -> delegate.getProperty(name) }
}
…Neo4j…
39
def insertAthlete(first, last, dob) {
graphDb.createNode(first: first, last: last, dob: dob)
}
def insertRecord(h, m, s, venue, when, athlete) {
def record = graphDb.createNode(distance: 42195,
time: h * 60 * 60 + m * 60 + s, venue: venue, when: when)
athlete.won(record)
record
}
Gremlin.load()
def tx = graphDb.beginTx()
def athlete1, athlete2, athlete3, athlete4
def marathon1, marathon2a, marathon2b, marathon3, marathon4a, marathon4b
…Neo4j…
40
try {
athlete1 = graphDb.createNode()
athlete1.first = 'Paul'
athlete1.last = 'Tergat'
athlete1.dob = '1969-06-17'
marathon1 = graphDb.createNode()
marathon1.distance = 42195
marathon1.time = 2 * 60 * 60 + 4 * 60 + 55
marathon1.venue = 'Berlin'
marathon1.when = '2003-09-28'
athlete1.won(marathon1)
athlete2 = insertAthlete('Khalid', 'Khannouchi', '1971-12-22')
marathon2a = insertRecord(2, 5, 38, 'London', '2002-04-14', athlete2)
marathon2b = insertRecord(2, 5, 42, 'Chicago', '1999-10-24', athlete2)
athlete3 = insertAthlete('Ronaldo', 'da Costa', '1970-06-07')
marathon3 = insertRecord(2, 6, 5, 'Berlin', '1998-09-20', athlete3)
athlete4 = insertAthlete('Paula', 'Radcliffe', '1973-12-17')
marathon4a = insertRecord(2, 17, 18, 'Chicago', '2002-10-13', athlete4)
marathon4b = insertRecord(2, 15, 25, 'London', '2003-04-13', athlete4)
…Neo4j…
41
def venue = marathon1.venue
def when = marathon1.when
println "$athlete1.first $athlete1.last won the $venue marathon on $when"
def allAthletes = [athlete1, athlete2, athlete3, athlete4]
def wonInLondon = allAthletes.findAll { athlete ->
athlete.getRelationships(MyRelationshipTypes.won).any { record ->
record.getOtherNode(athlete).venue == 'London'
}
}
assert wonInLondon*.last == ['Khannouchi', 'Radcliffe']
marathon2b.supercedes(marathon3)
marathon2a.supercedes(marathon2b)
marathon1.supercedes(marathon2a)
marathon4b.supercedes(marathon4a)
…Neo4j…
42
println "World records following $marathon3.venue $marathon3.when:"
def t = new Traversal()
for (Path p in t.description().breadthFirst().
relationships(MyRelationshipTypes.supercedes).
evaluator(Evaluators.fromDepth(1)).
uniqueness(Uniqueness.NONE).
traverse(marathon3)) {
def newRecord = p.endNode()
println "$newRecord.venue $newRecord.when"
}
Graph g = new Neo4jGraph(graphDb)
def pretty = { it.collect { "$it.venue $it.when" }.join(', ') }
def results = []
g.V('venue', 'London').fill(results)
println 'London world records: ' + pretty(results)
results = []
g.V('venue', 'London').in('supercedes').fill(results)
println 'World records after London: ' + pretty(results)
…Neo4j
43
results = []
def emitAll = { true }
def forever = { true }
def berlin98 = { it.venue == 'Berlin' && it.when.startsWith('1998') }
g.V.filter(berlin98).in('supercedes').loop(1, forever, emitAll).fill(results)
println 'World records after Berlin 1998: ' + pretty(results)
// def writer = new GraphMLWriter(g)
// def out = new FileOutputStream("c:/temp/athletes.graphml")
// writer.outputGraph(out)
// writer.setNormalize(true)
// out.close()
tx.success()
} finally {
tx.finish()
graphDb.shutdown()
}
More Information: Groovy in Action, 2ed
44

More Related Content

What's hot

Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksMongoDB
 
BDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
BDD - Behavior Driven Development Webapps mit Groovy Spock und GebBDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
BDD - Behavior Driven Development Webapps mit Groovy Spock und GebChristian Baranowski
 
Python dictionary : past, present, future
Python dictionary: past, present, futurePython dictionary: past, present, future
Python dictionary : past, present, futuredelimitry
 
What I learned from Seven Languages in Seven Weeks (IPRUG)
What I learned from Seven Languages in Seven Weeks (IPRUG)What I learned from Seven Languages in Seven Weeks (IPRUG)
What I learned from Seven Languages in Seven Weeks (IPRUG)Kerry Buckley
 
JPoint 2016 - Валеев Тагир - Странности Stream API
JPoint 2016 - Валеев Тагир - Странности Stream APIJPoint 2016 - Валеев Тагир - Странности Stream API
JPoint 2016 - Валеев Тагир - Странности Stream APItvaleev
 
Implementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with SlickImplementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with SlickHermann Hueck
 
Python 표준 라이브러리
Python 표준 라이브러리Python 표준 라이브러리
Python 표준 라이브러리용 최
 
Обзор фреймворка Twisted
Обзор фреймворка TwistedОбзор фреймворка Twisted
Обзор фреймворка TwistedMaxim Kulsha
 
Programming Java - Lection 07 - Puzzlers - Lavrentyev Fedor
Programming Java - Lection 07 - Puzzlers - Lavrentyev FedorProgramming Java - Lection 07 - Puzzlers - Lavrentyev Fedor
Programming Java - Lection 07 - Puzzlers - Lavrentyev FedorFedor Lavrentyev
 
Optimizing Slow Queries with Indexes and Creativity
Optimizing Slow Queries with Indexes and CreativityOptimizing Slow Queries with Indexes and Creativity
Optimizing Slow Queries with Indexes and CreativityMongoDB
 
엘라스틱서치 적합성 이해하기 20160630
엘라스틱서치 적합성 이해하기 20160630엘라스틱서치 적합성 이해하기 20160630
엘라스틱서치 적합성 이해하기 20160630Yong Joon Moon
 
JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 SpringKiyotaka Oku
 
Building DSLs with Groovy
Building DSLs with GroovyBuilding DSLs with Groovy
Building DSLs with GroovySten Anderson
 
Understanding Source Code Differences by Separating Refactoring Effects
Understanding Source Code Differences by Separating Refactoring EffectsUnderstanding Source Code Differences by Separating Refactoring Effects
Understanding Source Code Differences by Separating Refactoring EffectsShinpei Hayashi
 

What's hot (20)

Five
FiveFive
Five
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
 
BDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
BDD - Behavior Driven Development Webapps mit Groovy Spock und GebBDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
BDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
 
Scala introduction
Scala introductionScala introduction
Scala introduction
 
Python dictionary : past, present, future
Python dictionary: past, present, futurePython dictionary: past, present, future
Python dictionary : past, present, future
 
What I learned from Seven Languages in Seven Weeks (IPRUG)
What I learned from Seven Languages in Seven Weeks (IPRUG)What I learned from Seven Languages in Seven Weeks (IPRUG)
What I learned from Seven Languages in Seven Weeks (IPRUG)
 
Poly-paradigm Java
Poly-paradigm JavaPoly-paradigm Java
Poly-paradigm Java
 
JPoint 2016 - Валеев Тагир - Странности Stream API
JPoint 2016 - Валеев Тагир - Странности Stream APIJPoint 2016 - Валеев Тагир - Странности Stream API
JPoint 2016 - Валеев Тагир - Странности Stream API
 
WOTC_Import
WOTC_ImportWOTC_Import
WOTC_Import
 
Implementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with SlickImplementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with Slick
 
Functional es6
Functional es6Functional es6
Functional es6
 
Python 표준 라이브러리
Python 표준 라이브러리Python 표준 라이브러리
Python 표준 라이브러리
 
Обзор фреймворка Twisted
Обзор фреймворка TwistedОбзор фреймворка Twisted
Обзор фреймворка Twisted
 
Why Learn Python?
Why Learn Python?Why Learn Python?
Why Learn Python?
 
Programming Java - Lection 07 - Puzzlers - Lavrentyev Fedor
Programming Java - Lection 07 - Puzzlers - Lavrentyev FedorProgramming Java - Lection 07 - Puzzlers - Lavrentyev Fedor
Programming Java - Lection 07 - Puzzlers - Lavrentyev Fedor
 
Optimizing Slow Queries with Indexes and Creativity
Optimizing Slow Queries with Indexes and CreativityOptimizing Slow Queries with Indexes and Creativity
Optimizing Slow Queries with Indexes and Creativity
 
엘라스틱서치 적합성 이해하기 20160630
엘라스틱서치 적합성 이해하기 20160630엘라스틱서치 적합성 이해하기 20160630
엘라스틱서치 적합성 이해하기 20160630
 
JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 Spring
 
Building DSLs with Groovy
Building DSLs with GroovyBuilding DSLs with Groovy
Building DSLs with Groovy
 
Understanding Source Code Differences by Separating Refactoring Effects
Understanding Source Code Differences by Separating Refactoring EffectsUnderstanding Source Code Differences by Separating Refactoring Effects
Understanding Source Code Differences by Separating Refactoring Effects
 

Viewers also liked

awesome groovy
awesome groovyawesome groovy
awesome groovyPaul King
 
groovy transforms
groovy transformsgroovy transforms
groovy transformsPaul King
 
Make Your Testing Groovy
Make Your Testing GroovyMake Your Testing Groovy
Make Your Testing GroovyPaul King
 
concurrency gpars
concurrency gparsconcurrency gpars
concurrency gparsPaul King
 
Vert.X like Node.js but polyglot and reactive on JVM
Vert.X like Node.js but polyglot and reactive on JVMVert.X like Node.js but polyglot and reactive on JVM
Vert.X like Node.js but polyglot and reactive on JVMMassimiliano Dessì
 
Oredev 2015 - Taming Java Agents
Oredev 2015 - Taming Java AgentsOredev 2015 - Taming Java Agents
Oredev 2015 - Taming Java AgentsAnton Arhipov
 
Patterns of Cloud Native Architecture
Patterns of Cloud Native ArchitecturePatterns of Cloud Native Architecture
Patterns of Cloud Native ArchitectureAndrew Shafer
 
groovy rules
groovy rulesgroovy rules
groovy rulesPaul King
 

Viewers also liked (8)

awesome groovy
awesome groovyawesome groovy
awesome groovy
 
groovy transforms
groovy transformsgroovy transforms
groovy transforms
 
Make Your Testing Groovy
Make Your Testing GroovyMake Your Testing Groovy
Make Your Testing Groovy
 
concurrency gpars
concurrency gparsconcurrency gpars
concurrency gpars
 
Vert.X like Node.js but polyglot and reactive on JVM
Vert.X like Node.js but polyglot and reactive on JVMVert.X like Node.js but polyglot and reactive on JVM
Vert.X like Node.js but polyglot and reactive on JVM
 
Oredev 2015 - Taming Java Agents
Oredev 2015 - Taming Java AgentsOredev 2015 - Taming Java Agents
Oredev 2015 - Taming Java Agents
 
Patterns of Cloud Native Architecture
Patterns of Cloud Native ArchitecturePatterns of Cloud Native Architecture
Patterns of Cloud Native Architecture
 
groovy rules
groovy rulesgroovy rules
groovy rules
 

Similar to groovy databases

Ruby on Rails: Tasty Burgers
Ruby on Rails: Tasty BurgersRuby on Rails: Tasty Burgers
Ruby on Rails: Tasty BurgersAaron Patterson
 
MySQL flexible schema and JSON for Internet of Things
MySQL flexible schema and JSON for Internet of ThingsMySQL flexible schema and JSON for Internet of Things
MySQL flexible schema and JSON for Internet of ThingsAlexander Rubin
 
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)MongoSF
 
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+ConFoo
 
Imugi: Compiler made with Python
Imugi: Compiler made with PythonImugi: Compiler made with Python
Imugi: Compiler made with PythonHan Lee
 
ES6 patterns in the wild
ES6 patterns in the wildES6 patterns in the wild
ES6 patterns in the wildJoe Morgan
 
NoSQL and JavaScript: a Love Story
NoSQL and JavaScript: a Love StoryNoSQL and JavaScript: a Love Story
NoSQL and JavaScript: a Love StoryAlexandre Morgaut
 
Notes for SQLite3 Usage
Notes for SQLite3 UsageNotes for SQLite3 Usage
Notes for SQLite3 UsageWilliam Lee
 
JS Fest 2019 Node.js Antipatterns
JS Fest 2019 Node.js AntipatternsJS Fest 2019 Node.js Antipatterns
JS Fest 2019 Node.js AntipatternsTimur Shemsedinov
 
The Ring programming language version 1.9 book - Part 53 of 210
The Ring programming language version 1.9 book - Part 53 of 210The Ring programming language version 1.9 book - Part 53 of 210
The Ring programming language version 1.9 book - Part 53 of 210Mahmoud Samir Fayed
 
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6Dmitry Soshnikov
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with ClojureDmitry Buzdin
 
The Ring programming language version 1.4.1 book - Part 13 of 31
The Ring programming language version 1.4.1 book - Part 13 of 31The Ring programming language version 1.4.1 book - Part 13 of 31
The Ring programming language version 1.4.1 book - Part 13 of 31Mahmoud Samir Fayed
 
Utilizing Powerful Extensions for Analytics and Operations
Utilizing Powerful Extensions for Analytics and OperationsUtilizing Powerful Extensions for Analytics and Operations
Utilizing Powerful Extensions for Analytics and OperationsNeo4j
 
node.js and the AR.Drone: building a real-time dashboard using socket.io
node.js and the AR.Drone: building a real-time dashboard using socket.ionode.js and the AR.Drone: building a real-time dashboard using socket.io
node.js and the AR.Drone: building a real-time dashboard using socket.ioSteven Beeckman
 
Owasp Indy Q2 2012 Advanced SQLi
Owasp Indy Q2 2012 Advanced SQLiOwasp Indy Q2 2012 Advanced SQLi
Owasp Indy Q2 2012 Advanced SQLiowaspindy
 
From mysql to MongoDB(MongoDB2011北京交流会)
From mysql to MongoDB(MongoDB2011北京交流会)From mysql to MongoDB(MongoDB2011北京交流会)
From mysql to MongoDB(MongoDB2011北京交流会)Night Sailer
 

Similar to groovy databases (20)

3 database-jdbc(1)
3 database-jdbc(1)3 database-jdbc(1)
3 database-jdbc(1)
 
Ruby on Rails: Tasty Burgers
Ruby on Rails: Tasty BurgersRuby on Rails: Tasty Burgers
Ruby on Rails: Tasty Burgers
 
MySQL flexible schema and JSON for Internet of Things
MySQL flexible schema and JSON for Internet of ThingsMySQL flexible schema and JSON for Internet of Things
MySQL flexible schema and JSON for Internet of Things
 
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
 
Having Fun Programming!
Having Fun Programming!Having Fun Programming!
Having Fun Programming!
 
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
 
Imugi: Compiler made with Python
Imugi: Compiler made with PythonImugi: Compiler made with Python
Imugi: Compiler made with Python
 
ES6 patterns in the wild
ES6 patterns in the wildES6 patterns in the wild
ES6 patterns in the wild
 
NoSQL and JavaScript: a Love Story
NoSQL and JavaScript: a Love StoryNoSQL and JavaScript: a Love Story
NoSQL and JavaScript: a Love Story
 
Notes for SQLite3 Usage
Notes for SQLite3 UsageNotes for SQLite3 Usage
Notes for SQLite3 Usage
 
JS Fest 2019 Node.js Antipatterns
JS Fest 2019 Node.js AntipatternsJS Fest 2019 Node.js Antipatterns
JS Fest 2019 Node.js Antipatterns
 
The Ring programming language version 1.9 book - Part 53 of 210
The Ring programming language version 1.9 book - Part 53 of 210The Ring programming language version 1.9 book - Part 53 of 210
The Ring programming language version 1.9 book - Part 53 of 210
 
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with Clojure
 
The Ring programming language version 1.4.1 book - Part 13 of 31
The Ring programming language version 1.4.1 book - Part 13 of 31The Ring programming language version 1.4.1 book - Part 13 of 31
The Ring programming language version 1.4.1 book - Part 13 of 31
 
Utilizing Powerful Extensions for Analytics and Operations
Utilizing Powerful Extensions for Analytics and OperationsUtilizing Powerful Extensions for Analytics and Operations
Utilizing Powerful Extensions for Analytics and Operations
 
node.js and the AR.Drone: building a real-time dashboard using socket.io
node.js and the AR.Drone: building a real-time dashboard using socket.ionode.js and the AR.Drone: building a real-time dashboard using socket.io
node.js and the AR.Drone: building a real-time dashboard using socket.io
 
greenDAO
greenDAOgreenDAO
greenDAO
 
Owasp Indy Q2 2012 Advanced SQLi
Owasp Indy Q2 2012 Advanced SQLiOwasp Indy Q2 2012 Advanced SQLi
Owasp Indy Q2 2012 Advanced SQLi
 
From mysql to MongoDB(MongoDB2011北京交流会)
From mysql to MongoDB(MongoDB2011北京交流会)From mysql to MongoDB(MongoDB2011北京交流会)
From mysql to MongoDB(MongoDB2011北京交流会)
 

More from Paul King

tictactoe groovy
tictactoe groovytictactoe groovy
tictactoe groovyPaul King
 
functional groovy
functional groovyfunctional groovy
functional groovyPaul King
 
Make Testing Groovy
Make Testing GroovyMake Testing Groovy
Make Testing GroovyPaul King
 
Agile Testing Practices
Agile Testing PracticesAgile Testing Practices
Agile Testing PracticesPaul King
 
groovy DSLs from beginner to expert
groovy DSLs from beginner to expertgroovy DSLs from beginner to expert
groovy DSLs from beginner to expertPaul King
 
groovy and concurrency
groovy and concurrencygroovy and concurrency
groovy and concurrencyPaul King
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy PluginsPaul King
 
Dynamic Language Practices
Dynamic Language PracticesDynamic Language Practices
Dynamic Language PracticesPaul King
 
Make Your Builds More Groovy
Make Your Builds More GroovyMake Your Builds More Groovy
Make Your Builds More GroovyPaul King
 
Groovy Power Features
Groovy Power FeaturesGroovy Power Features
Groovy Power FeaturesPaul King
 
Groovy Testing Sep2009
Groovy Testing Sep2009Groovy Testing Sep2009
Groovy Testing Sep2009Paul King
 
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...Paul King
 
Groovy Tutorial
Groovy TutorialGroovy Tutorial
Groovy TutorialPaul King
 
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Paul King
 
XML and Web Services with Groovy
XML and Web Services with GroovyXML and Web Services with Groovy
XML and Web Services with GroovyPaul King
 

More from Paul King (16)

tictactoe groovy
tictactoe groovytictactoe groovy
tictactoe groovy
 
functional groovy
functional groovyfunctional groovy
functional groovy
 
Make Testing Groovy
Make Testing GroovyMake Testing Groovy
Make Testing Groovy
 
Agile Testing Practices
Agile Testing PracticesAgile Testing Practices
Agile Testing Practices
 
groovy DSLs from beginner to expert
groovy DSLs from beginner to expertgroovy DSLs from beginner to expert
groovy DSLs from beginner to expert
 
groovy and concurrency
groovy and concurrencygroovy and concurrency
groovy and concurrency
 
GroovyDSLs
GroovyDSLsGroovyDSLs
GroovyDSLs
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy Plugins
 
Dynamic Language Practices
Dynamic Language PracticesDynamic Language Practices
Dynamic Language Practices
 
Make Your Builds More Groovy
Make Your Builds More GroovyMake Your Builds More Groovy
Make Your Builds More Groovy
 
Groovy Power Features
Groovy Power FeaturesGroovy Power Features
Groovy Power Features
 
Groovy Testing Sep2009
Groovy Testing Sep2009Groovy Testing Sep2009
Groovy Testing Sep2009
 
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...Craig Smith & Paul King   Agile Tool Hacking   Taking Your Agile Development ...
Craig Smith & Paul King Agile Tool Hacking Taking Your Agile Development ...
 
Groovy Tutorial
Groovy TutorialGroovy Tutorial
Groovy Tutorial
 
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
 
XML and Web Services with Groovy
XML and Web Services with GroovyXML and Web Services with Groovy
XML and Web Services with Groovy
 

Recently uploaded

Leveling Up your Branding and Mastering MERN: Fullstack WebDev
Leveling Up your Branding and Mastering MERN: Fullstack WebDevLeveling Up your Branding and Mastering MERN: Fullstack WebDev
Leveling Up your Branding and Mastering MERN: Fullstack WebDevpmgdscunsri
 
03.2024_North America VMUG Optimizing RevOps using the power of ChatGPT in Ma...
03.2024_North America VMUG Optimizing RevOps using the power of ChatGPT in Ma...03.2024_North America VMUG Optimizing RevOps using the power of ChatGPT in Ma...
03.2024_North America VMUG Optimizing RevOps using the power of ChatGPT in Ma...jackiepotts6
 
Boost Efficiency: Sabre API Integration Made Easy
Boost Efficiency: Sabre API Integration Made EasyBoost Efficiency: Sabre API Integration Made Easy
Boost Efficiency: Sabre API Integration Made Easymichealwillson701
 
Unlocking AI: Navigating Open Source vs. Commercial Frontiers
Unlocking AI:Navigating Open Source vs. Commercial FrontiersUnlocking AI:Navigating Open Source vs. Commercial Frontiers
Unlocking AI: Navigating Open Source vs. Commercial FrontiersRaphaël Semeteys
 
Splashtop Enterprise Brochure - Remote Computer Access and Remote Support Sof...
Splashtop Enterprise Brochure - Remote Computer Access and Remote Support Sof...Splashtop Enterprise Brochure - Remote Computer Access and Remote Support Sof...
Splashtop Enterprise Brochure - Remote Computer Access and Remote Support Sof...Splashtop Inc
 
Mobile App Development process | Expert Tips
Mobile App Development process | Expert TipsMobile App Development process | Expert Tips
Mobile App Development process | Expert Tipsmichealwillson701
 
Unlocking the Power of IoT: A comprehensive approach to real-time insights
Unlocking the Power of IoT: A comprehensive approach to real-time insightsUnlocking the Power of IoT: A comprehensive approach to real-time insights
Unlocking the Power of IoT: A comprehensive approach to real-time insightsconfluent
 
Einstein Copilot Conversational AI for your CRM.pdf
Einstein Copilot Conversational AI for your CRM.pdfEinstein Copilot Conversational AI for your CRM.pdf
Einstein Copilot Conversational AI for your CRM.pdfCloudMetic
 
Mobile App Development company Houston
Mobile  App  Development  company HoustonMobile  App  Development  company Houston
Mobile App Development company Houstonjennysmithusa549
 
User Experience Designer | Kaylee Miller Resume
User Experience Designer | Kaylee Miller ResumeUser Experience Designer | Kaylee Miller Resume
User Experience Designer | Kaylee Miller ResumeKaylee Miller
 
VuNet software organisation powerpoint deck
VuNet software organisation powerpoint deckVuNet software organisation powerpoint deck
VuNet software organisation powerpoint deckNaval Singh
 
openEuler Community Overview - a presentation showing the current scale
openEuler Community Overview - a presentation showing the current scaleopenEuler Community Overview - a presentation showing the current scale
openEuler Community Overview - a presentation showing the current scaleShane Coughlan
 
CYBER SECURITY AND CYBER CRIME COMPLETE GUIDE.pLptx
CYBER SECURITY AND CYBER CRIME COMPLETE GUIDE.pLptxCYBER SECURITY AND CYBER CRIME COMPLETE GUIDE.pLptx
CYBER SECURITY AND CYBER CRIME COMPLETE GUIDE.pLptxBarakaMuyengi
 
BusinessGPT - SECURITY AND GOVERNANCE FOR GENERATIVE AI.pptx
BusinessGPT  - SECURITY AND GOVERNANCE  FOR GENERATIVE AI.pptxBusinessGPT  - SECURITY AND GOVERNANCE  FOR GENERATIVE AI.pptx
BusinessGPT - SECURITY AND GOVERNANCE FOR GENERATIVE AI.pptxAGATSoftware
 
Large Scale Architecture -- The Unreasonable Effectiveness of Simplicity
Large Scale Architecture -- The Unreasonable Effectiveness of SimplicityLarge Scale Architecture -- The Unreasonable Effectiveness of Simplicity
Large Scale Architecture -- The Unreasonable Effectiveness of SimplicityRandy Shoup
 
8 key point on optimizing web hosting services in your business.pdf
8 key point on optimizing web hosting services in your business.pdf8 key point on optimizing web hosting services in your business.pdf
8 key point on optimizing web hosting services in your business.pdfOffsiteNOC
 
Technical improvements. Reasons. Methods. Estimations. CJ
Technical improvements.  Reasons. Methods. Estimations. CJTechnical improvements.  Reasons. Methods. Estimations. CJ
Technical improvements. Reasons. Methods. Estimations. CJpolinaucc
 
Take Advantage of Mx Tracking Flight Scheduling Solutions to Streamline Your ...
Take Advantage of Mx Tracking Flight Scheduling Solutions to Streamline Your ...Take Advantage of Mx Tracking Flight Scheduling Solutions to Streamline Your ...
Take Advantage of Mx Tracking Flight Scheduling Solutions to Streamline Your ...MyFAA
 
8 Steps to Build a LangChain RAG Chatbot.
8 Steps to Build a LangChain RAG Chatbot.8 Steps to Build a LangChain RAG Chatbot.
8 Steps to Build a LangChain RAG Chatbot.Ritesh Kanjee
 

Recently uploaded (20)

20140812 - OBD2 Solution
20140812 - OBD2 Solution20140812 - OBD2 Solution
20140812 - OBD2 Solution
 
Leveling Up your Branding and Mastering MERN: Fullstack WebDev
Leveling Up your Branding and Mastering MERN: Fullstack WebDevLeveling Up your Branding and Mastering MERN: Fullstack WebDev
Leveling Up your Branding and Mastering MERN: Fullstack WebDev
 
03.2024_North America VMUG Optimizing RevOps using the power of ChatGPT in Ma...
03.2024_North America VMUG Optimizing RevOps using the power of ChatGPT in Ma...03.2024_North America VMUG Optimizing RevOps using the power of ChatGPT in Ma...
03.2024_North America VMUG Optimizing RevOps using the power of ChatGPT in Ma...
 
Boost Efficiency: Sabre API Integration Made Easy
Boost Efficiency: Sabre API Integration Made EasyBoost Efficiency: Sabre API Integration Made Easy
Boost Efficiency: Sabre API Integration Made Easy
 
Unlocking AI: Navigating Open Source vs. Commercial Frontiers
Unlocking AI:Navigating Open Source vs. Commercial FrontiersUnlocking AI:Navigating Open Source vs. Commercial Frontiers
Unlocking AI: Navigating Open Source vs. Commercial Frontiers
 
Splashtop Enterprise Brochure - Remote Computer Access and Remote Support Sof...
Splashtop Enterprise Brochure - Remote Computer Access and Remote Support Sof...Splashtop Enterprise Brochure - Remote Computer Access and Remote Support Sof...
Splashtop Enterprise Brochure - Remote Computer Access and Remote Support Sof...
 
Mobile App Development process | Expert Tips
Mobile App Development process | Expert TipsMobile App Development process | Expert Tips
Mobile App Development process | Expert Tips
 
Unlocking the Power of IoT: A comprehensive approach to real-time insights
Unlocking the Power of IoT: A comprehensive approach to real-time insightsUnlocking the Power of IoT: A comprehensive approach to real-time insights
Unlocking the Power of IoT: A comprehensive approach to real-time insights
 
Einstein Copilot Conversational AI for your CRM.pdf
Einstein Copilot Conversational AI for your CRM.pdfEinstein Copilot Conversational AI for your CRM.pdf
Einstein Copilot Conversational AI for your CRM.pdf
 
Mobile App Development company Houston
Mobile  App  Development  company HoustonMobile  App  Development  company Houston
Mobile App Development company Houston
 
User Experience Designer | Kaylee Miller Resume
User Experience Designer | Kaylee Miller ResumeUser Experience Designer | Kaylee Miller Resume
User Experience Designer | Kaylee Miller Resume
 
VuNet software organisation powerpoint deck
VuNet software organisation powerpoint deckVuNet software organisation powerpoint deck
VuNet software organisation powerpoint deck
 
openEuler Community Overview - a presentation showing the current scale
openEuler Community Overview - a presentation showing the current scaleopenEuler Community Overview - a presentation showing the current scale
openEuler Community Overview - a presentation showing the current scale
 
CYBER SECURITY AND CYBER CRIME COMPLETE GUIDE.pLptx
CYBER SECURITY AND CYBER CRIME COMPLETE GUIDE.pLptxCYBER SECURITY AND CYBER CRIME COMPLETE GUIDE.pLptx
CYBER SECURITY AND CYBER CRIME COMPLETE GUIDE.pLptx
 
BusinessGPT - SECURITY AND GOVERNANCE FOR GENERATIVE AI.pptx
BusinessGPT  - SECURITY AND GOVERNANCE  FOR GENERATIVE AI.pptxBusinessGPT  - SECURITY AND GOVERNANCE  FOR GENERATIVE AI.pptx
BusinessGPT - SECURITY AND GOVERNANCE FOR GENERATIVE AI.pptx
 
Large Scale Architecture -- The Unreasonable Effectiveness of Simplicity
Large Scale Architecture -- The Unreasonable Effectiveness of SimplicityLarge Scale Architecture -- The Unreasonable Effectiveness of Simplicity
Large Scale Architecture -- The Unreasonable Effectiveness of Simplicity
 
8 key point on optimizing web hosting services in your business.pdf
8 key point on optimizing web hosting services in your business.pdf8 key point on optimizing web hosting services in your business.pdf
8 key point on optimizing web hosting services in your business.pdf
 
Technical improvements. Reasons. Methods. Estimations. CJ
Technical improvements.  Reasons. Methods. Estimations. CJTechnical improvements.  Reasons. Methods. Estimations. CJ
Technical improvements. Reasons. Methods. Estimations. CJ
 
Take Advantage of Mx Tracking Flight Scheduling Solutions to Streamline Your ...
Take Advantage of Mx Tracking Flight Scheduling Solutions to Streamline Your ...Take Advantage of Mx Tracking Flight Scheduling Solutions to Streamline Your ...
Take Advantage of Mx Tracking Flight Scheduling Solutions to Streamline Your ...
 
8 Steps to Build a LangChain RAG Chatbot.
8 Steps to Build a LangChain RAG Chatbot.8 Steps to Build a LangChain RAG Chatbot.
8 Steps to Build a LangChain RAG Chatbot.
 

groovy databases

  • 1. Working with Databases and Groovy Dr Paul King Groovy Lead for Object Computing Inc. @paulk_asert http://slideshare.net/paulk_asert/groovy-databases https://github.com/paulk-asert/groovy-databases
  • 2. Contents • groovy.sql.Sql • Connecting to a database • Writing to a database • Reading from a database • Stored Procedures • Advanced Reading/Writing • groovy.sql.DataSet • MongoDB • Neo4j 2
  • 4. Connecting to a database… • Sql.newInstance • assumes driver is on the classpath • other newInstance variants available 4 import groovy.sql.Sql def url = 'jdbc:hsqldb:mem:marathon' def user = 'sa' def password = '' def driver = 'org.hsqldb.jdbcDriver' def sql = Sql.newInstance(url, user, password, driver) // use 'sql' instance sql.close()
  • 5. …Connecting to a database… • Some variations 5 Advanced def sql = Sql.newInstance( url: 'jdbc:hsqldb:mem:marathon', user: 'sa', password: '', driver: 'org.hsqldb.jdbcDriver', cacheStatements: true, resultSetConcurrency: CONCUR_READ_ONLY ) Sql.withInstance(url, user, password, driver) { // use 'sql' instance } // no close() required @Grab('org.hsqldb:hsqldb:2.3.3') @GrabConfig(systemClassLoader=true) import groovy.sql.Sql // ...
  • 6. …Connecting to a database… • new Sql() constructor • if you have a DataSource or existing Connection or Sql instance • Likely to be the preferred approach with JDK9 Jigsaw 6 import groovy.sql.Sql import org.hsqldb.jdbc.JDBCDataSource def dataSource = new JDBCDataSource( database: 'jdbc:hsqldb:mem:marathon', user: 'sa', password: '') def sql = new Sql(dataSource) // use 'sql' instance sql.close()
  • 7. …Connecting to a database • new Sql() constructor (cont'd) 7 @Grab('org.hsqldb:hsqldb:2.3.3') @Grab('commons-dbcp:commons-dbcp:1.4') import groovy.sql.Sql import org.apache.commons.dbcp.BasicDataSource def url = 'jdbc:hsqldb:mem:marathon' def driver = 'org.hsqldb.jdbcDriver' def dataSource = new BasicDataSource( driverClassName: driver, url: url, username: 'sa', password: '') def sql = new Sql(dataSource) // use 'sql' instance sql.close() Advanced
  • 8. Writing to a database… 8 def DDL = ''' DROP TABLE Athlete IF EXISTS; CREATE TABLE Athlete ( athleteId INTEGER GENERATED BY DEFAULT AS IDENTITY, firstname VARCHAR(64), lastname VARCHAR(64), dateOfBirth DATE, ); ''' sql.execute(DDL) sql.execute ''' INSERT INTO Athlete (firstname, lastname, dateOfBirth) VALUES ('Paul', 'Tergat', '1969-06-17') '''
  • 9. …Writing to a database 9 def data = [first: 'Khalid', last: 'Khannouchi', birth: '1971-12-22'] sql.execute """ INSERT INTO Athlete (firstname, lastname, dateOfBirth) VALUES (${data.first},${data.last},${data.birth}) """ String athleteInsert = 'INSERT INTO Athlete (firstname, lastname) VALUES (?,?)' def keys = sql.executeInsert athleteInsert, ['Ronaldo', 'da Costa'] assert keys[0] == [2] def rowsUpdated = sql.executeUpdate ''' UPDATE Athlete SET dateOfBirth='1970-06-07' where lastname='da Costa' ''' assert rowsUpdated == 1 sql.execute "delete from Athlete where lastname = 'Tergat'" Advanced
  • 10. Reading from a database… 10 sql.query('SELECT firstname, lastname FROM Athlete') { resultSet -> while (resultSet.next()) { print resultSet.getString(1) print ' ' println resultSet.getString('lastname') } } sql.eachRow('SELECT firstname, lastname FROM Athlete') { row -> println row[0] + ' ' + row.lastname }
  • 11. …Reading from a database 11 def first = sql.firstRow('SELECT lastname, dateOfBirth FROM Athlete') def firstString = first.toMapString().toLowerCase() assert firstString == '[lastname:tergat, dateofbirth:1969-06-17]' List athletes = sql.rows('SELECT firstname, lastname FROM Athlete') println "There are ${athletes.size()} Athletes:" println athletes.collect { "$it.FIRSTNAME ${it[-1]}" }.join(", ") assert sql.firstRow('SELECT COUNT(*) as num FROM Athlete').num == 3
  • 12. Invoking Stored Procedures… 12 def FULL_DLL = ''' DROP TABLE Athlete IF EXISTS; CREATE TABLE Athlete ( athleteId INTEGER GENERATED BY DEFAULT AS IDENTITY, firstname VARCHAR(64), lastname VARCHAR(64), dateOfBirth DATE ); DROP INDEX idx IF EXISTS; CREATE INDEX idx ON Athlete (athleteId); DROP TABLE Run IF EXISTS; CREATE TABLE Run ( runId INTEGER GENERATED BY DEFAULT AS IDENTITY, distance INTEGER, -- in meters time INTEGER, -- in seconds venue VARCHAR(64), when TIMESTAMP, fkAthlete INTEGER, CONSTRAINT fk FOREIGN KEY (fkAthlete) REFERENCES Athlete (athleteId) ON DELETE CASCADE ); ''' sql.execute FULL_DLL Advanced Athlete athleteId firstname lastname dateOfBirth Run runId distance time venue when fkAthlete
  • 13. …Invoking Stored Procedures… • And assume some data has been populated … • We've refactored our previous insert code into some helper methods. 13 insertAthlete('Paul', 'Tergat', '1969-06-17') insertAthlete('Khalid', 'Khannouchi', '1971-12-22') insertAthlete('Ronaldo', 'da Costa', '1970-06-07') insertRun(2, 4, 55, 'Berlin', '2003-09-28', 'Tergat') insertRun(2, 5, 38, 'London', '2002-04-14', 'Khannouchi') insertRun(2, 5, 42, 'Chicago', '1999-10-24', 'Khannouchi') insertRun(2, 6, 05, 'Berlin', '1998-09-20', 'da Costa')
  • 14. …Invoking Stored Procedures… 14 db.execute ''' CREATE FUNCTION SELECT_ATHLETE_RUN () RETURNS TABLE (lastname VARCHAR(64), venue VARCHAR(64), whenRun DATE) READS SQL DATA RETURN TABLE ( select Athlete.lastname, Run.venue, Run.whenRun from Athlete, Run where Athlete.athleteId = Run.fkAthlete order by whenRun ) ''' db.eachRow('CALL SELECT_ATHLETE_RUN()') { println "$it.lastname $it.venue $it.whenRun" } da Costa Berlin 1998-09-20 Khannouchi Chicago 1999-10-24 Khannouchi London 2002-04-14 Tergat Berlin 2003-09-28
  • 15. …Invoking Stored Procedures… 15 db.execute ''' CREATE FUNCTION FULL_NAME (p_lastname VARCHAR(64)) RETURNS VARCHAR(100) READS SQL DATA BEGIN ATOMIC declare ans VARCHAR(100); SELECT CONCAT(firstname, ' ', lastname) INTO ans FROM Athlete WHERE lastname = p_lastname; return ans; END ''' assert db.firstRow("{? = call FULL_NAME(?)}", ['Tergat'])[0] == 'Paul Tergat'
  • 16. …Invoking Stored Procedures 16 db.execute ''' CREATE PROCEDURE CONCAT_NAME (OUT fullname VARCHAR(100), IN first VARCHAR(50), IN last VARCHAR(50)) BEGIN ATOMIC SET fullname = CONCAT(first, ' ', last); END ''' db.call("{call CONCAT_NAME(?, ?, ?)}", [Sql.VARCHAR, 'Paul', 'Tergat']) { fullname -> assert fullname == 'Paul Tergat' }
  • 17. Advanced Reading… • Rowset metadata 17 def dump(sql, tablename) { println " CONTENT OF TABLE ${tablename} ".center(40, '-') sql.eachRow('SELECT * FROM ' + tablename) { rs -> def meta = rs.getMetaData() if (meta.columnCount <= 0) return for (i in 0..<meta.columnCount) { print "${i}: ${meta.getColumnLabel(i + 1)}".padRight(20) // counts from 1 print rs[i]?.toString() // counts from 0 print "n" } println '-' * 40 } }
  • 18. …Advanced Reading… 18 def dump(sql, tablename) { println " CONTENT OF TABLE ${tablename} ".center(40, '-') sql.eachRow('SELECT * FROM ' + tablename) { rs -> def meta = rs.getMetaData() if (meta.columnCount <= 0) return for (i in 0..<meta.columnCount) { print "${i}: ${meta.getColumnLabel(i + 1)}".padRight(20) // counts from 1 print rs[i]?.toString() // counts from 0 print "n" } println '-' * 40 } } ----------------------- CONTENT OF TABLE Athlete ----------------------- ATHLETEID FIRSTNAME LASTNAME DATEOFBIRTH ------------------------------------------------------------------------ 1 Paul Tergat 1969-06-17 2 Khalid Khannouchi 1971-12-22 3 Ronaldo da Costa 1970-06-07
  • 19. …Advanced Reading… 19 def dump2(sql, tablename) { def printColNames = { meta -> def width = meta.columnCount * 18 println " CONTENT OF TABLE ${tablename} ".center(width, '-') (1..meta.columnCount).each { print meta.getColumnLabel(it).padRight(18) } println() println '-' * width } def printRow = { row -> row.toRowResult().values().each { print it.toString().padRight(18) } println() } sql.eachRow('SELECT * FROM ' + tablename, printColNames, printRow) } metadata closure
  • 20. …Advanced Reading • Pagination 20 qry = 'SELECT * FROM Athlete' assert sql.rows(qry, 1, 4)*.lastname == ['Tergat', 'Khannouchi', 'da Costa', 'Gebrselassie'] assert sql.rows(qry, 5, 4)*.lastname == ['Makau', 'Radcliffe', 'Ndereba', 'Takahashi'] assert sql.rows(qry, 9, 4)*.lastname == ['Loroupe', 'Kristiansen']
  • 21. Advanced Writing… • Simple transaction support 21 sql.withTransaction { insertAthlete('Haile', 'Gebrselassie', '1973-04-18') insertAthlete('Patrick', 'Makau', '1985-03-02') } Advanced
  • 22. …Advanced Writing… • Batching of ad-hoc SQL statements 22 sql.execute "delete from Athlete where lastname = 'Tergat'" sql.withBatch { stmt -> stmt.addBatch ''' INSERT INTO Athlete (firstname, lastname, dateOfBirth) VALUES ('Paul', 'Tergat', '1969-06-17')''' stmt.addBatch """ INSERT INTO Run (distance, time, venue, when, fkAthlete) SELECT 42195, ${2*60*60+4*60+55}, 'Berlin', '2003-09-28', athleteId FROM Athlete WHERE lastname='Tergat'""" } //22/04/2013 6:34:59 AM groovy.sql.BatchingStatementWrapper processResult //FINE: Successfully executed batch with 2 command(s)
  • 23. …Advanced Writing… • Batching with a prepared statement 23 def qry = 'INSERT INTO Athlete (firstname, lastname, dateOfBirth) VALUES (?,?,?);' sql.withBatch(3, qry) { ps -> ps.addBatch('Paula', 'Radcliffe', '1973-12-17') ps.addBatch('Catherine', 'Ndereba', '1972-07-21') ps.addBatch('Naoko', 'Takahashi', '1972-05-06') ps.addBatch('Tegla', 'Loroupe', '1973-05-09') ps.addBatch('Ingrid', 'Kristiansen', '1956-03-21') } // If logging is turned on: // 20/04/2013 2:18:10 AM groovy.sql.BatchingStatementWrapper processResult // FINE: Successfully executed batch with 3 command(s) // 20/04/2013 2:18:10 AM groovy.sql.BatchingStatementWrapper processResult // FINE: Successfully executed batch with 2 command(s)
  • 24. …Advanced Writing • Named parameters (two variants) and named-ordinal parameters 24 @Canonical class Athlete { String first, last, dob } def ndereba = new Athlete('Catherine', 'Ndereba', '1972-07-21') def takahashi = new Athlete('Naoko', 'Takahashi') def takahashiExtra = [dob: '1972-05-06'] def loroupe = [first: 'Tegla', last: 'Loroupe', dob: '1973-05-09'] def insertPrefix = 'INSERT INTO Athlete (firstname, lastname, dateOfBirth) VALUES ' sql.execute insertPrefix + ' (?.first,?.last,?.dob)', ndereba sql.execute insertPrefix + ' (?1.first,?1.last,?2.dob)', takahashi, takahashiExtra sql.execute insertPrefix + ' (:first,:last,:dob)', loroupe sql.execute insertPrefix + ' (:first,:last,:dob)', first: 'Ingrid', last: 'Kristiansen', dob: '1956-03-21'
  • 25. Topics • groovy.sql.Sql • Connecting to a database • Writing to a database • Reading from a database • Stored Procedures • Advanced Reading/Writing  groovy.sql.DataSet • MongoDB • Neo4j
  • 26. groovy.sql.DataSet • Allows any table within an RDBMS to appear as if it was a Java-like collection of POGOs • Can be thought of as a poor man's object-relational mapping system • Has some smarts for lazy execution of database queries • Not meant to be as sophisticated as hibernate 26
  • 27. DataSet example… 27 def athletes = sql.dataSet('Athlete') athletes.each { println it.firstname } athletes.add( firstname: 'Paula', lastname: 'Radcliffe', dateOfBirth: '1973-12-17' ) athletes.each { println it.firstname } def runs = sql.dataSet('AthleteRun').findAll { it.firstname == 'Khalid' } runs.each { println "$it.lastname $it.venue" } Paul Khalid Ronaldo Paul Khalid Ronaldo Paula Khannouchi London Khannouchi Chicago
  • 28. …DataSet example 28 def query = athletes.findAll { it.firstname >= 'P' } query = query.findAll { it.dateOfBirth > '1970-01-01' } query = query.sort { it.dateOfBirth } query = query.reverse() println query.sql println query.parameters println query.rows()*.firstname // One SQL query here! select * from Athlete where firstname >= ? and dateOfBirth > ? order by dateOfBirth DESC [P, 1970-01-01] [Paula, Ronaldo]
  • 29. Overcoming groovy.sql.DataSet limitations 29 def DLL_WITH_VIEW = ''' DROP TABLE Athlete IF EXISTS; CREATE TABLE Athlete ( athleteId INTEGER GENERATED BY DEFAULT AS IDENTITY, firstname VARCHAR(64), lastname VARCHAR(64), dateOfBirth DATE ); DROP INDEX idx IF EXISTS; CREATE INDEX idx ON Athlete (athleteId); DROP TABLE Run IF EXISTS; CREATE TABLE Run ( runId INTEGER GENERATED BY DEFAULT AS IDENTITY, distance INTEGER, -- in meters time INTEGER, -- in seconds venue VARCHAR(64), when TIMESTAMP, fkAthlete INTEGER, CONSTRAINT fk FOREIGN KEY (fkAthlete) REFERENCES Athlete (athleteId) ON DELETE CASCADE ); DROP VIEW AthleteRun IF EXISTS; CREATE VIEW AthleteRun AS SELECT * FROM Athlete LEFT OUTER JOIN Run ON fkAthlete=athleteId; ''' db.execute DLL_WITH_VIEW
  • 30. Topics • groovy.sql.Sql • Connecting to a database • Writing to a database • Reading from a database • Stored Procedures • Advanced Reading/Writing • groovy.sql.DataSet  MongoDB • Neo4j
  • 31. MongoDB… 31 @Grab('com.gmongo:gmongo:1.3') import com.gmongo.GMongo import com.mongodb.util.JSON import groovy.transform.Field @Field db = new GMongo().getDB('athletes') db.athletes.drop() db.athletes << [first: 'Paul', last: 'Tergat', dob: '1969-06-17', runs: [ [distance: 42195, time: 2 * 60 * 60 + 4 * 60 + 55, venue: 'Berlin', when: '2003-09-28'] ]] def insertAthlete(first, last, dob) { db.athletes << [first: first, last: last, dob: dob] }
  • 32. …MongoDB… 32 def insertRun(h, m, s, venue, date, lastname) { db.athletes.update( [last: lastname], [$addToSet: [runs: [distance: 42195, time: h * 60 * 60 + m * 60 + s, venue: venue, when: date]]] ) } insertAthlete('Khalid', 'Khannouchi', '1971-12-22') insertAthlete('Ronaldo', 'da Costa', '1970-06-07') insertRun(2, 5, 38, 'London', '2002-04-14', 'Khannouchi') insertRun(2, 5, 42, 'Chicago', '1999-10-24', 'Khannouchi') insertRun(2, 6, 05, 'Berlin', '1998-09-20', 'da Costa')
  • 33. …MongoDB… 33 def radcliffe = """{ first: 'Paula', last: 'Radcliffe', dob: '1973-12-17', runs: [ {distance: 42195, time: ${2 * 60 * 60 + 15 * 60 + 25}, venue: 'London', when: '2003-04-13'} ] }""" db.athletes << JSON.parse(radcliffe) assert db.athletes.count == 4 db.athletes.find().each { println "$it._id $it.last ${it.runs.size()}" } 516b15fc2b10a15fa09331f2 Tergat 1 516b15fc2b10a15fa09331f3 Khannouchi 2 516b15fc2b10a15fa09331f4 da Costa 1 516b15fc2b10a15fa09331f5 Radcliffe 1
  • 34. …MongoDB 34 def londonAthletes = db.athletes.find('runs.venue': 'London')*.first assert londonAthletes == ['Khalid', 'Paula'] def youngAthletes = db.athletes.aggregate( [$project: [first: 1, dob: 1]], [$match: [dob: [$gte: '1970-01-01']]], [$sort: [dob: -1]] ) assert youngAthletes.results()*.first == ['Paula', 'Khalid', 'Ronaldo']
  • 35. Topics • groovy.sql.Sql • Connecting to a database • Writing to a database • Reading from a database • Stored Procedures • Advanced Reading/Writing • groovy.sql.DataSet • MongoDB  Neo4j
  • 37. …Neo4j… 37 @Grab('org.neo4j:neo4j-kernel:2.1.4') @Grab('com.tinkerpop.gremlin:gremlin-groovy:2.5.0') //@Grab('com.tinkerpop.blueprints:blueprints-neo4j-graph:2.5.0') @Grab('com.tinkerpop.blueprints:blueprints-neo4j-graph:2.5.0;transitive=false') @Grab('com.tinkerpop.blueprints:blueprints-core:2.5.0') //@Grab('codehaus-stax:stax:1.1.1') //@GrabResolver('https://repository.jboss.org/nexus/content/repositories/thirdparty-releases') @GrabExclude('org.codehaus.groovy:groovy') import com.tinkerpop.blueprints.Graph import com.tinkerpop.blueprints.impls.neo4j.Neo4jGraph //import com.tinkerpop.blueprints.util.io.graphml.GraphMLWriter import com.tinkerpop.gremlin.groovy.Gremlin import groovy.transform.Field import org.neo4j.graphdb.traversal.Evaluators import org.neo4j.kernel.EmbeddedGraphDatabase import org.neo4j.graphdb.* import org.neo4j.kernel.Traversal import org.neo4j.kernel.Uniqueness @Field graphDb = new EmbeddedGraphDatabase("athletes") enum MyRelationshipTypes implements RelationshipType { ran, supercedes }
  • 38. …Neo4j… 38 // some optional metaclass syntactic sugar EmbeddedGraphDatabase.metaClass { createNode { Map properties -> def n = delegate.createNode() properties.each { k, v -> n[k] = v } n } } Node.metaClass { propertyMissing { String name, val -> delegate.setProperty(name, val) } propertyMissing { String name -> delegate.getProperty(name) } methodMissing { String name, args -> delegate.createRelationshipTo(args[0], MyRelationshipTypes."$name") } } Relationship.metaClass { propertyMissing { String name, val -> delegate.setProperty(name, val) } propertyMissing { String name -> delegate.getProperty(name) } }
  • 39. …Neo4j… 39 def insertAthlete(first, last, dob) { graphDb.createNode(first: first, last: last, dob: dob) } def insertRecord(h, m, s, venue, when, athlete) { def record = graphDb.createNode(distance: 42195, time: h * 60 * 60 + m * 60 + s, venue: venue, when: when) athlete.won(record) record } Gremlin.load() def tx = graphDb.beginTx() def athlete1, athlete2, athlete3, athlete4 def marathon1, marathon2a, marathon2b, marathon3, marathon4a, marathon4b
  • 40. …Neo4j… 40 try { athlete1 = graphDb.createNode() athlete1.first = 'Paul' athlete1.last = 'Tergat' athlete1.dob = '1969-06-17' marathon1 = graphDb.createNode() marathon1.distance = 42195 marathon1.time = 2 * 60 * 60 + 4 * 60 + 55 marathon1.venue = 'Berlin' marathon1.when = '2003-09-28' athlete1.won(marathon1) athlete2 = insertAthlete('Khalid', 'Khannouchi', '1971-12-22') marathon2a = insertRecord(2, 5, 38, 'London', '2002-04-14', athlete2) marathon2b = insertRecord(2, 5, 42, 'Chicago', '1999-10-24', athlete2) athlete3 = insertAthlete('Ronaldo', 'da Costa', '1970-06-07') marathon3 = insertRecord(2, 6, 5, 'Berlin', '1998-09-20', athlete3) athlete4 = insertAthlete('Paula', 'Radcliffe', '1973-12-17') marathon4a = insertRecord(2, 17, 18, 'Chicago', '2002-10-13', athlete4) marathon4b = insertRecord(2, 15, 25, 'London', '2003-04-13', athlete4)
  • 41. …Neo4j… 41 def venue = marathon1.venue def when = marathon1.when println "$athlete1.first $athlete1.last won the $venue marathon on $when" def allAthletes = [athlete1, athlete2, athlete3, athlete4] def wonInLondon = allAthletes.findAll { athlete -> athlete.getRelationships(MyRelationshipTypes.won).any { record -> record.getOtherNode(athlete).venue == 'London' } } assert wonInLondon*.last == ['Khannouchi', 'Radcliffe'] marathon2b.supercedes(marathon3) marathon2a.supercedes(marathon2b) marathon1.supercedes(marathon2a) marathon4b.supercedes(marathon4a)
  • 42. …Neo4j… 42 println "World records following $marathon3.venue $marathon3.when:" def t = new Traversal() for (Path p in t.description().breadthFirst(). relationships(MyRelationshipTypes.supercedes). evaluator(Evaluators.fromDepth(1)). uniqueness(Uniqueness.NONE). traverse(marathon3)) { def newRecord = p.endNode() println "$newRecord.venue $newRecord.when" } Graph g = new Neo4jGraph(graphDb) def pretty = { it.collect { "$it.venue $it.when" }.join(', ') } def results = [] g.V('venue', 'London').fill(results) println 'London world records: ' + pretty(results) results = [] g.V('venue', 'London').in('supercedes').fill(results) println 'World records after London: ' + pretty(results)
  • 43. …Neo4j 43 results = [] def emitAll = { true } def forever = { true } def berlin98 = { it.venue == 'Berlin' && it.when.startsWith('1998') } g.V.filter(berlin98).in('supercedes').loop(1, forever, emitAll).fill(results) println 'World records after Berlin 1998: ' + pretty(results) // def writer = new GraphMLWriter(g) // def out = new FileOutputStream("c:/temp/athletes.graphml") // writer.outputGraph(out) // writer.setNormalize(true) // out.close() tx.success() } finally { tx.finish() graphDb.shutdown() }
  • 44. More Information: Groovy in Action, 2ed 44