SQL Rules: Combining AND/OR Conditions
Simple rules like @{and, name = :name} handle single conditions well, but real-world queries often need nested logic like (A AND B) OR (C AND D). This article shows how dbVisitor rules handle complex compound conditions in one line.
Query Scenario
Suppose we have the following query requirement:
Query users who meet any group of the following conditions:
ageequals specified value ANDsexis 1nameequals specified value ANDidis in the specified list
The corresponding SQL logical structure is:
WHERE (age = ? AND sex = '1') OR (name = ? AND id IN (?, ?, ?))
Condition Handling
If you don't use dbVisitor's advanced rules, implementing this logic in other frameworks (such as MyBatis XML) can be very painful:
- Troublesome Parenthesis Management: You need to carefully control the generation of
(and)to prevent leaving empty brackets()when some parameters are null, which would cause SQL errors. - Tedious Prefix Handling: You need to handle the splicing of the
ORkeyword. If the first group of conditions is empty, the second group cannot haveORin front; if there are alreadyWHEREconditions before, you need to addAND.
The resulting XML might look like cryptic code:
<!-- Cumbersome XML Implementation (Counterexample) -->
<trim prefix="AND (" suffix=")" prefixOverrides="OR">
<if test="age != null">
(age = #{age} AND sex = '1')
</if>
<if test="name != null">
OR (name = #{name} AND id IN
<foreach ...>...</foreach>
)
</if>
</trim>
Composing SQL Rules
dbVisitor's design philosophy is "Let SQL return to SQL". Its @{and, ...} rule supports not only simple key = value, but also writing complete SQL fragments containing parentheses and logical operators.
Code Example
We just need to write the complete logic in one @{and} rule:
/* SQL template; ids must be non-empty when the combined condition is enabled */
select * from user_info
where status = 'ENABLE'
@{and, ((age = :age and sex = '1') or (name = :name and id in @{in, :ids})) }
How Rules Work
When executing this SQL, the dbVisitor engine performs the following judgments:
- Parameter Scanning: The engine scans all parameters referenced in the rule expression
(age = :age ...)(i.e.,:age,:name,:ids). - Dynamic Decision:
- Case A: All key parameters are null. If
:age,:name,:idsare allnull, the entire@{and, ...}rule will be completely ignored. The SQL degrades toselect * from user_info where status = 'ENABLE'. - Case B: Valid parameters exist. As long as any one of the parameters is not null, the entire expression will be appended to the SQL as a whole. The rule does not remove NULL comparisons inside the combined expression. Validate required parameters, especially ids. Use separate conditional rules if the groups should be independently optional.
- Case A: All key parameters are null. If
- Automatic Decoration: dbVisitor automatically handles the connector after
WHERE. If this is the first condition, it automatically fills inAND(if there is alreadystatus='ENABLE'before it).
Generated SQL
Assuming the input parameters are age = 18, name = "Tom", ids = [1, 2, 3], the final generated SQL is:
select * from user_info
where status = 'ENABLE'
AND ( (age = ? and sex = '1') or (name = ? and id in (?, ?, ?)) )
Benefits
- High Readability: The rule you write is standard SQL syntax, including parentheses and
ORlogic. Anyone who knows SQL can understand it at a glance without having to decipher the nested logic of XML tags. - Zero Glue Code: No need for complex tags like
<trim>,<if>,<choose>to handle the splicing of SQL syntax fragments. - Security: Although writing complex expressions is allowed, all variables (
:age, etc.) are still processed through JDBC PreparedStatement, binding data values. Table names, column names, and${...}text substitution are outside that protection and must be controlled by the application.
Summary
dbVisitor's rule engine is not just for simple non-null judgments. By supporting nested complex SQL expressions in rules, it allows us to handle complex dynamic query logic in a way that is most intuitive and closest to native SQL.
Next time you encounter a complex OR combination query, try writing the logic directly into @{and, ...}!