代码写了那么多,CURD也可以如此简单(上)?

[toc]

话说进入一家公司,如果经常CURD,是不是感觉很枯燥,但是自己有没有想过,写了一个模块又一个模块,怎么能让自己的代码稍微提高些逼格呢?本文就是基于此背景,应运而生。

1.前言

话说曾经在一家公司,尤其是参与了后端给运营端比如某某平台,参与的大多数需求,常年日积月累就是一个个增加、删除、编辑、查询列表等等。写的多了,或多或少会有些感慨,哪部分代码是可以抽象出来,哪些代码可以不再CP/CV了,就这样经过一代代升华,诞生了今天的主题-基于SpringMVC的一个三层抽象模块封装-BaseModule

2.思考

日常最多的功能模块具备的特征如下:

  • 功能模块都具备新增、编辑、查看详情、分页查询。
  • 数据ORM对象大多是单表,也有甚少的关联查询操作。
  • 返回给前端数据模型和业务处理逻辑抽象部分相同。
  • 都是基于SpringMVC+MyBatis框架实现相关功能。

3.架构

整体设计
从上图,我们可以看到:

  • 第一个区域即本文抽象的基础模块,称之为抽象模块(Abstract Module)。它提供了BaseMapperBaseServiceBaseFacade的抽象封装,具备了日常的通用功能操作,无需再编码重复的代码。另外,以Impl结尾的BaseServiceImplBaseFacadeImpl实现了相关对象的依赖注入,使得你的代码只需要继承该抽象类即可。
  • 第二个区域即你的功能模块需要使用抽象部分,比如继承相关接口或者抽象类,即可以实现扩展。

这里需要特别说明一下,为何要引入一个Facade层,其实我们知道为了对Service进行包装,比如我们把VO转换Entity,比如我们新增、删除需要返回统一的Result协议类型。

4.设计

BaseMapper

DAO层的抽象封装,自己功能模块的Mapper需要继承该接口。

package org.suze.framework.base;


import org.suze.framework.base.page.PageForm;

import java.io.Serializable;
import java.util.List;

/**
 * @description: Mapper基类
 * @Date : 下午3:36 2018/2/5
 * @param <E> entity对象
 * @param <PK> 主键,1、返回自增ID;2:查询对象主键;3:删除对象主键(联合主键)
 * @param <F> 封装查询参数
 * @Author : 石冬冬-Heil Hitler
 */
public interface BaseMapper<E extends Serializable,PK,F extends Serializable>  {
    /**
     * 分页加载数据
     * @param form
     * @return
     */
    List<E> queryByPage(PageForm<F> form);
    /**
     * 分页查询数量
     * @param form
     * @return
     */
    int queryCount(PageForm<F> form);
    /**
     * 查询列表
     * @param form
     * @return
     */
    List<E> queryList(F form);

    /**
     * 根据主键id查询对象
     * @param uniqueKey
     * @return E
     */
    E selectByPrimaryKey(PK uniqueKey);

    /**
     * 根据主键删除
     * @param uniqueKey
     * @return
     */
    int deleteByPrimaryKey(PK uniqueKey);

    /**
     * 有选择性的新增对象
     * @param record
     * @return 主键id
     */
    int insertSelective(E record);

    /**
     * 新增对象
     * @param record
     * @return 主键id
     */
    int insert(E record);

    /**
     * 有选择性的更新对象
     * @param record
     * @return
     */
    int updateByPrimaryKeySelective(E record);

    /**
     * 更新
     * @param record
     * @return
     */
    int updateByPrimaryKey(E record);
    /**
     * 批量新增
     * @param list
     * @return
     */
    int batchInsert(List<E> list);

    /**
     * 批量新增,忽略已存在数据
     * @param list
     * @return
     */
    int batchInsertIgnore(List<E> list);

    /**
     * 批量删除
     * @param list
     * @return
     */
    int batchDelete(List<E> list);

    /**
     * 根据条件删除
     * @param form
     * @return
     */
    int deleteByForm(F form);

}

复制代码

BaseService

Service层的抽象封装,相应Service需要继承该接口。

package org.suze.framework.base;


import org.suze.framework.base.page.PageForm;

import java.io.Serializable;
import java.util.List;

/**
 * BaseService
 * @Date : 2019/5/31 下午2:28
 * @Author : 石冬冬-Seig Heil
 * @param <E> 数据库实体对象
 * @param <P> 查询主键
 * @param <F> 查询条件Form对象
 */
