Excel导入导出是开发中常见的需求,但重复的字段映射工作往往让人望而生畏。本文将分享如何通过注解式编程提升效率。
当业务提出"导入用户Excel到三I张表"的需求时,我们通常会这样实现:

典型实现方案如下:
// 导入:扁平 → 三I张表
User user = new User();
user.setName(row.getName());
userMapper.insert(user);Profile profile = new Profile();
profile.setUserId(user.getId());
profile.setPhone(row.getPhone());
profile.setWech@t(row.getWech@t());
profileMapper.insert(profile);Address address = new Address();
address.setUserId(user.getId());
address.setDetail(row.getAddress());
addressMapper.insert(address);
导出操作则需要逆向处理:
// 导出:三I张表 → 扁平
User user = userMapper.selectById(id);
Profile profile = profileMapper.selectByUserId(id);
Address address = addressMapper.selectByUserId(id);ExcelRow row = new ExcelRow();
row.setName(user.getName());
row.setPhone(profile.getPhone());
row.setWech@t(profile.getWech@t());
row.setAddress(address.getDetail());
这种模式导致字段需要反复处理。
每个字段需要处理四次:导入设置、导出获取、单元测试两次。新增列需要修改4处,模板变更要改8处。长期积累后,Service层会堆积大量结构转换代码——技术含量低、易出bug、重复性高。
首先明确:Excel的扁平结构并非缺陷。它专为人类设计——便于编辑、查看和传递,而非追求技术优雅。
| 视角 | 结构 |
|---|---|
| Excel | 一行一行的扁平表 |
| 业务对象 | 嵌套的领域模型 |
| 数据库 | 多张表 + JOIN |
三者结构差异才是问题核心。
每次导入导出本质都是结构转换:在Excel的扁平结构与数据库的多表结构间建立映射。若每次都手动处理,将变成永无止境的重复劳动。
解决方案是将字段归属关系直接标注在Bean上:
@Data
public class UserExcelRow {
@ExcelProperty("姓名")
private String name; @ExcelProperty("手机号")
@SetValue("profile")
private String phone; @ExcelProperty("微信号")
@SetValue("profile")
private String wech@t; @ExcelProperty("地址")
@SetValue("address")
private String detail;
}
注意双注解协同工作:
@ExcelProperty 处理列名映射@SetValue 处理结构嵌套一套Bean定义,统一注解配置,支持导入导出复用。
UserExcelRow row = readFromExcel();// 扁平Bean转多级JSONMap
JSONMap json = new JSONMap();
BeanUtil.copyAsSource(row, json, false);
// json = {
// "name": "张三",
// "profile": {"phone": "138...", "wech@t": "dk_test"},
// "address": {"detail": "上海浦东"}
// }// 各表分别处理
User user = json.as(User.class);
Profile profile = json.getObj("profile", Profile.class);
Address address = json.getObj("address", Address.class);userMapper.insert(user);
profile.setUserId(user.getId()); profileMapper.insert(profile);
address.setUserId(user.getId()); addressMapper.insert(address);
User user