Skip to content

Commit 346b91c

Browse files
committed
docs: updated README.md with latest features
1 parent 259b16b commit 346b91c

1 file changed

Lines changed: 217 additions & 42 deletions

File tree

README.md

Lines changed: 217 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
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
4243
SqlResult 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
6263
PreparedStatement stmt = connection.prepareStatement(result.getSql());
@@ -67,6 +68,58 @@ for (int i = 0; i < params.size(); i++) {
6768
ResultSet 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

72125
Implement `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
105169
new 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

135301
By 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

Comments
 (0)