public interface BaseService<E extends Serializable,P,F extends Serializable> {
    /**
     * 分页加载数据
     * @param form
     * @return
     */
    List<E> queryByPage(PageForm<F> form);
    /**
     * 分页查询数量
     * @param form
     * @return
     */
    int queryCount(PageForm<F> form);
    /**
     * 查询实体对象
     * @param primaryKey
     * @return
     */
    E queryRecord(P primaryKey);
    /**
     * 新增实体对象
     * @param record
     * @return
     */
    int insertRecord(E record);
    /**
     * 修改实体对象
     * @param record
     * @return
     */
    int updateRecord(E record);

    /**
     * 修改实体对象
     * @param record
     * @return
     */
    int updateByPrimaryKeySelective(E record);
    /**
     * 删除对象
     * @param primaryKey
     * @return
     */
    int deleteRecord(P primaryKey);
    /**
     * 批量新增
     * @param list
     * @return
     */
    int batchInsert(List<E> list);

    /**
     * 批量插入(没有即插入,有的话不做处理)
     * @param list
     * @return
     */
    int batchInsertIgnore(List<E> list);
    /**
     * 批量删除
     * @param list
     * @return
     */
    int batchDelete(List<E> list);
    /**
     * 根据条件删除
     * @param form
     * @return
     */
    int deleteByForm(F form);
    /**
     * 查询列表
     * @param form
     * @return
     */
    List<E> queryList(F form);
}

复制代码

BaseServiceImpl

Service实现类的抽象封装,相应Service实现类需要继承该接口。

package org.suze.framework.base.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.suze.framework.base.BaseMapper;
import org.suze.framework.base.BaseService;
import org.suze.framework.base.page.PageForm;

import java.io.Serializable;
import java.util.List;

/**
 * @description: BaseService
 * @Date : 2019/5/31 下午2:25
 * @Author : 石冬冬-Seig Heil
 * @param <E> 数据库实体对象
 * @param <P> 查询主键
 * @param <F> 查询条件Form对象
 */
public abstract class BaseServiceImpl<E extends Serializable,P,F extends Serializable> implements BaseService<E,P,F> {
    /**
     * mapper
     */
    protected BaseMapper<E,P,F> mapper;

    @Autowired
    public void setBaseMapper(BaseMapper<E, P, F> baseMapper) {
        this.mapper = baseMapper;
    }

    @Override
    public List<E> queryByPage(PageForm<F> form) {
        return mapper.queryByPage(form);
    }

    @Override
    public int queryCount(PageForm<F> form) {
        return mapper.queryCount(form);
    }

    @Override
    public E queryRecord(P primaryKey) {
        return mapper.selectByPrimaryKey(primaryKey);
    }

    @Override
    public int insertRecord(E record) {
        return mapper.insert(record);
    }

    @Override
    public int updateRecord(E record) {
        return mapper.updateByPrimaryKey(record);
    }

    @Override
    public int updateByPrimaryKeySelective(E record) {
        return mapper.updateByPrimaryKeySelective(record);
    }

    @Override
    public int deleteRecord(P primaryKey) {
        return mapper.deleteByPrimaryKey(primaryKey);
    }

    @Override
    public int batchInsert(List<E> list) {
        return mapper.batchInsert(list);
    }

    @Override
    public int batchInsertIgnore(List<E> list) {
        return mapper.batchInsertIgnore(list);
    }

    @Override
    public int batchDelete(List<E> list) {
        return mapper.batchDelete(list);
    }

    @Override
    public int deleteByForm(F form) {
        return mapper.deleteByForm(form);
    }

    @Override
    public List<E> queryList(F form) {
        return mapper.queryList(form);
    }
}

复制代码

BaseFacade

Facade层的抽象封装,相应Facade需要继承该接口。

package org.suze.framework.base;

import org.suze.framework.base.page.PageForm;
import org.suze.framework.base.page.PageVO;

import java.io.Serializable;

/**
 * @description: Facade 接口基类
 * @Date : 2019/5/31 下午2:45
 * @Author : 石冬冬-Seig Heil
 * @param <V> 页面显示VO对象
 * @param <E> 数据库实体对象
 * @param <P> 查询主键
 * @param <F> 查询条件Form对象
 */
public interface BaseFacade<V extends Serializable,E extends Serializable,P,F extends Serializable> {
    /**
     * 分页加载数据
     * @param form
     * @return
     */
    Result<PageVO<V>> loadRecords(PageForm<F> form);
    /**
     * 查询实体对象
     * @param primaryKey
     * @return
     */
    Result<V> queryRecord(P primaryKey);
    /**
     * 保存实体对象
     * @param record
     * @return
     */
    Result<String> saveRecord(E record);
    /**
     * 删除对象
     * @param primaryKey
     * @return
     */
    Result<String> deleteRecord(P primaryKey);

