Databases 17 min read

Why MyBatis-Flex Shines Among the Flood of New ORM Frameworks

The article provides a detailed technical comparison of MyBatis-Flex with MyBatis-Plus and Fluent-MyBatis, covering feature matrices, code examples for basic queries, aggregation, complex conditions, multi‑table joins, partial updates, and a step‑by‑step Spring Boot integration guide, before concluding on its maturity and suitability for production.

Shepherd Advanced Notes
Shepherd Advanced Notes
Shepherd Advanced Notes
Why MyBatis-Flex Shines Among the Flood of New ORM Frameworks

Overview

MyBatis is the dominant ORM in Java back‑ends. MyBatis‑Plus (open‑source since 2016) and Fluent‑MyBatis (developed by Alibaba Cloud) are widely used extensions. MyBatis‑Flex is a newer, lightweight enhancement that has attracted over 1 000 GitHub stars.

Feature comparison

Basic CRUD on entity: supported by all three.

Pagination query: supported by all three.

Pagination total cache: supported by Flex and Plus, not by Fluent‑MyBatis.

Pagination without SQL parsing (lighter, higher performance): supported by Flex and Fluent‑MyBatis, not by Plus.

Multi‑table query (FROM multiple tables, LEFT/INNER JOIN, UNION/UNION ALL): supported only by Flex; Plus lacks support; Fluent‑MyBatis supports joins but not FROM‑multiple‑tables syntax.

Composite primary‑key support: only Flex provides it.

No third‑party dependencies besides MyBatis: only Flex is dependency‑free.

QueryWrapper RPC support for micro‑services: Flex supports it, Plus does not, Fluent‑MyBatis status unknown.

SQL audit, data masking, field permission, field encryption, dictionary write‑back: only Flex provides these features out‑of‑the‑box; Plus offers them in paid editions; Fluent‑MyBatis does not.

Db + Row, entity listener, multi‑datasource (including Spring transaction and non‑Spring), dynamic schema: only Flex implements them.

Multi‑tenant and dynamic table name: Flex and Plus support multi‑tenant; Flex and Plus support dynamic table name; Fluent‑MyBatis supports neither.

Usage examples

Basic query

MyBatis‑Flex

QueryWrapper query = QueryWrapper.create()
    .where(EMPLOYEE.LAST_NAME.like(searchWord)) // null condition ignored
    .and(EMPLOYEE.GENDER.eq(1))
    .and(EMPLOYEE.AGE.gt(24));
List<Employee> employees = employeeMapper.selectListByQuery(query);

MyBatis‑Plus

QueryWrapper<Employee> queryWrapper = Wrappers.query()
    .like(searchWord != null, "last_name", searchWord)
    .eq("gender", 1)
    .gt("age", 24);
List<Employee> employees = employeeMapper.selectList(queryWrapper);

Lambda style (Plus)

LambdaQueryWrapper<Employee> queryWrapper = Wrappers.<Employee>lambdaQuery()
    .like(StringUtils.isNotEmpty(searchWord), Employee::getUserName, "B")
    .eq(Employee::getGender, 1)
    .gt(Employee::getAge, 24);
List<Employee> employees = employeeMapper.selectList(queryWrapper);

Fluent‑MyBatis

EmployeeQuery query = new EmployeeQuery()
    .where.lastName().like(searchWord, If::notNull)
    .and.gender().eq(1)
    .and.age().gt(24)
    .end();
List<Employee> employees = employeeMapper.listEntity(query);

Aggregation functions

MyBatis‑Flex

QueryWrapper query = QueryWrapper.create()
    .select(
        ACCOUNT.ID,
        ACCOUNT.USER_NAME,
        max(ACCOUNT.BIRTHDAY),
        avg(ACCOUNT.SEX).as("sex_avg")
    );
List<Employee> employees = employeeMapper.selectListByQuery(query);

MyBatis‑Plus

QueryWrapper<Employee> queryWrapper = Wrappers.query()
    .select(
        "id",
        "user_name",
        "max(birthday)",
        "avg(birthday) as sex_avg"
    );
List<Employee> employees = employeeMapper.selectList(queryWrapper);
Drawback: field names are hard‑coded, prone to typos, and IDE refactoring/auto‑completion is unavailable.

Fluent‑MyBatis

EmployeeQuery query = new EmployeeQuery()
    .select
        .id()
        .userName()
        .max.birthday()
        .avg.sex("sex_avg")
    .end();
List<Employee> employees = employeeMapper.listEntity(query);
Drawback: the DSL does not follow natural SQL intuition.

AND/OR conditions

Target SQL:

SELECT * FROM tb_account
WHERE id >= 100 AND (sex = 1 OR sex = 2) OR (age IN (18,19,20) AND user_name LIKE "%michael%")

MyBatis‑Flex

QueryWrapper query = QueryWrapper.create()
    .where(ACCOUNT.ID.ge(100))
    .and(ACCOUNT.SEX.eq(1).or(ACCOUNT.SEX.eq(2)))
    .or(ACCOUNT.AGE.in(18,19,20).and(ACCOUNT.USER_NAME.like("michael")));

MyBatis‑Plus

