Java + JDBC
Generate Java code using standard JDBC APIs. Works with any JDBC-compatible database including SQLite, DuckDB, PostgreSQL, MySQL, and more.
Overview
Section titled “Overview”| Property | Value |
|---|---|
| Generator | java/sqlite, java/duckdb, or java/postgres |
| Runtime | JVM (Java 17+) |
| API Style | Synchronous |
| Driver | Any JDBC driver |
Installation
Section titled “Installation”Add SQG to your build process and include the appropriate JDBC driver:
# Install SQG globallypnpm add -g @sqg/sqgGradle Dependencies
Section titled “Gradle Dependencies”// SQLiteimplementation 'org.xerial:sqlite-jdbc:3.51.1.0'
// DuckDBimplementation 'org.duckdb:duckdb_jdbc:1.4.2.0'
// PostgreSQLimplementation 'org.postgresql:postgresql:42.7.0'Maven Dependencies
Section titled “Maven Dependencies”<!-- SQLite --><dependency> <groupId>org.xerial</groupId> <artifactId>sqlite-jdbc</artifactId> <version>3.51.1.0</version></dependency>
<!-- DuckDB --><dependency> <groupId>org.duckdb</groupId> <artifactId>duckdb_jdbc</artifactId> <version>1.4.2.0</version></dependency>
<!-- PostgreSQL --><dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.7.0</version></dependency>Example Configuration
Section titled “Example Configuration”version: 1name: my-app
sql: - files: - queries.sql gen: - generator: java/sqlite # or: java/duckdb, java/postgres output: ./src/main/java/com/myapp/db/ config: package: com.myapp.db migrations: true # generates applyMigrations() methodConfig options:
package- Java package name for generated classesmigrations- Whentrue, generates anapplyMigrations()method with built-in tracking
Quick Start
Section titled “Quick Start”-
Write your SQL
-- MIGRATE 1CREATE TABLE users (id INTEGER PRIMARY KEY,name TEXT NOT NULL,email TEXT UNIQUE,active INTEGER DEFAULT 1);-- QUERY all_usersSELECT id, name, email, active FROM users ORDER BY name;-- QUERY get_user :one@set id = 1SELECT id, name, email, active FROM users WHERE id = ${id};-- QUERY find_active_users@set active = 1SELECT id, name, email FROM users WHERE active = ${active};-- EXEC create_user@set name = 'John Doe'INSERT INTO users (name, email) VALUES (${name}, ${email});-- EXEC update_user@set id = 1@set name = 'Jane Doe'UPDATE users SET name = ${name}, email = ${email} WHERE id = ${id};-- EXEC delete_user@set id = 1DELETE FROM users WHERE id = ${id}; -
Generate code
Terminal window sqg sqg.yaml -
Use the generated code
import com.myapp.db.MyApp;import java.sql.Connection;import java.sql.DriverManager;public class Main {public static void main(String[] args) throws Exception {// Connect to databaseConnection conn = DriverManager.getConnection("jdbc:sqlite:app.db");// Apply migrations (with built-in tracking)MyApp.applyMigrations(conn);// Create query instanceMyApp queries = new MyApp(conn);// Insert data// Query datafor (MyApp.AllUsersRow user : queries.allUsers()) {System.out.println(user.name() + ": " + user.email());}// Get single rowMyApp.GetUserRow user = queries.getUser(1);if (user != null) {System.out.println("Found: " + user.name());}// Update data// Delete dataqueries.deleteUser(2);conn.close();}}
Generated Code Structure
Section titled “Generated Code Structure”The generator creates a single Java file with:
- Record types for each query result
- A class with methods for each query/exec
- Static
getMigrations()method returning migration SQL strings - Static
applyMigrations()method (whenconfig.migrations: true) that tracks and applies migrations automatically
// ...
public List<AllUsersResult> allUsers() throws SQLException { try ( var stmt = connection.prepareStatement( "SELECT id, name, email, active FROM users ORDER BY name;" ) ) { try (var rs = stmt.executeQuery()) { var results = new ArrayList<AllUsersResult>(); while (rs.next()) { results.add( new AllUsersResult( (Integer) rs.getObject(1), (String) rs.getObject(2), (String) rs.getObject(3), (Integer) rs.getObject(4) ) ); } return results; } } }
// ...Type Mapping
Section titled “Type Mapping”| SQL Type | Java Type |
|---|---|
INTEGER, INT | Integer |
BIGINT | Long |
REAL, DOUBLE | Double |
FLOAT | Float |
TEXT, VARCHAR | String |
BLOB | byte[] |
BOOLEAN | Boolean |
DATE | LocalDate |
TIMESTAMP | LocalDateTime |
TIMESTAMPTZ | OffsetDateTime |
TIME | LocalTime |
DECIMAL, NUMERIC | BigDecimal |
UUID | UUID |
TEXT[], INTEGER[] | List<String>, List<Integer> |
ENUM | String |
All types are nullable (using wrapper classes).
PostgreSQL
Section titled “PostgreSQL”SQG supports PostgreSQL via the java/postgres generator. PostgreSQL-specific features include:
PostgreSQL requires a running server for type introspection. Provide the connection string via environment variable:
export SQG_POSTGRES_URL="postgresql://user:password@localhost:5432/mydb"If SQG_POSTGRES_URL is not set, SQG will automatically start a PostgreSQL container using Testcontainers (requires Docker).
User-Defined Types (ENUMs)
Section titled “User-Defined Types (ENUMs)”SQG introspects PostgreSQL’s pg_type catalog to resolve user-defined types like ENUMs:
-- MIGRATE 1CREATE TYPE task_status AS ENUM ('pending', 'active', 'completed', 'cancelled');
CREATE TABLE tasks ( id SERIAL PRIMARY KEY, title TEXT NOT NULL, status task_status DEFAULT 'pending');
-- QUERY get_tasks_by_status@set status = 'active'SELECT id, title, status FROM tasks WHERE status = ${status}::task_status;ENUM values are mapped to String in Java.
Array Types
Section titled “Array Types”PostgreSQL arrays are mapped to Java List<T>:
CREATE TABLE tasks ( id SERIAL PRIMARY KEY, tags TEXT[], priority_scores INTEGER[]);// Generated recordrecord GetAllTasksResult(Integer id, String title, List<String> tags, List<Integer> priorityScores) {}
// Usagefor (var task : queries.getAllTasks()) { System.out.println(task.tags()); // [urgent, backend] System.out.println(task.priorityScores()); // [10, 20, 30]}TIMESTAMPTZ Support
Section titled “TIMESTAMPTZ Support”PostgreSQL TIMESTAMPTZ columns are mapped to OffsetDateTime (with UTC offset):
CREATE TABLE events ( id SERIAL PRIMARY KEY, created_at TIMESTAMPTZ DEFAULT NOW());// Generated type uses OffsetDateTimerecord EventResult(Integer id, OffsetDateTime createdAt) {}