    /**
     * 更新状态
     * @param record
     * @return
     */
    Result<String> modifyStatus(E record);
}

复制代码

BaseFacadeImpl

Facade实现类的抽象封装,相应Facade实现类需要继承该接口。

package org.suze.framework.base.impl;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.suze.framework.base.BaseConverter;
import org.suze.framework.base.BaseFacade;
import org.suze.framework.base.BaseService;
import org.suze.framework.base.Result;
import org.suze.framework.base.enums.RemoteEnum;
import org.suze.framework.base.page.PageForm;
import org.suze.framework.base.page.PageVO;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;

/**
 * @description: BaseFacadeImpl,相关功能模块应该继承此类
 * @Date : 2019/5/31 下午10:04
 * @Author : 石冬冬-Seig Heil
 * @param <V> 页面显示VO对象
 * @param <E> 数据库实体对象
 * @param <P> 查询主键
 * @param <F> 查询条件Form对象
 */
@Slf4j
public abstract class BaseFacadeImpl<V extends Serializable,E extends Serializable,P extends Serializable,F extends Serializable> implements BaseFacade<V,E,P,F> {
    /**
     * service
     */
    protected BaseService<E,P,F> service;

    /**
     * 是否修改操作
     * @param record
     * @return
     */
    protected abstract boolean isModify(E record);

    /**
     * VO转换器
     * @return
     */
    protected abstract BaseConverter<V,E> converter();

    @Autowired
    public void setService(BaseService<E, P, F> service) {
        this.service = service;
    }

    @Override
    public Result<PageVO<V>> loadRecords(PageForm<F> form) {
        List<V> voList = Collections.emptyList();
        int count = 0;
        try {
            List<E> queryList = service.queryByPage(form);
            count = service.queryCount(form);
            voList = converter().convertList(queryList);
        } catch (Exception e) {
            log.error("loadRecords exception,form={}", JSON.toJSON(form),e);
            return Result.failInServer(PageVO.newInstance(count,voList));
        }
        return Result.suc(PageVO.newInstance(count,voList));
    }

    @Override
    public Result queryRecord(P primaryKey) {
        E entity = service.queryRecord(primaryKey);
        if(null == entity){
            return Result.fail(RemoteEnum.WARN_EMPTY_RECORD);
        }
        return Result.suc(converter().convert(entity));
    }

    @Override
    public Result<String> saveRecord(E record) {
        if(isModify(record)){
            service.updateRecord(record);
        }else{
            service.insertRecord(record);
        }
        return Result.suc();
    }

    @Override
    public Result<String> deleteRecord(P primaryKey) {
        service.deleteRecord(primaryKey);
        return Result.suc();
    }

    @Override
    public Result<String> modifyStatus(E record) {
        service.updateByPrimaryKeySelective(record);
        return Result.suc();
    }
}

复制代码

这里要特别说明一下,该Facade层的,我们可以看到saveRecord把新增、编辑通过定义一个抽象isModify方法,以区分该共操作是新增还是编辑。

BaseConverter

提供Entity对VO的转换,自己需要实现VO转换器并继承该抽象类。

package org.suze.framework.base;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 值对像转化抽像类,目的将源对像转化为值对像。
 * @Date : 2019/6/1 上午8:27
 * @Author : 石冬冬-Seig Heil
 * @param <R> 目标值对像
 * @param <O> 源对像
 */
public abstract class BaseConverter<R extends Serializable, O extends Serializable> {
	/**
	 * 勾子方法,由了子类实现具体的转换规则。
	 * @param o 源对像
	 * @return <R> 目标值对像
	 */
	public abstract R convert(O o);
	/**
	 * List转化方法
	 * @param es 源对像列表
	 * @return List<V>目标值对像列表
	 */
	public List<R> convertList(List<O> es) {
		if(null == es || es.isEmpty()){
			return Collections.emptyList();
		}
		List<R> voList = new ArrayList<>(es.size());
		for(O origin : es) {
			R vo = convert(origin);
			voList.add(vo);
		}
		return voList;
	}
}

复制代码

PageForm

对分页查询功能的页面查询参数封装。

package org.suze.framework.base.page;

import java.io.Serializable;

/**
 * Description: 分页Form类<br/>
 * @author 石冬冬
 * @version V1.0  by 石冬冬-Heil Hitler on  2020/3/27 12:48
 */
