11# JavaQueryBuilder
22
3- A lightweight, fluent Java library for building SQL queries and filtering in-memory data, no runtime dependencies required.
3+ A lightweight, fluent Java library for building parameterized SQL queries and filtering in-memory data, no runtime dependencies required.
44
55## Features
66
7- - Fluent, readable builder API
8- - Generates parameterized SQL ` SELECT ` statements (safe from SQL injection)
9- - Supports all common operators: ` = ` , ` != ` , ` > ` , ` < ` , ` LIKE ` , ` IN ` , ` BETWEEN ` , ` IS NOT NULL `
10- - Column selection, ` GROUP BY ` , ` ORDER BY ` , ` LIMIT ` , and ` OFFSET `
7+ - Fluent, readable builder API for SELECT, INSERT, UPDATE, DELETE, and CREATE TABLE
8+ - All values are parameterized, safe from SQL injection by design
9+ - Supports all common operators: ` = ` , ` != ` , ` > ` , ` >= ` , ` < ` , ` <= ` , ` LIKE ` , ` NOT LIKE ` , ` IN ` , ` NOT IN ` , ` BETWEEN ` , ` IS NULL ` , ` IS NOT NULL `
10+ - Column selection, ` DISTINCT ` , ` GROUP BY ` , ` ORDER BY ` , ` LIMIT ` , and ` OFFSET `
11+ - SQL dialect support: Standard, MySQL, SQLite
1112- In-memory filtering via ` QueryableStorage `
12- - Zero runtime dependencies, pure Java 25
13+ - Zero runtime dependencies, pure Java 21+
1314
1415## Installation
1516
@@ -36,7 +37,7 @@ Add the repository of Jitpack to your `pom.xml`:
3637
3738## Quick Start
3839
39- ### Generating SQL
40+ ### SELECT
4041
4142``` java
4243SqlResult result = new QueryBuilder ()
@@ -49,14 +50,14 @@ SqlResult result = new QueryBuilder()
4950 .offset(50 )
5051 .buildSql(" users" );
5152
52- String sql = result. getSql(); // the parameterized SQL string
53- List<Object > params = result. getParameters(); // the bound values
53+ String sql = result. getSql(); // parameterized SQL string
54+ List<Object > params = result. getParameters(); // bound values
5455
55- // sql → "SELECT id, name, email FROM users WHERE status = ? AND name LIKE ? AND age > ? ORDER BY name ASC LIMIT 25 OFFSET 50"
56- // params → ["active", "%alice%", 18]
56+ // sql → "SELECT id, name, email FROM users WHERE status = ? AND name LIKE ? AND age > ? ORDER BY name ASC LIMIT 25 OFFSET 50"
57+ // params → ["active", "%alice%", 18]
5758```
5859
59- Pass ` sql ` and ` params ` directly to a JDBC ` PreparedStatement ` :
60+ Pass directly to a JDBC ` PreparedStatement ` :
6061
6162``` java
6263PreparedStatement stmt = connection. prepareStatement(result. getSql());
@@ -67,6 +68,58 @@ for (int i = 0; i < params.size(); i++) {
6768ResultSet rs = stmt. executeQuery();
6869```
6970
71+ ### INSERT
72+
73+ ``` java
74+ SqlResult result = QueryBuilder . insertInto(" users" )
75+ .value(" name" , " Alice" )
76+ .value(" email" , " alice@example.com" )
77+ .value(" age" , 30 )
78+ .build();
79+
80+ // sql → "INSERT INTO users (name, email, age) VALUES (?, ?, ?)"
81+ // params → ["Alice", "alice@example.com", 30]
82+ ```
83+
84+ ### UPDATE
85+
86+ ``` java
87+ SqlResult result = QueryBuilder . update(" users" )
88+ .set(" status" , " inactive" )
89+ .set(" updated_at" , " 2026-04-16" )
90+ .whereEquals(" id" , 42 )
91+ .build();
92+
93+ // sql → "UPDATE users SET status = ?, updated_at = ? WHERE id = ?"
94+ // params → ["inactive", "2026-04-16", 42]
95+ ```
96+
97+ ### DELETE
98+
99+ ``` java
100+ SqlResult result = QueryBuilder . deleteFrom(" sessions" )
101+ .whereEquals(" user_id" , 42 )
102+ .whereLessThan(" expires_at" , " 2026-01-01" )
103+ .build();
104+
105+ // sql → "DELETE FROM sessions WHERE user_id = ? AND expires_at < ?"
106+ // params → [42, "2026-01-01"]
107+ ```
108+
109+ ### CREATE TABLE
110+
111+ ``` java
112+ SqlResult result = QueryBuilder . createTable(" users" )
113+ .column(" id" , " INT" )
114+ .column(" name" , " VARCHAR(255)" )
115+ .column(" email" , " VARCHAR(255)" )
116+ .primaryKey(" id" )
117+ .ifNotExists()
118+ .build();
119+
120+ // sql → "CREATE TABLE IF NOT EXISTS users (id INT, name VARCHAR(255), email VARCHAR(255), PRIMARY KEY (id))"
121+ ```
122+
70123### In-Memory Filtering
71124
72125Implement ` QueryableStorage ` on your storage class to enable query-based lookups without a database:
@@ -88,36 +141,56 @@ public class UserStore implements QueryableStorage {
88141
89142## All Operators
90143
91- | Builder method | SQL clause | In-memory |
144+ | Builder method | SQL clause | Available on |
92145| ---| ---| ---|
93- | ` whereEquals(col, val) ` | ` col = ? ` | ` col.equals(val) ` |
94- | ` whereNotEquals(col, val) ` | ` col != ? ` | ` !col.equals(val) ` |
95- | ` whereGreaterThan(col, val) ` | ` col > ? ` | ` col > val ` |
96- | ` whereLessThan(col, val) ` | ` col < ? ` | ` col < val ` |
97- | ` whereLike(col, substr) ` | ` col LIKE ? ` (value wrapped with ` % ` ) | ` col.contains(substr) ` |
98- | ` whereExists(col) ` | ` col IS NOT NULL ` | ` map.containsKey(col) ` |
99- | ` whereIn(col, collection) ` | ` col IN (?, ?, ...) ` | ` collection.contains(col) ` |
100- | ` whereBetween(col, a, b) ` | ` col BETWEEN ? AND ? ` | ` a <= col <= b ` |
101-
102- ## Full Builder Reference
146+ | ` whereEquals(col, val) ` | ` col = ? ` | QueryBuilder, DeleteBuilder, UpdateBuilder, SelectBuilder |
147+ | ` orWhereEquals(col, val) ` | ` OR col = ? ` | QueryBuilder, UpdateBuilder |
148+ | ` whereNotEquals(col, val) ` | ` col != ? ` | QueryBuilder, DeleteBuilder |
149+ | ` whereGreaterThan(col, val) ` | ` col > ? ` | QueryBuilder, DeleteBuilder |
150+ | ` whereGreaterThanOrEquals(col, val) ` | ` col >= ? ` | QueryBuilder, DeleteBuilder, UpdateBuilder |
151+ | ` whereLessThan(col, val) ` | ` col < ? ` | QueryBuilder, DeleteBuilder |
152+ | ` whereLessThanOrEquals(col, val) ` | ` col <= ? ` | QueryBuilder, DeleteBuilder |
153+ | ` whereLike(col, substr) ` | ` col LIKE ? ` (wrapped with ` % ` ) | QueryBuilder, SelectBuilder |
154+ | ` whereNotLike(col, substr) ` | ` col NOT LIKE ? ` (wrapped with ` % ` ) | QueryBuilder |
155+ | ` whereExists(col) ` | ` col IS NOT NULL ` | QueryBuilder |
156+ | ` whereNull(col) ` | ` col IS NULL ` | QueryBuilder |
157+ | ` whereNotNull(col) ` | ` col IS NOT NULL ` | QueryBuilder |
158+ | ` whereIn(col, list) ` | ` col IN (?, ?, ...) ` | QueryBuilder, DeleteBuilder, SelectBuilder |
159+ | ` whereNotIn(col, list) ` | ` col NOT IN (?, ?, ...) ` | QueryBuilder, DeleteBuilder |
160+ | ` whereBetween(col, a, b) ` | ` col BETWEEN ? AND ? ` | QueryBuilder, DeleteBuilder |
161+
162+ ## Builder Reference
163+
164+ ### QueryBuilder (SELECT)
165+
166+ ` QueryBuilder ` is the main entry point for SELECT queries. It also provides static factory methods for all other statement types.
103167
104168``` java
105169new QueryBuilder ()
106170 // --- column selection (optional, defaults to *) ---
107171 .select(" id" , " name" , " created_at" )
108-
109- // --- conditions ---
110- .whereEquals(" status" , " active" )
111- .whereNotEquals(" role" , " banned" )
112- .whereGreaterThan(" age" , 18 )
113- .whereLessThan(" score" , 1000 )
114- .whereLike(" username" , " john" ) // SQL: username LIKE '%john%'
115- .whereExists(" email" ) // SQL: email IS NOT NULL
116- .whereIn(" country" , List . of(" US" , " CA" , " GB" ))
117- .whereBetween(" created_at" , startDate, endDate)
172+ .distinct() // adds DISTINCT
173+
174+ // --- conditions (all joined with AND unless or* variant used) ---
175+ .whereEquals(" status" , " active" ) // status = ?
176+ .orWhereEquals(" status" , " pending" ) // OR status = ?
177+ .whereNotEquals(" role" , " banned" ) // role != ?
178+ .whereGreaterThan(" age" , 18 ) // age > ?
179+ .whereGreaterThanOrEquals(" age" , 18 ) // age >= ?
180+ .whereLessThan(" score" , 1000 ) // score < ?
181+ .whereLessThanOrEquals(" score" , 1000 ) // score <= ?
182+ .whereLike(" username" , " john" ) // username LIKE '%john%'
183+ .whereNotLike(" email" , " spam" ) // email NOT LIKE '%spam%'
184+ .whereExists(" verified_at" ) // verified_at IS NOT NULL
185+ .whereNull(" deleted_at" ) // deleted_at IS NULL
186+ .whereNotNull(" email" ) // email IS NOT NULL
187+ .whereIn(" country" , List . of(" US" , " CA" ))
188+ .whereNotIn(" role" , List . of(" bot" , " banned" ))
189+ .whereBetween(" created_at" , from, to)
118190
119191 // --- sorting and grouping ---
120192 .groupBy(" department" )
193+ .havingRaw(" COUNT(*) > 5" )
121194 .orderBy(" created_at" , false ) // false = DESC
122195 .orderBy(" name" , true ) // true = ASC
123196
@@ -126,33 +199,135 @@ new QueryBuilder()
126199 .offset(50 )
127200
128201 // --- output ---
202+ .from(" users" ) // sets table for build() / no-arg buildSql()
129203 .build() // → Query (for in-memory use)
130- .buildSql(" users" ); // → SqlResult (for JDBC use)
204+ .buildSql(" users" ) // → SqlResult (for JDBC use)
205+ .buildSql(" users" , SqlDialect . MYSQL ); // → SqlResult with dialect quoting
206+ ```
207+
208+ ### Static Factory Methods
209+
210+ All DML/DDL builders are accessible from ` QueryBuilder ` without needing to import the individual classes:
211+
212+ ``` java
213+ // INSERT
214+ QueryBuilder . insert() // new InsertBuilder
215+ QueryBuilder . insertInto(" tableName" ) // new InsertBuilder pre-configured with table
216+
217+ // UPDATE
218+ QueryBuilder . update() // new UpdateBuilder
219+ QueryBuilder . update(" tableName" ) // new UpdateBuilder pre-configured with table
220+
221+ // DELETE
222+ QueryBuilder . delete() // new DeleteBuilder
223+ QueryBuilder . deleteFrom(" tableName" ) // new DeleteBuilder pre-configured with table
224+
225+ // CREATE TABLE
226+ QueryBuilder . createTable() // new CreateBuilder
227+ QueryBuilder . createTable(" tableName" ) // new CreateBuilder pre-configured with table
228+ ```
229+
230+ ### InsertBuilder
231+
232+ ``` java
233+ QueryBuilder . insertInto(" users" )
234+ .value(" name" , " Alice" )
235+ .value(" email" , " alice@example.com" )
236+ .build(); // → SqlResult
237+ ```
238+
239+ ### UpdateBuilder
240+
241+ ``` java
242+ QueryBuilder . update(" users" )
243+ .set(" name" , " Alice" )
244+ .set(" status" , " active" )
245+ .whereEquals(" id" , 1 )
246+ .orWhereEquals(" email" , " alice@example.com" )
247+ .whereGreaterThanOrEquals(" age" , 18 )
248+ .build(); // → SqlResult
249+ ```
250+
251+ ### DeleteBuilder
252+
253+ ``` java
254+ QueryBuilder . deleteFrom(" users" )
255+ .whereEquals(" id" , 1 )
256+ .whereNotEquals(" role" , " admin" )
257+ .whereGreaterThan(" age" , 0 )
258+ .whereGreaterThanOrEquals(" score" , 10 )
259+ .whereLessThan(" attempts" , 5 )
260+ .whereLessThanOrEquals(" balance" , 0 )
261+ .whereIn(" status" , List . of(" expired" , " banned" ))
262+ .whereNotIn(" plan" , List . of(" premium" , " trial" ))
263+ .whereBetween(" created_at" , from, to)
264+ .build(); // → SqlResult
265+ ```
266+
267+ ### CreateBuilder
268+
269+ ``` java
270+ QueryBuilder . createTable(" orders" )
271+ .column(" id" , " BIGINT" )
272+ .column(" user_id" , " INT" )
273+ .column(" total" , " DECIMAL(10,2)" )
274+ .column(" created_at" , " TIMESTAMP" )
275+ .primaryKey(" id" )
276+ .ifNotExists()
277+ .build(); // → SqlResult
278+ ```
279+
280+ ### SelectBuilder
281+
282+ ` SelectBuilder ` provides a standalone, self-contained SELECT builder (no ` Query ` object):
283+
284+ ``` java
285+ new SelectBuilder ()
286+ .from(" users" )
287+ .select(" id" , " name" )
288+ .distinct()
289+ .whereEquals(" active" , true )
290+ .whereIn(" role" , List . of(" admin" , " editor" ))
291+ .whereLike(" name" , " alice" )
292+ .groupBy(" department" )
293+ .orderBy(" name" , true )
294+ .limit(10 )
295+ .offset(20 )
296+ .build(); // → SqlResult
131297```
132298
133299## SQL Dialects
134300
135301By default, ` buildSql(table) ` uses ` SqlDialect.STANDARD ` (no identifier quoting). Pass a second argument to use a different dialect:
136302
137303``` java
138- // Standard SQL
139- SqlResult sql = new QueryBuilder ()
304+ // Standard SQL (default)
305+ SqlResult result = new QueryBuilder ()
140306 .whereEquals(" status" , " active" )
141- .buildSql(" users" ); // SqlDialect.STANDARD
307+ .buildSql(" users" );
308+ // → SELECT * FROM users WHERE status = ?
309+
310+ // MySQL
311+ SqlResult result = new QueryBuilder ()
312+ .select(" id" , " name" )
313+ .whereEquals(" status" , " active" )
314+ .buildSql(" users" , SqlDialect . MYSQL );
315+ // → SELECT `id`, `name` FROM `users` WHERE `status` = ?
142316
143317// SQLite
144- SqlResult sql = new QueryBuilder ()
318+ SqlResult result = new QueryBuilder ()
145319 .whereEquals(" status" , " active" )
146320 .buildSql(" users" , SqlDialect . SQLITE );
147321// → SELECT * FROM "users" WHERE "status" = ?
148322```
149323
150- | Dialect | ` SqlDialect.STANDARD ` | ` SqlDialect.SQLITE ` |
324+ | Dialect | Identifier quoting | Boolean values |
151325| ---| ---| ---|
152- | Identifier quoting | none | double quotes ` "col" ` |
153- | Boolean values | ` true ` / ` false ` | ` 1 ` / ` 0 ` |
326+ | ` SqlDialect.STANDARD ` | none | ` true ` / ` false ` |
327+ | ` SqlDialect.MYSQL ` | back-ticks `` `col` `` | ` true ` / ` false ` |
328+ | ` SqlDialect.SQLITE ` | double quotes ` "col" ` | ` 1 ` / ` 0 ` |
154329
155- SQLite wraps every table and column name in double quotes, which safely handles reserved words and names with special characters. Java booleans are converted to ` 1 ` /` 0 ` to match SQLite's integer-backed boolean storage.
330+ MySQL back-tick quoting safely handles reserved words and case-sensitive identifiers. SQLite double-quote quoting serves the same purpose, and Java booleans are converted to ` 1 ` /` 0 ` to match SQLite's integer-backed boolean storage.
156331
157332## How SQL Generation Works
158333
0 commit comments