DEV Community

ts5432
ts5432

Posted on

Building Query Conditions In JooqTemplate

Using JooqTemplate requires no code generation and no XML configuration.

Add Dependency

gradle:

implementation 'net.sf.sprtool:sprtool-jooq:1.7.0'
Enter fullscreen mode Exit fullscreen mode

maven:

<dependency>
    <groupId>net.sf.sprtool</groupId>
    <artifactId>sprtool-jooq</artifactId>
    <version>1.7.0</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Inject JooqTemplate

import static net.sf.sprtool.jooq.JooqFields.*;

import net.sf.sprtool.jooq.JooqTemplate;

@Autowired
private JooqTemplate jt;
Enter fullscreen mode Exit fullscreen mode

Using JooqTemplate

Method 1: conditions() — Field-Value Pair Varargs (Recommended)

conditions(Object... fieldValuePairs) takes arguments in pairs of "field name, value"; the operator is written directly after the field name string, and when no operator is written the default is ==. It returns a List<Condition>, and conditions whose values are null are automatically ignored:

// name = 'Smartphone' and price > 1000 and status = 'on_sale'
List<Condition> conds = jt.conditions(
        "name", "Smartphone",
        "price>", 1000,        // the > operator follows the field name
        "stock<=", 50,         // the <= operator
        "status", "on_sale");  // default operator ==

// You can also separate the field and operator with a colon: "price:>"
List<Condition> conds2 = jt.conditions("price:>", 1000);
Enter fullscreen mode Exit fullscreen mode

When a parameter value is null, that condition is ignored — ideal for dynamic queries: if the frontend doesn't send a value, no filter is applied:

// When nameParam is null, no name condition is generated
List<Condition> conds = jt.conditions("name", nameParam, "category", categoryParam);
Enter fullscreen mode Exit fullscreen mode

You can also mix native jOOQ Condition objects directly into the varargs:

Condition other = DSL.field("price").ge(1000);
List<Condition> conds = jt.conditions("category", "Electronics", other);
Enter fullscreen mode Exit fullscreen mode

Method 2: and() / or() — Directly Returning a Single Combined Condition

and(...) and or(...) take arguments exactly like conditions(); the difference is the return value: multiple conditions are merged with AND / OR into a single Condition, and null is returned when there are no conditions at all:

// AND: name = 'John' and age > 30 and status = 'active'
Condition cond = jt.and("name", "John", "age>", 30, "status", "active");

// OR: name like 'Soph%' or name like 'Fra%'
Condition cond2 = jt.or("name:%l", "Soph", "name:%l", "Fra");

// Empty arguments return null
Condition empty = jt.and();   // null
Enter fullscreen mode Exit fullscreen mode

Find two users with OR (corresponding to the user_table example in JooqTemplateOrTest):

// name = 'John' or name = 'Lee'
Condition cond = jt.or("name", "John", "name", "Lee");
List<LowerCaseRecord> records = jt.queryLowerCaseRecord("user_table", cond);
Enter fullscreen mode Exit fullscreen mode

Method 3: condition() — Single-Condition Method

condition(Name fieldName, String operator, Object[] values) accepts a jOOQ Name, an operator string, and a value array, and generates a single Condition. The number of values must match what the operator requires, otherwise an IllegalArgumentException is thrown:

import org.jooq.Condition;
import org.jooq.Name;
import org.jooq.impl.DSL;

// Equality: == requires 1 value
Condition c1 = jt.condition(DSL.name("name"), "==", new Object[]{"Smartphone"});

// Comparison: > requires 1 value
Condition c2 = jt.condition(DSL.name("price"), ">", new Object[]{1000});

// in: requires 1 value, and the value must be a Collection or Object[]
Condition c3 = jt.condition(DSL.name("category"), "in",
        new Object[]{Arrays.asList("Electronics", "Appliances", "Apparel")});

// between: requires 2 values
Condition c4 = jt.condition(DSL.name("created_at"), "between",
        new Object[]{beginDate, endDate});

// isnull: requires 0 values
Condition c5 = jt.condition(DSL.name("remark"), "isnull", new Object[]{});
Enter fullscreen mode Exit fullscreen mode

This method also follows the null rules: a null value returns null (ignored); wrapping with ND(null) generates is null; a null field name or operator returns null; an invalid operator throws IllegalArgumentException:

// Value is null → returns null, condition ignored
Condition c = jt.condition(DSL.name("name"), "==", new Object[]{null});   // null

// ND wrapping null → generates name is null
Condition c2 = jt.condition(DSL.name("name"), "==", new Object[]{ND(null)});

// Value count mismatch → throws IllegalArgumentException("Operator require value count is 1")
Condition c3 = jt.condition(DSL.name("name"), "==", new Object[]{});
Enter fullscreen mode Exit fullscreen mode

not() — Negating a Condition

not(Condition) negates an existing condition and returns null when the input is null:

Condition original = jt.and("status", "disabled");
Condition notDisabled = jt.not(original);   // not (status = 'disabled')
Enter fullscreen mode Exit fullscreen mode

orOne() — Multiple Fields Matching the Same Value

orOne(Object... sameOneValues) starts with several "fields" (single-value operators), and the last argument is the shared value, generating field1 op value or field2 op value .... A typical "global keyword search" scenario:

// name = 'Apple' or nick_name = 'Apple' or remark = 'Apple'
Condition cond = jt.orOne("name", "nick_name", "remark", "Apple");
Enter fullscreen mode Exit fullscreen mode

Null Fallback for between

between / notbetween take 2 values; when either end is null, it automatically falls back to a one-sided comparison, and when both ends are null the whole condition is ignored:

// Both begin and end are non-null: price between 100 and 500
jt.conditions("price:between", 100, 500);

// begin=100, end=null: price >= 100
jt.conditions("price:between", 100, null);

// notbetween fallback: begin=null, end=500 → price > 500
jt.conditions("price:notbetween", null, 500);
Enter fullscreen mode Exit fullscreen mode

Operator Reference

Condition operators (written after the field name or passed to condition(); all are case-insensitive):

Operator Value Count Meaning
== (default) 1 Equal to
<> 1 Not equal to
> >= < <= 1 Comparison
% / like 1 Contains %value%
%l / llike 1 Prefix match value%
%r / rlike 1 Suffix match %value
in / notin 1 Value is a Collection or Object[]
between / notbetween 2 Range; a null end falls back automatically
isnull / isnotnull 0 Null check

Sort operators (only used in the order part of a query or in orders()): asc, ascnf (nulls first), ascnl (nulls last), desc, descnf, descnl.

Null handling rules:

  • In queries (conditions() / and() / or() / condition()), a null value → the condition is ignored.
  • A value wrapped with ND(null) → generates field is null.
  • In UPDATE / DELETE, a null value → generates field is null.

Top comments (0)