A facade over Java's JDBC APIs providing greatly simplified resource management and result set handling with Groovy-specific enhancements.
—
Groovy SQL provides specialized result handling classes that offer Groovy-friendly access to database results. These classes bridge the gap between JDBC's ResultSet interface and Groovy's expressive syntax patterns.
GroovyRowResult is a Map-like wrapper for SQL result rows that provides case-insensitive column access and integrates seamlessly with Groovy's property access syntax.
GroovyRowResult(Map<String, Object> result)Access column values using Groovy property syntax with case-insensitive column names:
Object getProperty(String property)Access columns by their position in the result set:
Object getAt(int index) // Supports negative indices for reverse accessGroovyRowResult implements the full Map interface with case-insensitive key handling:
// Map interface methods
void clear()
boolean containsKey(Object key) // Case-insensitive
boolean containsValue(Object value)
Set<Map.Entry<String, Object>> entrySet()
boolean equals(Object o)
Object get(Object property) // Case-insensitive
int hashCode()
boolean isEmpty()
Set<String> keySet()
Object put(String key, Object value) // Handles case-insensitive replacement
void putAll(Map<? extends String, ?> t)
Object remove(Object rawKey) // Case-insensitive
int size()
Collection<Object> values()GroovyResultSet extends the standard JDBC ResultSet interface with Groovy-specific enhancements for more expressive data access patterns.
interface GroovyResultSet extends ResultSet, GroovyObjectAccess columns using Groovy's array-like syntax:
Object getAt(int index) // Supports negative indices
Object getAt(String columnName) // Case-insensitive column accessUpdate column values using Groovy's assignment syntax:
void putAt(int index, Object newValue)
void putAt(String columnName, Object newValue)Add new rows and iterate using closures:
void add(Map values) // Add new row from map
void eachRow(Closure closure) // Iterate with closureImplementation class that provides the actual functionality for GroovyResultSet:
GroovyResultSetExtension(ResultSet set)Object getProperty(String columnName)
void setProperty(String columnName, Object newValue)Object getAt(int index) // Supports negative indices
void putAt(int index, Object newValue)boolean next()
boolean previous()void add(Map values)
void eachRow(Closure closure)Object invokeMethod(String name, Object args) // Delegates to underlying ResultSetProxy class for creating GroovyResultSet instances:
GroovyResultSetProxy(ResultSet set)
GroovyResultSetProxy(GroovyResultSetExtension ext)GroovyResultSet getImpl() // Get proxy instance
Object invoke(Object proxy, Method method, Object[] args) // InvocationHandler methodWrapper for accessing ResultSetMetaData columns with Groovy syntax:
ResultSetMetaDataWrapper(ResultSetMetaData target, int index)Object invokeMethod(String name, Object args) // Invoke metadata methods with column index
Object getProperty(String property) // Get metadata properties for column
void setProperty(String property, Object newValue) // Read-only (throws exception)def sql = Sql.newInstance("jdbc:h2:mem:test", "sa", "", "org.h2.Driver")
// Different ways to access column values
sql.eachRow("SELECT user_id, first_name, last_name, email FROM users") { row ->
// Property access (case-insensitive)
println "User ID: ${row.user_id}"
println "Name: ${row.first_name} ${row.last_name}"
// Alternative case variations work
println "Email: ${row.EMAIL}" // Works even if column is lowercase
println "Name: ${row.First_Name}" // Works with different casing
// Index access
println "First column: ${row[0]}" // user_id
println "Last column: ${row[-1]}" // email (negative index)
// Map-style access
println "User: ${row.get('first_name')}"
println "Contains email: ${row.containsKey('email')}"
}def sql = Sql.newInstance("jdbc:h2:mem:test", "sa", "", "org.h2.Driver")
// Using GroovyResultSet in query closure
sql.query("SELECT * FROM users WHERE active = ?", [true]) { rs ->
while (rs.next()) {
// Groovy-style column access
def userName = rs['user_name']
def userEmail = rs['email']
// Update values
rs['last_login'] = new Date()
// Access by index (supports negative)
def firstCol = rs[0] // First column
def lastCol = rs[-1] // Last column
println "User: ${userName}, Email: ${userEmail}"
}
}def sql = Sql.newInstance("jdbc:h2:mem:test", "sa", "", "org.h2.Driver")
sql.eachRow("SELECT USER_NAME, USER_EMAIL, CREATED_DATE FROM users") { row ->
// All these work regardless of actual column case
println row.user_name // Works
println row.USER_NAME // Works
println row.User_Name // Works
println row.userName // Works
// Map access is also case-insensitive
println row['user_email'] // Works
println row['USER_EMAIL'] // Works
println row['userEmail'] // Works
}def sql = Sql.newInstance("jdbc:h2:mem:test", "sa", "", "org.h2.Driver")
sql.eachRow("SELECT id, data, created_at FROM complex_table") { row ->
// Handle different data types
def id = row.id as Integer
def jsonData = row.data as String
def timestamp = row.created_at as Timestamp
// Check for null values
if (row.data != null) {
println "Data: ${jsonData}"
}
// Convert to appropriate types
def date = timestamp?.toLocalDateTime()
println "Created: ${date}"
}def sql = Sql.newInstance("jdbc:h2:mem:test", "sa", "", "org.h2.Driver")
sql.eachRow("SELECT * FROM users", { meta ->
// Access metadata using wrapper
def columnCount = meta.columnCount
println "Result has ${columnCount} columns:"
(1..columnCount).each { i ->
def column = meta[i-1] // 0-based index
println "Column ${i}: ${column.columnName} (${column.columnTypeName})"
}
}) { row ->
// Process rows
println "Row: ${row}"
}def sql = Sql.newInstance("jdbc:h2:mem:test", "sa", "", "org.h2.Driver")
// Collect results with transformations
def results = []
sql.eachRow("SELECT name, age, salary FROM employees") { row ->
// Create modified result
def employee = [:]
employee.name = row.name?.toUpperCase()
employee.age = row.age as Integer
employee.salaryCategory = row.salary > 50000 ? 'high' : 'standard'
results << employee
}
results.each { emp ->
println "${emp.name}: ${emp.salaryCategory} salary"
}def sql = Sql.newInstance("jdbc:h2:mem:test", "sa", "", "org.h2.Driver")
// Get all rows and use Groovy collection methods
def users = sql.rows("SELECT name, age, department FROM users")
// Filter using Groovy methods
def youngUsers = users.findAll { it.age < 30 }
def departments = users.collect { it.department }.unique()
def avgAge = users.sum { it.age } / users.size()
println "Young users: ${youngUsers.size()}"
println "Departments: ${departments}"
println "Average age: ${avgAge}"
// Group by department
def byDept = users.groupBy { it.department }
byDept.each { dept, empList ->
println "${dept}: ${empList.size()} employees"
}Install with Tessl CLI
npx tessl i tessl/maven-org-codehaus-groovy--groovy-sql