MiniMall:CRUD的代码是不可能写得?是的,我都帮你写好了

虽然从整个项目的角度来说,我们把整个项目拆成了一个个微服务,这一点是有别于单体应用的。但是从一个业务模块本身的实现,不管是单体应用架构还是微服务架构,都会有控制层、业务层、持久层,这一点是不会变的。而我们今天要说的,就是针对每一层的增删改查操作进行架构封装,这几个常见操作的实现基本上都是一样的,实在没有必要在每个业务模块里写重复的代码,我们也不是这样的程序员对不对?

友情提示:看本篇博客,最好结合源码一起来。每次访问github比较慢的,可以将源码检出下载到本地。

1. 持久层

因为我们的持久层使用的是Spring Data JPA框架实现,站在巨人的肩膀上,自然也就没我们啥事了。Spring Data JPA提供核心的接口Repository来表示持久层的接口定义,该接口有很多的子接口,其中就有一个CrudRepository接口,里面定义了常见的CRUD操作,比如savesaveAllfindById等,具体如下:

@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
    <S extends T> S save(S var1);

    <S extends T> Iterable<S> saveAll(Iterable<S> var1);

    Optional<T> findById(ID var1);

    boolean existsById(ID var1);

    Iterable<T> findAll();

    Iterable<T> findAllById(Iterable<ID> var1);

    long count();

    void deleteById(ID var1);

    void delete(T var1);

    void deleteAll(Iterable<? extends T> var1);

    void deleteAll();
}

但是大家可以看到,在CrudRepository接口中并没有分页查询以及规范查询方法。这就不得不提到另一个常见的接口JpaSpecificationExecutor,其代码如下:

public interface JpaSpecificationExecutor<T> {
    Optional<T> findOne(@Nullable Specification<T> var1);

    List<T> findAll(@Nullable Specification<T> var1);

    Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);

    List<T> findAll(@Nullable Specification<T> var1, Sort var2);

    long count(@Nullable Specification<T> var1);
}

而在我们的项目中,通常一个业务模块有搜索界面、新建编辑界面、查看界面,也就表示一个模块的持久化接口往往需要实现CrudRepositoryJpaSpecificationExecutor两个接口,也正是因为如此,在我们的项目持久层中,提供了BaseRepository接口作为所有具备CRUD以及分页查询功能的父接口:

@NoRepositoryBean
public interface BaseRepository<T extends IsEntity> extends CrudRepository<T, String>, JpaSpecificationExecutor<T> {

}

那么以项目模块举例,其持久层接口直接继承BaseRepository即可:

public interface StoreRepository extends BaseRepository<Store> {

    /**
     * 根据代码查找
     *
     * @param code
     * @return
     */
    Optional<Store> findByCode(String code);
}

2. 业务层

2.1 接口封装

业务层主要是接收控制层的方法调用以及调用持久层进行数据的处理,这一层是集中处理业务的地方,也即真正的业务核心所在。对于业务层这一层的CRUD封装,我们定义了CrudService接口,其代码如下:

public interface CrudService<T extends IsEntity> {

    /**
     * 新建实体,新增一条记录
     *
     * @param entity 实体
     * @return 新增记录的主键值
     */
    String save(T entity);

    /**
     * 根据主键uuid,删除一条记录
     *
     * @param uuid 主键uuid
     */
    void deleteById(String uuid);

    /**
     * 通过主键id,获取实体
     *
     * @param uuid 主键id
     * @return 实体
     */
    T findById(String uuid);

    /**
     * 通过主键集合获取实体,并将结果转换成map
     *
     * @param uuids
     * @return
     */
    Map<String, T> findAllByIds(Set<String> uuids);

    /**
     * 根据关键字分页查询
     *
     * @param definition
     * @return
     */
    QueryResult<T> query(QueryDefinition definition);
}

额外的,除了对CRUD进行封装外,我们还提供了CacheServiceSupportStateService两个接口分别用于支持缓存和状态改变操作(基础资料是有状态的,比如项目有使用中和已停用两种状态)。其代码分别如下:

  • CacheService
public interface CacheService {

