目录
每个service中不要包含别的service的mapper需要调用尽量走service:
架构师格言:
在实现相同功能的情况下,谁写的代码少谁就牛逼。
背景:
最近开发了一个关于用户设置企业的新功能,本地测试与测试环境测试均能通过,但是发布到线上以后,当用户选择的数据量很大以后,用户点击确定按钮就一直转圈,最后数据库报:Caused by: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction 异常。由于数据量比较大业务比较复杂,该问题一共耗时三天才解决,在解决问题的过程中涉及到很多东西,包括sql优化,代码优化,在解决该问题的过程中得到了架构师的手把手教学,让我收获颇深,真正体会了什么叫代码之美。
代码走读:
讲真,我觉得代码走读和你裸着站在人群中被指指点点那种感受是一样的,被嫌弃你的胸小,屁股小......,代码走读两小时,我全程黑着脸,感觉我是一个没有良心的码畜(写代码的畜生)。
代码优化总结:

方法抽取:
比如这段代码,我甚至都不需要具体点进去看每个方法干了啥我就知道addApplication干了啥,
@Override
public void addApplication(ApplicationDto applicationDto) {
Application application = JsonUtil.object2Object(applicationDto, Application.class);
validateAppName(applicationDto);
validateAppRoute(applicationDto);
validateAppRouteName(applicationDto);
applicationMapper.insert(application);
}

但如果我写成下面这样,看起来就会很难受。一般我们的方法体都不应该过于长,太长了可读性非常差,自己读完后面的,忘记了前面在干啥。
@Override
public void addApplication(ApplicationDto applicationDto) {
Application application = JsonUtil.object2Object(applicationDto, Application.class);
LambdaQueryWrapper<Application> queryWrapper1 = Wrappers.<Application>query().lambda().eq(Application::getName, applicationDto.getName()).eq(Application::isDel, 0);
Optional.ofNullable(applicationDto.getId()).ifPresent(id -> queryWrapper1.and(wrapper -> wrapper.ne(Application::getId, applicationDto.getId())));
Application application1 = getOne(queryWrapper1);
if (!Objects.isNull(application1)) {
throw new BasicBusinessException("系统名已存在!");
}
LambdaQueryWrapper<Application> queryWrapper2 = Wrappers.<Application>query().lambda().eq(Application::getRoute, applicationDto.getRoute()).eq(Application::isDel, 0);
Optional.ofNullable(applicationDto.getId()).ifPresent(id -> queryWrapper2.and(wrapper -> wrapper.ne(Application::getId, applicationDto.getId())));
Application application2 = getOne(queryWrapper2);
if (!Objects.isNull(application2)) {
throw new BasicBusinessException("系统路由已存在!");
}
LambdaQueryWrapper<Application> queryWrapper3 = Wrappers.<Application>query().lambda().eq(Application::getRouteName, applicationDto.getRouteName()).eq(Application::isDel, 0);
Optional.ofNullable(applicationDto.getId()).ifPresent(id -> queryWrapper3.and(wrapper -> wrapper.ne(Application::getId, applicationDto.getId())));
Application application3 = getOne(queryWrapper3);
if (!Objects.isNull(application3)) {
throw new BasicBusinessException("系统标识已存在!");
}
applicationMapper.insert(application);
}

常量提取:
将代码中的常量,以及魔法值的数据提取出来,以便公用和可读。
public Long createDefaultTenantRole(String tenanName, Long tenantId) {
Role tenantDefaultRole = Role.builder()
.name(tenanName + "管理员")
.code("ROLE_TENANT_ADMIN_" + UuidUtil.get15UUID())
.classify(1)
.tenantId(tenantId)
.type(0)
.build();
roleService.save(tenantDefaultRole);
return tenantDefaultRole.getId();
}

将常量提取出来:
private static final String ROLE_TENANT_VISIT = "ROLE_TENANT_VISIT_";

注释明确:
复杂的方法逻辑请写好注释,方便复盘,也方便他人后期的维护,不然你走后你写的垃圾代码将使后来者生不如死。注释不是越多越好,只写重要的那么几句就行了,太多了还以为你是在写小说呢,方法取名的时候尽量不要走非主流路线,带有实际意义一些,不会的单词就谷歌翻译,别写汉语拼音了,真的太low了。很多很长的单词没必要全部写下来,取前面三四个字母就行了,像这样:kubernetes=k8s ,JsonToObject= Json2Obj。
每个service中不要包含别的service的mapper需要调用尽量走service:
我想大部分人写serviceImpl都是这样的:
public class ApplicationServiceImpl implements ApplicationService {
private final ApplicationMapper applicationMapper;
private final RoleMenuMapper roleMenuMapper;
private final TenantSystemMapper tenantSystemMapper;
private final SystemConfigMapper systemConfigMapper;
private final UserRoleMapper userRoleMapper;
private final MenuMapper menuMapper;
}
都是直接包含别人的service的mapper,可能我们刚开始使用Mvc模式的时候就是这样写的,然后我以为这样写没什么毛病但是架构师建议我不要这样写,他建议我这样写:
public class ApplicationServiceImpl implements ApplicationService {
private final ApplicationMapper applicationMapper;
private final RoleMenuServiceImpl roleMenuService;
private final TenantSystemServiceImpl tenantSystemService;
private final SystemConfigServiceImpl systemConfigService;
private final UserRoleServiceImpl userRoleService;
private final MenuServiceImpl menuService;
}

-
这样写有什么好处呢?
- service比mapper拥有的功能更强、更全面。
- 很多操作都需进行逻辑校验后才能被操作,而很多这样的逻辑都是通用的,比如更新一条数据之前先要校验是否被使用,如果被使用那就不能更新,如果引用别人的service就能够实现方法复用。
- 高并发场景下能够避免事务太长导致mysql报锁等待超时异常。
巧用框架:
public class ApplicationServiceImpl extends ServiceImpl<ApplicationMapper, Application> implements ApplicationService {
private final ApplicationMapper applicationMapper;
就拿mybatisPlus来说,当我们继承了ServiceImpl以后,我们能够发现ServiceImpl中有这么多已实现的方法可以用,那我们在使用的时候就尽量用上。
比如:获取一条数据:我呢吧可以用getOne()而不是通过applicationMapper再去写一个。
- 前后端做好传参约定:
约定>规范>编码