Master MyBatis-Plus Advanced Features: Batch Insert, Logical Delete, Auto Fill, Type Handler, Dynamic Tables and More
This tutorial walks through MyBatis-Plus's advanced capabilities—including high‑performance batch inserts, logical deletion with global config, automatic timestamp and user fields via MetaObjectHandler, JSON type handling, dynamic table name resolution, and multi‑tenant support—showing concrete code, configuration, and benchmark results for each feature.
First, a tb_user table is created with typical columns (id, user_no, nickname, email, phone, gender, birthday, is_delete, create_time, update_time, create_by, update_by, address) and a corresponding User entity annotated with @TableName("tb_user").
1. Batch Insert
The article compares two approaches for inserting 1,000 User records. The mapper‑level test inserts each record individually, taking 42516 ms. The service‑level saveBatch method reduces the time to 19385 ms, but still remains slow for a service response.
@Test
public void testMapperBatchAdd() {
List<User> users = new ArrayList<>();
for (long i = 1; i <= 1000; i++) {
User user = User.builder()
.id(i)
.userNo("No-" + i)
.nickname("哈哈")
.phone("12345678901")
.email("[email protected]")
.birthday(new Date())
.gender(0)
.isDelete(0)
.build();
users.add(user);
}
long start = System.currentTimeMillis();
users.forEach(user -> userDAO.insert(user));
long end = System.currentTimeMillis();
System.out.println("执行时长:" + (end - start) + "毫秒");
}Adding rewriteBatchedStatements=true to the MySQL JDBC URL (and using driver ≥ 5.1.13) dramatically improves performance; the same service‑level batch now finishes in 1364 ms.
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://ip:3306/db_test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true2. Logical Delete
Logical deletion is enabled via global configuration:
mybatis-plus:
global-config:
db-config:
logic-delete-field: isDelete
logic-delete-value: 1
logic-not-delete-value: 0Calling userDAO.deleteById(1L) generates an UPDATE tb_user SET is_delete=1 WHERE id=? AND is_delete=0 statement, as shown in the debug log. Queries automatically add is_delete=0 to filter out logically deleted rows.
When a column participates in a unique index, logical deletion can cause conflicts. Adding a delete_time column to the unique index resolves this, as demonstrated by the two scenarios before and after the change.
3. Automatic Field Filling
A base class BaseDO defines common audit fields with @TableField(fill = FieldFill.INSERT) or INSERT_UPDATE. A custom MetaObjectHandler implementation populates these fields when they are null.
public class DefaultDBFieldHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
if (metaObject.getOriginalObject() instanceof BaseDO) {
BaseDO baseDO = (BaseDO) metaObject.getOriginalObject();
Date current = new Date();
if (Objects.isNull(baseDO.getCreateTime())) {
baseDO.setCreateTime(current);
}
if (Objects.isNull(baseDO.getUpdateTime())) {
baseDO.setUpdateTime(current);
}
baseDO.setCreateBy(1001L);
baseDO.setUpdateBy(1002L);
}
}
@Override
public void updateFill(MetaObject metaObject) {
Object modifyTime = getFieldValByName("updateTime", metaObject);
if (Objects.isNull(modifyTime)) {
setFieldValByName("updateTime", new Date(), metaObject);
}
Object modifier = getFieldValByName("updateBy", metaObject);
if (Objects.isNull(modifier)) {
setFieldValByName("updateBy", 1002L, metaObject);
}
}
}The handler is registered as a Spring bean:
@Bean
public MetaObjectHandler defaultMetaObjectHandler() {
return new DefaultDBFieldHandler();
}Inserting a User without explicitly setting audit fields results in the generated SQL containing populated create_time, update_time, create_by, and update_by values.
4. Complex Field Type Handling
For JSON or array columns, a custom Address class is defined and stored as a JSON string using JacksonTypeHandler. The entity must enable autoResultMap = true to map the JSON automatically.
@Data
public class Address {
private Long id;
private String province;
private String city;
private String region;
private String address;
} @Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "tb_user", autoResultMap = true)
public class User extends BaseDO {
@TableId(type = IdType.AUTO)
private Long id;
private String userNo;
private String nickname;
private String email;
private String phone;
private Integer gender;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date birthday;
private Integer isDelete;
@TableField(typeHandler = JacksonTypeHandler.class)
private Address address;
}Inserting a user with an Address object produces a JSON string in the address column, and a subsequent query returns a fully populated Address instance.
5. Dynamic Table Name
The dynamic‑table‑name plugin is configured via a MybatisPlusInterceptor and a custom TableNameHandler. The handler reads a request‑scoped tableNo parameter and appends _1 or _2 to the original table name.
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor());
return interceptor;
}
@Bean
public DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor() {
DynamicTableNameInnerInterceptor interceptor = new DynamicTableNameInnerInterceptor();
interceptor.setTableNameHandler(new MyTableNameHandler());
return interceptor;
} public class MyTableNameHandler implements TableNameHandler {
@Override
public String dynamicTableName(String sql, String tableName) {
Map<String, Object> paramMap = RequestDataHelper.getRequestData();
if (paramMap == null || paramMap.isEmpty()) {
return tableName;
}
int random = (int) paramMap.get("tableNo");
String suffix = (random % 2 == 1) ? "_2" : "_1";
return tableName + suffix;
}
}The helper class stores request parameters in a ThreadLocal map, making them accessible to the handler.
6. Multi‑Tenant Plugin
MyBatis‑Plus also offers a multi‑tenant interceptor; the article references a previous analysis for detailed usage.
7. Summary
All the demonstrated features—batch operations, logical deletion, automatic field filling, JSON type handling, dynamic table resolution, and multi‑tenant support—are practical extensions that improve code efficiency, reduce boilerplate, and address common enterprise scenarios when using MyBatis‑Plus in backend development.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Shepherd Advanced Notes
Dedicated to sharing advanced Java technical insights, daily work snippets, and the power of persistent effort.
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.
