Why MyBatis-Flex Beats MyBatis-Plus: Features, Performance & More

MyBatis-Flex is a lightweight, high‑performance MyBatis enhancement offering zero third‑party dependencies, flexible QueryWrapper support, superior CRUD speed, advanced relational mappings, built‑in data masking, caching, multi‑datasource handling, and customizable SQL auditing, making it a compelling alternative to MyBatis‑Plus for modern Java backend development.

Architect's Tech Stack
Architect's Tech Stack
Architect's Tech Stack
Why MyBatis-Flex Beats MyBatis-Plus: Features, Performance & More

Introduction

MyBatis-Flex is an elegant MyBatis enhancement framework that is lightweight, highly performant and flexible. It provides a built‑in QueryWrapper that greatly reduces SQL writing effort and error risk.

More Lightweight

MyBatis-Flex has no third‑party dependencies beyond MyBatis itself, offering higher autonomy, controllability and stability.

More Flexible

MyBatis-Flex offers a very flexible QueryWrapper supporting association queries, multi‑table queries, multiple primary keys, logical deletion, optimistic locking, data filling, data masking, etc.

Higher Performance

Through a unique architecture without MyBatis interceptors or SQL parsing, MyBatis-Flex achieves exponential performance gains.

Feature Comparison

功能或特点

MyBatis-Flex

MyBatis-Plus

Fluent-MyBatis

对 entity 的基本增删改查

分页查询

分页查询之总量缓存

分页查询无 SQL 解析设计(更轻量,及更高性能)

多表查询:from 多张表

多表查询:left join、inner join 等等

多表查询:union,union all

单主键配置

多种 id 生成策略

支持多主键、复合主键

字段的 typeHandler 配置

除了 MyBatis,无其他第三方依赖(更轻量)

QueryWrapper 是否支持在微服务项目下进行 RPC 传输

未知

逻辑删除

乐观锁

SQL 审计

数据填充

✔️(收费)

数据脱敏

✅(收费)

✔️(收费)

字段权限

✅(收费)

字段加密

✅(收费)

字典回写

✅(收费)

多数据源支持

借助其他框架或收费

多数据源事务管理(@Transactional)

多数据源非Spring项目支持

多租户

动态表名

动态 Schema

Performance Comparison

MyBatis-Flex single‑record query is about 5‑10× faster than MyBatis-Plus.

MyBatis-Flex ten‑record query is about 5‑10× faster than MyBatis-Plus.

MyBatis-Flex pagination query is about 5‑10× faster than MyBatis-Plus.

MyBatis-Flex data update is about 5‑10× faster than MyBatis-Plus.

Official site: https://mybatis-flex.com/

Code Practice

Dependency configuration example (Maven):

<dependency>
    <groupId>com.mybatis-flex</groupId>
    <artifactId>mybatis-flex-spring-boot-starter</artifactId>
    <version>1.5.3</version>
</dependency>
<dependency>
    <groupId>com.mybatis-flex</groupId>
    <artifactId>mybatis-flex-processor</artifactId>
    <version>1.5.3</version>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.0.31</version>
</dependency>
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

One‑to‑One Association @RelationOneToOne

Example code:

@Data
public class Account implements Serializable {
    @Id(keyType = KeyType.Auto)
    private Long id;
    private String userName;
    @RelationOneToOne(selfField = "id", targetField = "accountId")
    private IDCard idCard;
}

@Data
@Table(value = "tb_idcard")
public class IDCard implements Serializable {
    private Long accountId;
    private String cardNo;
    private String content;
}
If the selfField is the primary key and the table has only one primary key, the annotation can be simplified to @RelationOneToOne(targetField = "accountId").

One‑to‑Many Association @RelationOneToMany

Example code:

@Data
public class Account implements Serializable {
    @Id(keyType = KeyType.Auto)
    private Long id;
    private String userName;
    @RelationOneToMany(selfField = "id", targetField = "accountId")
    private List<Book> books;
}

@Data
@Table(value = "tb_book")
public class Book implements Serializable {
    @Id(keyType = KeyType.Auto)
    private Long id;
    private Long accountId;
    private String title;
}

If the collection type is a Map, specify mapKeyField to define the map key column.

@RelationOneToMany(selfField = "id", targetField = "accountId", mapKeyField = "id")
private Map<Long, Book> books;

Many‑to‑One Association @RelationManyToOne

Example code:

@Data
public class Book implements Serializable {
    @Id(keyType = KeyType.Auto)
    private Long id;
    private Long accountId;
    private String title;
    @RelationManyToOne(selfField = "accountId", targetField = "id")
    private Account account;
}

Many‑to‑Many Association @RelationManyToMany

Example code:

@Data
public class Account implements Serializable {
    @Id(keyType = KeyType.Auto)
    private Long id;
    private String userName;
    @RelationManyToMany(
        joinTable = "tb_role_mapping",
        selfField = "id", joinSelfColumn = "account_id",
        targetField = "id", joinTargetColumn = "role_id"
    )
    private List<Role> roles;
}

