Higher-level abstraction over Java's JDBC technology providing simplified database operations with resource management
—
Advanced parameter binding with typed parameters for stored procedures, GString expression support, and named parameter processing.
Define typed parameters for stored procedure input/output operations.
// Input parameter interface
public interface InParameter {
int getType(); // JDBC type constant
Object getValue(); // Parameter value
}
// Output parameter interface
public interface OutParameter {
int getType(); // JDBC type constant
}
// Bidirectional parameter interface
public interface InOutParameter extends InParameter, OutParameter {}
// ResultSet output parameter interface
public interface ResultSetOutParameter extends OutParameter {}Create typed parameters using static factory methods for all JDBC types.
// Generic parameter factories
public static InParameter in(int type, Object value);
public static OutParameter out(int type);
public static InOutParameter inout(InParameter in);
public static ResultSetOutParameter resultSet(int type);
// Type-specific InParameter factories
public static InParameter ARRAY(Object value);
public static InParameter BIGINT(Object value);
public static InParameter BINARY(Object value);
public static InParameter BIT(Object value);
public static InParameter BLOB(Object value);
public static InParameter BOOLEAN(Object value);
public static InParameter CHAR(Object value);
public static InParameter CLOB(Object value);
public static InParameter DATE(Object value);
public static InParameter DECIMAL(Object value);
public static InParameter DOUBLE(Object value);
public static InParameter FLOAT(Object value);
public static InParameter INTEGER(Object value);
public static InParameter LONGVARBINARY(Object value);
public static InParameter LONGVARCHAR(Object value);
public static InParameter NUMERIC(Object value);
public static InParameter OTHER(Object value);
public static InParameter REAL(Object value);
public static InParameter SMALLINT(Object value);
public static InParameter TIME(Object value);
public static InParameter TIMESTAMP(Object value);
public static InParameter TINYINT(Object value);
public static InParameter VARBINARY(Object value);
public static InParameter VARCHAR(Object value);Pre-defined OutParameter constants for all JDBC types.
// OutParameter constants
public static final OutParameter ARRAY;
public static final OutParameter BIGINT;
public static final OutParameter BINARY;
public static final OutParameter BIT;
public static final OutParameter BLOB;
public static final OutParameter BOOLEAN;
public static final OutParameter CHAR;
public static final OutParameter CLOB;
public static final OutParameter DATALINK;
public static final OutParameter DATE;
public static final OutParameter DECIMAL;
public static final OutParameter DISTINCT;
public static final OutParameter DOUBLE;
public static final OutParameter FLOAT;
public static final OutParameter INTEGER;
public static final OutParameter JAVA_OBJECT;
public static final OutParameter LONGVARBINARY;
public static final OutParameter LONGVARCHAR;
public static final OutParameter NULL;
public static final OutParameter NUMERIC;
public static final OutParameter OTHER;
public static final OutParameter REAL;
public static final OutParameter REF;
public static final OutParameter SMALLINT;
public static final OutParameter STRUCT;
public static final OutParameter TIME;
public static final OutParameter TIMESTAMP;
public static final OutParameter TINYINT;
public static final OutParameter VARBINARY;
public static final OutParameter VARCHAR;Example:
import static groovy.sql.Sql.*
// Using type constants for output parameters
def result = sql.call('CALL get_user_stats(?, ?)', [userId, INTEGER])
println "User count: ${result[0][1]}"
// Using factory methods for input parameters
def params = [
VARCHAR('John'),
INTEGER(25),
TIMESTAMP(new Date()),
BOOLEAN(true)
]
sql.call('CALL create_user(?, ?, ?, ?)', params)
// Mixed input/output parameters
def mixedParams = [
VARCHAR('admin'), // input
out(INTEGER), // output
inout(VARCHAR('pending')) // input/output
]
def results = sql.call('CALL process_request(?, ?, ?)', mixedParams)Use Groovy GString expressions for dynamic parameter binding with automatic type inference.
Example:
// Simple GString binding
def userId = 123
def status = 'active'
sql.eachRow("SELECT * FROM users WHERE id = $userId AND status = $status") { row ->
println row.name
}
// Complex expressions
def minDate = new Date() - 30
def categories = ['electronics', 'books', 'clothing']
sql.eachRow("SELECT * FROM products WHERE created_date > $minDate AND category IN (${categories.join(',')})") { row ->
println "${row.name}: ${row.category}"
}
// Null handling
def optionalFilter = condition ? someValue : null
sql.eachRow("SELECT * FROM items WHERE ${optionalFilter ? "category = $optionalFilter" : '1=1'}") { row ->
println row
}Use named parameters with :name syntax for improved readability and maintainability.
// Named parameter processing (internal)
public SqlWithParams checkForNamedParams(String sql, List<?> params);
protected SqlWithParams buildSqlWithIndexedProps(String sql);
// Configuration
public boolean isEnableNamedQueries();
public void setEnableNamedQueries(boolean enableNamedQueries);Example:
// Enable named parameters
sql.enableNamedQueries = true
// Named parameter syntax
def params = [
userId: 123,
status: 'active',
minAge: 18,
department: 'Engineering'
]
// Use named parameters in queries
sql.eachRow('SELECT * FROM users WHERE id = :userId AND status = :status', params) { row ->
println row.name
}
// Complex named query
def searchCriteria = [
firstName: 'John',
lastName: 'Smith',
minSalary: 50000,
departments: ['IT', 'Engineering', 'Sales']
]
sql.eachRow('''
SELECT * FROM employees
WHERE first_name LIKE :firstName
AND last_name LIKE :lastName
AND salary >= :minSalary
AND department IN (:departments)
''', searchCriteria) { row ->
println "${row.first_name} ${row.last_name} - ${row.department}"
}
// Property-based named parameters (e.g., :user.id, :user.name)
def user = [id: 123, name: 'Alice', email: 'alice@example.com']
sql.executeUpdate('INSERT INTO users (id, name, email) VALUES (:user.id, :user.name, :user.email)', [user: user])Control how variables are expanded into SQL strings versus parameter placeholders.
// Variable expansion interface
public interface ExpandedVariable {
Object getObject();
}
// Factory method
public static ExpandedVariable expand(Object object);Example:
import static groovy.sql.Sql.expand
// Normal parameter binding (safe from SQL injection)
def tableName = 'users'
def userId = 123
// This won't work - table names can't be parameterized
// sql.rows('SELECT * FROM ? WHERE id = ?', [tableName, userId])
// Use expand() for dynamic SQL parts (be careful with user input!)
def safeSql = "SELECT * FROM ${expand(tableName)} WHERE id = ?"
sql.eachRow(safeSql, [userId]) { row ->
println row.name
}
// Dynamic ORDER BY clause
def sortColumn = 'name' // validated against allowed columns
def sortOrder = 'ASC' // validated against ASC/DESC
def orderBy = "${expand(sortColumn)} ${expand(sortOrder)}"
sql.eachRow("SELECT * FROM users ORDER BY $orderBy") { row ->
println row.name
}
// Building dynamic WHERE clauses
def buildWhereClause(filters) {
def conditions = []
def params = []
if (filters.name) {
conditions << 'name LIKE ?'
params << "%${filters.name}%"
}
if (filters.minAge) {
conditions << 'age >= ?'
params << filters.minAge
}
if (filters.department) {
conditions << 'department = ?'
params << filters.department
}
def whereClause = conditions ? "WHERE ${conditions.join(' AND ')}" : ''
return [whereClause: expand(whereClause), params: params]
}
def filters = [name: 'John', minAge: 25]
def query = buildWhereClause(filters)
sql.eachRow("SELECT * FROM users ${query.whereClause}", query.params) { row ->
println row
}Handle type conversion and parameter validation for database operations.
Example:
import java.sql.Types
import java.sql.Timestamp
import java.text.SimpleDateFormat
// Automatic type conversion examples
def params = [
// String to appropriate SQL type
stringParam: 'Hello World',
// Number conversion
intParam: 42,
longParam: 123456789L,
doubleParam: 3.14159,
bigDecimalParam: new BigDecimal('999.99'),
// Date/time conversion
dateParam: new Date(),
timestampParam: new Timestamp(System.currentTimeMillis()),
// Boolean conversion
boolParam: true,
// Null handling
nullParam: null,
// Binary data
blobParam: 'binary data'.bytes
]
sql.executeUpdate('''
INSERT INTO test_table (
string_col, int_col, long_col, double_col, decimal_col,
date_col, timestamp_col, bool_col, null_col, blob_col
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', params.values() as List)
// Custom type handling
def customDateFormat = new SimpleDateFormat('yyyy-MM-dd')
def dateString = '2024-01-15'
def parsedDate = customDateFormat.parse(dateString)
sql.executeUpdate('INSERT INTO events (event_date) VALUES (?)', [TIMESTAMP(parsedDate)])
// Validate parameters before use
def validateAndExecute(sql, query, params) {
params.each { param ->
if (param instanceof String && param.contains(';')) {
throw new IllegalArgumentException('Potentially unsafe parameter detected')
}
}
return sql.executeUpdate(query, params)
}Install with Tessl CLI
npx tessl i tessl/maven-org-apache-groovy--groovy-sql