public class PageForm<T> implements Serializable {
    private static final long serialVersionUID = 1315360688901318671L;
    /**
     * 分页起止
     */
    private int start = 0;
    /**
     * 分页大小(每页多数条)
     */
    private int limit = 20;
    /**
     * 是否分页
     */
    private boolean paging = true;
    /**
     * 分页包装的Form条件
     */
    private T form;

    public PageForm() {
    }

    public PageForm(T form) {
        this.form = form;
    }

    public PageForm(int start, int limit, T form) {
        this.start = start;
        this.limit = limit;
        this.form = form;
    }

    public PageForm(boolean paging) {
        this.paging = paging;
    }

    public PageForm(boolean paging, T form) {
        this.paging = paging;
        this.form = form;
    }

    /**
     * 静态实例方法,外部调用
     * @param start 分页起止
     * @param limit 分页大小(每页多数条)
     * @param form 分页包装的Form条件
     * @param <T>
     * @return
     */
    public static <T> PageForm newInstance(int start, int limit, T form){
        return new PageForm(start,limit,form);
    }

    /**
     * 静态实例方法,外部调用
     * @param paging 是否分页
     * @param form 分页包装的Form条件
     * @param <T>
     * @return
     */
    public static <T> PageForm newInstance(boolean paging,T form){
        return new PageForm(paging,form);
    }

    public int getStart() {
        return start;
    }

    public void setStart(int start) {
        this.start = start;
    }

    public int getLimit() {
        return limit;
    }

    public void setLimit(int limit) {
        this.limit = limit;
    }

    public T getForm() {
        return form;
    }

    public void setForm(T form) {
        this.form = form;
    }

    public boolean isPaging() {
        return paging;
    }

    public void setPaging(boolean paging) {
        this.paging = paging;
    }
}

复制代码

PageVO

对分页查询功能的数据结果集封装。

package org.suze.framework.base.page;

import java.io.Serializable;
import java.util.List;

/**
 * Description:分页查询VO类 <br/>
 * @author 石冬冬
 * @version V1.0  by 石冬冬-Heil Hitler on  2020/3/27 12:57
 */
public class PageVO<T> implements Serializable {
    private static final long serialVersionUID = 1724063683524348852L;
    /**
     * 总条数
     */
    private int recordsTotal;
    /**
     * 结果集
     */
    private List<T> data;

    public PageVO() {
    }

    public PageVO(int total, List<T> data){
        this.data = data;
        this.recordsTotal = total;
    }
    /**
     * 静态方法,返回实例对象
     * @param total 总条数
     * @param data 数据记录集合
     * @param <T>
     * @return
     */
    public static <T> PageVO newInstance(int total, List<T> data){
        return new PageVO(total,data);
    }

    public PageVO(List<T> data){
        this.data = data;
    }

    public int getRecordsTotal() {
        return recordsTotal;
    }

    public void setRecordsTotal(int recordsTotal) {
        this.recordsTotal = recordsTotal;
    }

    public List<T> getData() {
        return data;
    }

    public void setData(List<T> data) {
        this.data = data;
    }
}

复制代码

Result

对Restful API接口的响应封装。

package org.suze.framework.base;

import lombok.Data;
import org.suze.framework.base.enums.RemoteEnum;

import java.io.Serializable;

/**
 * @description: 用于DTO返回泛型使用,提供Dobbo服务一种数据协议
 * @Date : 上午11:49 2020/3/29
 * @Author : 石冬冬-Heil Hitler
 */
@Data
public class Result<E> implements Serializable {
    private final static int SUCCESS_CODE = 0;
    private final static int FAILURE_CODE = 1;
    private final static String SUCCESS_MSG = "操作成功";
    private final static String FAILURE_MSG = "操作失败";
    private static final long serialVersionUID = -6237151417035547947L;
    /**
     * 是否执行成功
     */
    private boolean success;
    /**
     * 包体
     */
    private E data;
    /**
     * 执行操作code
     */
    private int code;
    /**
     * 业务消息
     */
    private String msg;

    public Result() {
    }

    /**
     * 构造函数
     * @param success
     * @param data
     * @param code
     * @param msg
     */
    public Result(boolean success, E data, int code, String msg) {
        this.success = success;
        this.data = data;
        this.code = code;
        this.msg = msg;
    }

    /**
     * 静态方法,返回执行成功
     * @return
     */
    public static Result suc(){
        return suc(SUCCESS_MSG,SUCCESS_CODE,SUCCESS_MSG);
    }