@Data
@Table(value = "tb_role")
public class Role implements Serializable {
    private Long id;
    private String name;
}

Parent‑Child Recursive Query

Example code (default recursion depth 3, can be set to 10):

QueryWrapper qw = QueryWrapper.create();
qw.where(MENU.PARENT_ID.eq(0));
RelationManager.setMaxDepth(10);
List<Menu> menus = menuMapper.selectListWithRelationsByQuery(qw);

Chain Operations

MyBatis-Flex provides QueryChain, UpdateChain and DbChain for fluent query, update and delete operations.

@SpringBootTest
class ArticleServiceTest {
    @Autowired
    ArticleService articleService;

    @Test
    void testChain() {
        List<Article> articles = articleService.queryChain()
            .select(ARTICLE.ALL_COLUMNS)
            .from(ARTICLE)
            .where(ARTICLE.ID.ge(100))
            .list();
    }
}

When not in a service, you can create a chain with the mapper:

List<Article> articles = QueryChain.of(mapper)
    .select(ARTICLE.ALL_COLUMNS)
    .from(ARTICLE)
    .where(ARTICLE.ID.ge(100))
    .list();

Data Masking

Use @ColumnMask with built‑in mask types (e.g., Masks.CHINESE_NAME) to mask sensitive fields such as names, phone numbers, ID numbers, etc.

@Table("tb_account")
public class Account {
    @Id(keyType = KeyType.Auto)
    private Long id;
    @ColumnMask(Masks.CHINESE_NAME)
    private String userName;
}

Phone number masking

Fixed‑line masking

ID number masking

License‑plate masking

Address masking

Email masking

Password masking

Bank card masking

Custom mask registration:

MaskManager.registerMaskProcessor("customRule", data -> data);

Temporarily disable masking:

try {
    MaskManager.skipMask();
    // queries without masking
    accountMapper.selectListByQuery(...);
} finally {
    MaskManager.restoreMask();
}

Data Caching

MyBatis second‑level cache is supported but not ideal for distributed environments; Spring Cache is recommended.

@Service
@CacheConfig(cacheNames = "account")
public class AccountServiceImpl extends CacheableServiceImpl<MyAccountMapper, Account> {
    @Override
    @CacheEvict(allEntries = true)
    public boolean remove(QueryWrapper query) {
        return super.remove(query);
    }
    @Override
    @Cacheable(key = "#id")
    public Account getById(Serializable id) {
        return super.getById(id);
    }
    // other CRUD methods with appropriate @CacheEvict / @Cacheable annotations
}

SQL Auditing

Enable auditing: AuditManager.setAuditEnable(true); Provide a custom MessageFactory to create audit messages:

public class MyMessageFactory implements MessageFactory {
    @Override
    public AuditMessage create() {
        AuditMessage message = new AuditMessage();
        // set platform, module, url, user, etc.
        return message;
    }
}
AuditManager.setMessageFactory(new MyMessageFactory());

Implement MessageReporter to send audit logs (e.g., to HTTP endpoint, log system, or message queue):

public class MyMessageReporter implements MessageReporter {
    @Override
    public void sendMessages(List<AuditMessage> messages) {
        // send messages to desired destination
    }
}

Implement MessageCollector for immediate collection (e.g., console output):

public class MyMessageCollector implements MessageCollector {
    @Override
    public void collect(AuditMessage auditMessage) {
        System.out.println(auditMessage.getFullSql());
    }
}

Two built‑in collectors are available: ScheduledMessageCollector (periodic sending via MessageReporter) and ConsoleMessageCollector (outputs to console).

Multiple Data Sources

Configuration example (application.yml):

mybatis-flex:
  datasource:
    ds1:
      url: jdbc:mysql://127.0.0.1:3306/db
      username: root
      password: 123456
    ds2:
      url: jdbc:mysql://127.0.0.1:3306/db2
      username: root
      password: 123456

Four ways to select a data source:

Programmatically: DataSourceKey.use("ds2") (must be cleared with DataSourceKey.clear()).

Annotation on mapper class: @UseDataSource("ds2").

Annotation on mapper method: @UseDataSource("ds2").

Annotation on entity: @Table(dataSource="ds2") – entity CRUD uses the specified source.

Priority order:

DataSourceKey.use() > @UseDataSource on method > @UseDataSource on class > @Table(dataSource="...")

.

try {
    DataSourceKey.use("ds2");
    List<Row> rows = Db.selectAll("tb_account");
    System.out.println(rows);
} finally {
    DataSourceKey.clear();
}

Overall, MyBatis-Flex provides a comprehensive, lightweight, and high‑performance alternative to MyBatis‑Plus for Java backend development.

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.

JavaormMybatis-FlexDataMaskingSQLAuditing
Architect's Tech Stack
Written by

Architect's Tech Stack

Java backend, microservices, distributed systems, containerized programming, and more.

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.