CRUD也有他的道道(一)

22 阅读4分钟

最近面试,普遍看到一个问题,大多候选人因为公司项目只是一些CRUD的工作,所以其技术能力的提升有限,以此算是一个自身能力不突出的解释吧,虽然这种情况也是正常,但不可否认,"九层之台,起于累土",CRUD就是这些土。技术架构固然重要,但砖块垒得好不好何尝不重要?

今天就来扒一扒CRUD中的一些门道!

当然门道很多,无法一一列举,只找一些来看看

一、create or update?

先来看两个服务接口设计:

// 第一组接口
public BaseResult createOrUpdate(UserRequest request);

// 第二组接口
public BaseResult create(UserRequest request);

public BaseResult update(UserUpdateRequest request);

这是比较常见的两种设计,一种将创建和更新合并成一个接口,以期在一个接口中实现更多的功能,一个则是分开,各干各的,需要调用者自己来决定使用创建还是更新。

在讨论这个问题之前,有一些前置的条件,我们这里更多讲的是核心服务的设计,而非与前端对接的接口设计,两者面对的用户不同,取舍不同,后面有机会再细说如何协调前端业务接口与后端核心服务的对接,毕竟这涉及到系统架构设计,一两句话也讲不清楚!
当然这里核心服务这个概念可能就会有不同理解,先按下不表,以后有机会再解释,目前可以理解成为内部其他服务提供服务的服务...有点绕

先说结论,于我而言,我更倾向于后者,即将创建和更新分开来,这使得服务接口的职责更加明确,不会产生歧义。并且作为调用者来说,应该要自己能够区分清楚是创建还是更新,核心服务不该被业务牵着走,核心服务有自己的设计目标,虽然同为服务业务,但其接口设计上,应更多考虑通用性,扩展性,职责分工明确,而不是随着业务的变化而常常需要改变接口,这一系列总结起来就一个目标,接口足够稳定(不是运行稳定,是设计稳定,以不变应万变,以小变应大变)!

当然这种设计思想不仅适合服务接口设计,也适合内部不同层的接口设计,毕竟这个设计思想的源头就是最基本的设计原则:单一职责

二、影响行数重要吗?

一般在update或remove的时候,会有影响行数的概念,很多人容易忽略这个信息,在代码里直接调用操作方法,而不加处理这个返回结果:

@Override
public void update(xxx object) {
    xxxDAO.updateByYyy(object);
}

这么一般不会有什么问题,毕竟有些操作即使没成功,再试一下也不是不可以,但从设计的角度来看,这里就不够严谨了,缺失了异常的处理。

那么如果我们要去处理这个返回值,该抛出异常吗?未必,一般来说可以不抛出,但要记个日志,而是否属于业务上的错误,则需要上层服务方法来做决定。

这里还是有个隐含的前提,按照分层设计,此处展示的代码属于系统核心层里的组件层,或者换个说法,就是封装一下数据库的调用,并不做什么业务处理,所以这里的方法是给内部其他业务功能去调用的,因此这里不会做异常与否的决策,而是交给上层(业务层)决定

影响行数是否为0,大部分时候它不为0,但如果没有这条日志,或这个异常的处理,在真正遇到问题时可能会一脸懵,无从下手,毕竟这个问题若没日志,可以藏得很好!

    @Override
    public void update(xxx object) {
        Long impact = nnnDAO.update(object);
        if (impact == null || impact.intValue() <= 0) {
            logger.warn("xxx失败,影响行数为0,object={}", object);
        }
    }

也许这段代码从上线,到软件生命周期结束都不会进入一次,但正是这样的无用之用,保障了系统在异常情况下能够被及时定位和解决

先这么些吧,后面有空再接着写,明天元旦,大家按时下班,新年快乐!