    /**
     * 获取模块的key前缀常量定义,可选值{@link com.autumn.mall.commons.api.MallModuleKeyPrefixes}
     *
     * @return
     */
    String getModuleKeyPrefix();
}
  • SupportStateService
public interface SupportStateService<S> {

    /**
     * 修改状态
     *
     * @param uuid        唯一标识
     * @param targetState 目标状态
     */
    void changeState(String uuid, S targetState);
}

还是以项目模块举例,其业务层接口如下:

public interface StoreService extends CrudService<Store>, CacheService, SupportStateService<UsingState> {

}

2.2 实现类封装

除了对接口的封装外,我们还需要对CRUD的实现进行封装,这才是我们的重点,否则只是简简单单地对接口进行封装,实际上其意义并不大。

封装后的业务层接口实现类,是一个抽象类。该实现类实现了CrudServiceCacheService两个接口,也即表示对于大部分的业务模块来说,都是支持CRUD操作和缓存的。其代码如下:

public abstract class AbstractServiceImpl<T extends IsEntity> implements CrudService<T>, CacheService {
    public abstract BaseRepository<T> getRepository();

    public abstract SpecificationBuilder getSpecificationBuilder();
}

具体实现见源代码,重点说一说继承该业务层接口抽象实现类需要实现的两个抽象方法。

  • getRepository():返回一个持久层接口,其返回值就是我们持久层的父接口。

  • getSpecificationBuilder():返回一个规范查询定义的构造器,用于针对规范查询的条件构造。

老规矩,还是以项目模块的业务层实现类举例,其代码如下(细节见源码):

@Service
public class StoreServiceImpl extends AbstractServiceImpl<Store> implements StoreService {

    @Autowired
    private StoreRepository storeRepository;
    @Autowired
    private StoreSpecificationBuilder specificationBuilder;

    @Override
    public StoreRepository getRepository() {
        return storeRepository;
    }

    @Override
    public SpecificationBuilder getSpecificationBuilder() {
        return specificationBuilder;
    }

    @Override
    public String getModuleKeyPrefix() {
        return MallModuleKeyPrefixes.INVEST_KEY_PREFIX_OF_STORE;
    }
}

3. 控制层

说完持久层和业务层,我们再说看看控制层是怎么对CRUD操作进行封装的。当然……还是以项目模块举例,先来看看项目模块控制层的代码:

@Api(value = "项目管理")
@RestController
@RequestMapping("/store")
public class StoreController extends AbstractSupportStateController<Store, UsingState> implements StoreApi {

    @Autowired
    private StoreService storeService;

    @Override
    public SupportStateService<UsingState> getSupportStateService() {
        return storeService;
    }

    @Override
    public CrudService<Store> getCrudService() {
        return storeService;
    }

    @Override
    public List<String> getSummaryFields() {
        return Arrays.asList(UsingState.using.name(), UsingState.disabled.name());
    }
}

惊不惊喜,意不意外,没有看到一行CRUD的代码。来捋一捋类的继承关系:
在这里插入图片描述

  • AbstractSupportStateController

其中AbstractSupportStateController用来封装状态改变的操作,子类需要实现getSupportStateService()方法,还记得项目模块的业务层接口是怎么定义的吗?StoreService继承了SupportStateService来表示这是一个支持实体状态改变的业务。

public abstract class AbstractSupportStateController<T extends IsEntity, S extends Enum> extends AbstractController<T> {

    @PutMapping("/{uuid}")
    public ResponseResult changeState(@PathVariable("uuid") String uuid, @RequestParam("targetState") S targetState) {
        getSupportStateService().changeState(uuid, targetState);
        return new ResponseResult(CommonsResultCode.SUCCESS);
    }

    public abstract SupportStateService<S> getSupportStateService();
}
  • AbstractController

在这个抽象实现类中用来封装了控制层的CRUD操作,其中有一个很重要的抽象方法就是子类需要实现一个返回值为CrudService的方法:

public abstract CrudService<T> getCrudService();

至此,我们项目对CRUD的代码已经封装的差不多了,每个业务模块的各个层需要做的就是实现各种抽象方法和做个性化的实现。

——End——
更多精彩分享,可扫码关注微信公众号哦。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值