    /**
     * 静态方法,返回执行成功
     * @param t
     * @param <E>
     * @return
     */
    public static <E> Result<E> suc(E t){
        return suc(t,SUCCESS_CODE,SUCCESS_MSG);
    }

    /**
     * 静态方法,返回执行成功
     * @param t
     * @param code
     * @param msg
     * @param <E>
     * @return
     */
    public static <E> Result<E> suc(E t,int code,String msg){
        return new Result<>(Boolean.TRUE,t,code,msg);
    }

    /**
     * 静态方法,返回执行成功
     * @param t 返回具体的包装对象
     * @param remoteEnum 业务枚举对象
     * @param msg 业务信息
     * @param <E>
     * @return
     */
    public static <E> Result<E> suc(E t, RemoteEnum remoteEnum, String msg){
        return new Result<>(Boolean.TRUE,t,remoteEnum.getIndex(),msg);
    }

    /**
     * 静态方法,返回执行失败
     * @return
     */
    public static Result<Object> fail(){
        return fail(FAILURE_MSG,FAILURE_CODE,FAILURE_MSG);
    }

    /**
     * 静态方法,返回执行失败
     * @param t
     * @param <E>
     * @return
     */
    @Deprecated
    public static <E> Result<E> fail(E t){
        return fail(t,FAILURE_CODE,FAILURE_MSG);
    }

    @Deprecated
    public static Result fail(String msg){
        return fail(null, -1, msg);
    }

    /**
     * 静态方法,返回执行失败
     * @param code 业务代码
     * @param msg 业务信息
     * @param <E>
     * @return
     */
    public static <E> Result<E> fail(int code,String msg){
        return fail(null, code, msg);
    }

    /**
     * 静态方法,返回执行失败
     * @param remoteEnum 业务枚举
     * @param msg 业务信息
     * @param <E>
     * @return
     */
    public static <E> Result<E> fail(RemoteEnum remoteEnum,String msg){
        return fail(null, remoteEnum.getIndex(), msg);
    }

    /**
     * 静态方法,返回执行失败
     * @param remoteEnum
     * @return
     */
    public static <E> Result<E> fail(RemoteEnum remoteEnum){
        return new Result<>(Boolean.FALSE,null,remoteEnum.getIndex(),remoteEnum.getName());
    }
    /**
     * 静态方法,返回执行失败
     * @param t
     * @param code
     * @param msg
     * @param <E>
     * @return
     */
    public static <E> Result<E> fail(E t,int code,String msg){
        return new Result<>(Boolean.FALSE,t,code,msg);
    }

    /**
     * 静态方法,返回执行成功
     * @param t 返回具体的包装对象
     * @param remoteEnum 业务枚举对象
     * @param msg 业务信息
     * @param <E>
     * @return
     */
    public static <E> Result<E> fail(E t, RemoteEnum remoteEnum, String msg){
        return fail(t,remoteEnum.getIndex(),msg);
    }

    /**
     * 静态方法,返回执行失败:参数为空
     * @param t
     * @param <E>
     * @return
     */
    public static <E> Result<E> failWithEmptyParam(E t){
        return fail(t, RemoteEnum.ERROR_WITH_EMPTY_PARAM.getIndex(),RemoteEnum.ERROR_WITH_EMPTY_PARAM.getName());
    }
    /**
     * 静态方法,返回执行失败:服务端内部错误
     * @param t
     * @param <E>
     * @return
     */
    public static <E> Result<E> failInServer(E t){
        return fail(t, RemoteEnum.ERROR_IN_SERVER.getIndex(),RemoteEnum.ERROR_IN_SERVER.getName());
    }
    /**
     * 静态方法,返回执行失败:运行时错误
     * @param t
     * @param <E>
     * @return
     */
    public static <E> Result<E> failInRuntime(E t){
        return fail(t, RemoteEnum.ERROR_IN_RUNTIME.getIndex(),RemoteEnum.ERROR_IN_RUNTIME.getName());
    }
}

复制代码

5.总结

上述三层MapperServiceFacade,即我们Abstract ModuleFacade的意义使得我们日常的业务处理通过它来负责,无需侵入到Service层。

源码:gitee.com/suze/base-m…

下一篇:《代码写了那么多,CURD也可以如此简单(下)?》

下面的是我的公众号二维码图片,欢迎关注。

公众号

原文:blog.csdn.net/shichen2010…

猜你喜欢

转载自juejin.im/post/5e8093e56fb9a03c4a495dda