目錄
每個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再去寫一個。
- 前後端做好傳參約定:
約定>規範>編碼