QueryWrapper<Employee> query = Wrappers.query()
    .ge("id", 100)
    .and(i -> i.eq("sex", 1).or(x -> x.eq("sex", 2)))
    .or(i -> i.in("age", 18,19,20).like("user_name", "michael")));

Lambda version (Plus)

LambdaQueryWrapper<Employee> query = Wrappers.<Employee>lambdaQuery()
    .ge(Employee::getId, 100)
    .and(i -> i.eq(Employee::getSex, 1).or(x -> x.eq(Employee::getSex, 2)))
    .or(i -> i.in(Employee::getAge, 18,19,20).like(Employee::getUserName, "michael"));

Fluent‑MyBatis

AccountQuery query = new AccountQuery()
    .where.id().ge(100)
    .and(new AccountQuery().where.sex().eq(1).or(new AccountQuery().where.sex().eq(2).end()))
    .or(new AccountQuery().where.age.in(18,19,20).and.userName().like("michael").end())
    .end();
Drawback: many .end() calls are easy to forget or misuse.

Multi‑table query

MyBatis‑Flex

QueryWrapper query = QueryWrapper.create()
    .select().from(ACCOUNT)
    .leftJoin(ARTICLE).on(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID))
    .where(ACCOUNT.AGE.ge(10));
List<Account> accounts = mapper.selectListByQuery(query);

MyBatis‑Plus // Not supported Fluent‑MyBatis

StudentQuery leftQuery = new StudentQuery("a1").selectAll().where.age().eq(34).end();
HomeAddressQuery rightQuery = new HomeAddressQuery("a2").where.address().like("address").end();
IQuery query = leftQuery
    .join(rightQuery)
    .on(l -> l.where.homeAddressId(), r -> r.where.id()).endJoin()
    .build();
List<StudentEntity> entities = this.mapper.listEntity(query);
Drawback: the DSL does not match SQL intuition and the numerous .end() / .endJoin() calls are easy to miss.

Partial field update

Desired SQL:

UPDATE tb_account SET user_name = "michael", age = 18, birthday = NULL WHERE id = 100;

MyBatis‑Flex

Account account = UpdateEntity.of(Account.class);
account.setId(100); // primary key
account.setUserName("michael");
account.setAge(18);
account.setBirthday(null);
accountMapper.update(account);

MyBatis‑Plus (UpdateWrapper)

UpdateWrapper<Account> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("id", 100);
updateWrapper.set("user_name", "michael");
updateWrapper.set("age", 18);
updateWrapper.set("birthday", null);
accountMapper.update(null, updateWrapper);

Fluent‑MyBatis

AccountUpdate update = new AccountUpdate()
    .update.userName().is("michael")
    .age().is(18)
    .birthday().is(null)
    .end()
    .where.id().eq(100).end();
accountMapper.updateBy(update);

Spring Boot integration of MyBatis‑Flex

Step 1 – Create database table

CREATE TABLE IF NOT EXISTS `tb_account` (
    `id` INTEGER PRIMARY KEY AUTO_INCREMENT,
    `user_name` VARCHAR(100),
    `age` INTEGER,
    `birthday` DATETIME
);
INSERT INTO tb_account(id, user_name, age, birthday) VALUES
(1, '张三', 18, '2020-01-11'),
(2, '李四', 19, '2021-03-21');

Step 2 – Add Maven dependencies

<dependencies>
    <dependency>
        <groupId>com.mybatis-flex</groupId>
        <artifactId>mybatis-flex-spring-boot-starter</artifactId>
        <version>1.7.5</version>
    </dependency>
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
    </dependency>
    <!-- test only -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Step 3 – Configure datasource (application.yml)

# DataSource Config
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/flex_test
    username: root
    password: 123456

Step 4 – Add @MapperScan to the Spring Boot main class

@SpringBootApplication
@MapperScan("com.mybatisflex.test.mapper")
public class MybatisFlexTestApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisFlexTestApplication.class, args);
    }
}

Step 5 – Define entity and mapper

@Data
@Table("tb_account")
public class Account {
    @Id(keyType = KeyType.Auto)
    private Long id;
    private String userName;
    private Integer age;
    private Date birthday;
}

public interface AccountMapper extends BaseMapper<Account> { }

Step 6 – Write a test case

import static com.mybatisflex.test.entity.table.AccountTableDef.ACCOUNT;

@SpringBootTest
class MybatisFlexTestApplicationTests {
    @Autowired
    private AccountMapper accountMapper;

    @Test
    void contextLoads() {
        QueryWrapper queryWrapper = QueryWrapper.create()
            .select()
            .where(ACCOUNT.AGE.eq(18));
        Account account = accountMapper.selectOneByQuery(queryWrapper);
        System.out.println(account);
    }
}

Console output:

Account(id=1, userName=张三, age=18, birthday=Sat Jan 11 00:00:00 CST 2020)

Conclusion

MyBatis‑Flex is still under heavy development, exhibits a relatively high bug rate, and has limited production‑grade experience. It can be learned quickly (approximately one week of hands‑on practice), but teams should evaluate its stability before adopting it as the primary ORM for critical projects.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaSpring BootORMMyBatis-PlusFluent-MyBatisMyBatis-Flex
Shepherd Advanced Notes
Written by

Shepherd Advanced Notes

Dedicated to sharing advanced Java technical insights, daily work snippets, and the power of persistent effort.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.