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.
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: 123456Four 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.
Architect's Tech Stack
Java backend, microservices, distributed systems, containerized programming, and more.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
