Skip to content

How to Build Dynamic Queries with MyBatis-Flex QueryWrapper: Filters, Conditions, and Sorting

I needed to build a query that filters users by age, status, and sorts results by registration date. With plain MyBatis, I’d write XML mapping files or concatenate SQL strings. I wanted something type-safe and fluent.

MyBatis-Flex QueryWrapper provides exactly that—a fluent API for building SQL queries in Java.

Basic Query with Multiple Conditions

Let’s say I need to find all active users aged 18 or older, sorted by age in descending order:

UserRepository.java
public List<Account> findActiveAdults() {
QueryWrapper wrapper = QueryWrapper.create()
.where(ACCOUNT.AGE.ge(18))
.and(ACCOUNT.STATUS.eq("ACTIVE"))
.orderBy(ACCOUNT.AGE.desc());
return accountMapper.selectListByQuery(wrapper);
}

The QueryWrapper.create() method starts the chain. I use .where() for the first condition, .and() for subsequent conditions, and .orderBy() for sorting.

The ACCOUNT constant is generated by MyBatis-Flex and provides type-safe column references. No string literals for column names—compile-time checking catches typos.

Dynamic Query with Optional Filters

Real applications need queries where conditions are optional. Users might filter by name, age, or both—or neither. I used to write multiple query variants. Now I build the wrapper incrementally:

UserRepository.java
public List<Account> searchUsers(String name, Integer minAge, String status) {
QueryWrapper wrapper = QueryWrapper.create();
if (name != null && !name.isEmpty()) {
wrapper.and(ACCOUNT.USERNAME.like(name + "%"));
}
if (minAge != null) {
wrapper.and(ACCOUNT.AGE.ge(minAge));
}
if (status != null) {
wrapper.and(ACCOUNT.STATUS.eq(status));
}
wrapper.orderBy(ACCOUNT.CREATED_AT.desc());
return accountMapper.selectListByQuery(wrapper);
}

This approach generates different SQL based on which parameters are present. No string concatenation, no SQL injection risk.

Available Operators

QueryWrapper supports the standard comparison and condition operators:

eq(column, value) - equals (=)
ne(column, value) - not equals (<>)
gt(column, value) - greater than (>)
ge(column, value) - greater than or equal (>=)
lt(column, value) - less than (<)
le(column, value) - less than or equal (<=)
like(column, value) - LIKE clause
in(column, values) - IN clause
isNull(column) - IS NULL
isNotNull(column) - IS NOT NULL
between(column, v1, v2) - BETWEEN clause

Each operator returns the wrapper, enabling method chaining.

Sorting Syntax

Sorting uses a different syntax—call .desc() or .asc() directly on the column reference:

SortingExamples.java
// Single sort column
QueryWrapper.create()
.orderBy(ACCOUNT.AGE.desc());
// Multiple sort columns
QueryWrapper.create()
.orderBy(ACCOUNT.STATUS.asc())
.orderBy(ACCOUNT.CREATED_AT.desc());

The generated SQL will have ORDER BY status ASC, created_at DESC.

Passing QueryWrapper to Mapper

MyBatis-Flex mappers extend BaseMapper&lt;T&gt;, which provides built-in query methods:

AccountMapper.java
public interface AccountMapper extends BaseMapper&lt;Account&gt; {
// No additional methods needed
// selectListByQuery is inherited from BaseMapper
}

Call selectListByQuery(wrapper) to execute the query and return results. Use selectOneByQuery(wrapper) when you expect a single result.

In Summary

In this post, I showed how to use MyBatis-Flex QueryWrapper to build type-safe SQL queries in Java. You can chain conditions for static queries or build incrementally for dynamic scenarios—no XML mapping files or string concatenation required. The generated column constants provide compile-time safety, and the fluent API keeps queries readable.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments