Spring Boot 自定义应用开发框架九——基本增删改查“低代码”框架初设计1

本文介绍了作者在构建后端框架时采用的低代码设计,将后端分为前台和中台两个服务,以提高业务纯粹性和后期维护性。框架包括core、framework和common三个模块,其中common包含controller和服务层基类,用于简化增删改查等常见操作。通过实体类和基类的抽象,减少了开发人员的重复工作,提高了开发效率。
摘要由CSDN通过智能技术生成

概述

因为最近一直在为公司搭建底层框架, 好久没有更新博客了,本次搭建的框架结构基本沿用的就是前面几篇博客所写的结构,最大的不同点是,为了让底层数据更加的纯粹,将后端开发拆成了2个服务,一个是接收前端的提交数据,进行业务上的处理,然后将处理后的数据再提交给另一个后端,姑且我们分别将这2个服务叫做“前台”与“中台”,也即一个完整的功能模块有3个服务,分别是前端、前台、中台。
这样将后端拆分成前台,中台两个服务优劣势都比较明显。
先说劣势:前后端沟通成本上升;简单模块的代码会有冗余(但随着需求的迭代,这点可能会变成优点)。
优点:业务比较纯粹,前台只负责前端提交的数据,业务的处理,权限的处理;后台只负责数据的操作;后期容易迭代,维护,升级,数据库方面也会更好的优化与隔离;安全性会更高,如中台只能被局域网访问。
正因为后端被分成了前台、中台两个部分,框架的结构与前面讲的稍微有点不同,主要分为3个模块:底层的工具类(core)、框架(framework)、元件库(common)
core : 主要就是各种工具类
framework:包含但不限于 日志处理、异常处理、权限处理、前台的RestTemplate 等
common:controller、service 基类封装,分页控件、事务处理、持久层封装等
通过上面的描述能明确的知道common就是中台需要的基础框架,主要就是对持久层的处理。
以上就是我搭建的开发框架的一个基本架构,本篇文章主要是讲一种基本增删改查低代码设计,框架的具体信息就不过多介绍了。

“低代码”初设想

现在的低代码一般指的是让非专业编程人员通过“拖拉拽”开发组件,就能完成应用搭建,例如阿里钉钉的易搭等。但我这里并不是指这类通过“表单或数据”来驱动的低代码应用,而是指开发人员以基础增删改查功能上的低代码设计。

增删改查

企业内部的管理类软件,绝大多数功能都是基本的“增删改查”,可以说这类操作基本占了70%,甚至有些管理类软件能到90%,剩下的就是数据分析、图表展示功能。
而"增删改查"这四类功能中,绝大多数的模块中"增删改"这3类都是单表操作,只有查询功能需要多表操作,对于大多数初中级开发人员来说,主要的开发工作都是这几类功能,而我们需要做的就是减少这种固定化的开发流程。

框架结构

后台的框架结构遵循的还是三层结构,即controller层、service层、dao层。dao层现在网上很多,例如比较流行的mybatis-plus,如果觉得太重,也可以自己写一个,我这里就不多说了,这里主要讲的是controller层和service层。

功能列表

一般模块的基础功能都是差不多的,主要有以下几类:
1.新增 save
2.编辑 update
3.删除 deleted
4.通过ID查询 queryById
5.查询列表 queryList
6.分页 queryPage
以上6种,除了分页之外,其余都是可以固定化到框架中,这样开发人员基本就可以不用编写相应代码,只要创建controller类即可。

实体类、 controller层、service层基类

后端的程序归根结底就是对数据的处理,而数据一般都是存在数据库的数据表中,我们在实际开发过程中,一般每套controller\service\dao 就对应一张表的处理,而每张表一般都会有相同的基础字段,例如id,删除状态、创建人、创建时间等,因此可以定义一个实体的基类:
BaseEntity.java

@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("实体公共属性")
public class BaseEntity implements Serializable {
	private static final long serialVersionUID = 1L;	
	@ApiModelProperty("ID")
	@TableId(type = IdType.AUTO)
	@NsGridField(title = "ID", isEnable = false, isVisible = false)
	private Long id;	
	@ApiModelProperty("删除标记")
	@TableField
	@NsGridField(type = "boolean", title = "删除标记", isEnable = false, isVisible = false)
	private Boolean deleted;
	@ApiModelProperty("创建人ID")
	@TableField(fill = FieldFill.INSERT)
	@NsGridField(title = "创建人ID")
	private Long createUserId;	
	@ApiModelProperty("创建时间")
	@TableField(fill = FieldFill.INSERT)
	@NsGridField(title = "创建时间")
	@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
	private LocalDateTime createDateTime;
}

service层基类

现在一般使用的比较多的是mybatis-plus,本身就封装了很多service层的方法,但在实际开发中可能会根据需要封装符合本公司业务的方法,例如开启状态删除后,mybatis-plus所有的删除只能状态删除,而一些中间表的数据有时候还是希望被物理删除的,再如通过对象直接进行删除、编辑等功能。
INsService.java

public interface INsService<T> extends IService<T> {
	    boolean deleteById(Serializable id) throws Exception;
		//根据实体的主键ID修改对象
	    boolean update(T entity) throws Exception;    
 }   

service实现基类

AbstractNsService.java

public abstract class AbstractNsService<M extends BaseMapper<T>, T extends BaseEntity> extends ServiceImpl<M, T> implements INsService<T> {
	@Override
	public boolean deleteById(final Serializable id) {		
		final String sql = NsStringUtils.formatString("delete from {0} where id = {1}",
				tableName, id.toString());
		return SqlRunner.db().delete(sql);
	}
	@Override
	public boolean update(final T entity) throws Exception {
		final Object id = NsBeanUtils.getFieldValue(entity, "id");
		if (id == null) {
			throw new NsRuntimeException("nssql40008", "主键ID必须有值");
		}
		final String setSql = SQLGenerator.getUpdateSetSql(entity);
		final String sql = NsStringUtils.formatString("update {0} set {1} where id = {2}",
				tableName, setSql, id.toString());
		return SqlRunner.db().update(sql);
	}
}

以上只是service层的案例,具体的代码就不写了,这里还可以添加其他共用的方法,例如通过对象的属性获取信息集合、个数等,甚至可以封装符合本公司下常用的数据处理,例如我还封装了树状数据结构的处理,免得开发人员写递归函数了。

Controller层基类

public abstract class AbstractNsController<S extends INsService<T>, T extends BaseEntity> {

	@Autowired
	protected S service;

	@ApiOperation(value = "新增")
	@PostMapping("save")
	public NsResponse<T> save(@RequestBody final T entity) {
		try {
			this.service.save(entity);
		} catch (final Exception e) {
			e.printStackTrace();
			throw new NsRuntimeException("新增异常", e);
		}
		return NsResponse.ok(entity);
	}

	@ApiOperation(value = "编辑")
	@PostMapping("update")
	public NsResponse<T> update(@RequestBody final T entity) {
		try {
			this.service.update(entity);
		} catch (final Exception e) {
			e.printStackTrace();
			throw new NsRuntimeException("编辑异常", e);
		}
		return NsResponse.ok(entity);
	}

	@ApiOperation(value = "物理删除")
	@PostMapping(value = "delete")
	public NsResponse<T> delete(@RequestBody final T t) {
		try {
			final String id = NsBeanUtils.getFieldValue(t, "id");
			this.service.deleteByIds(id);
		} catch (final Exception e) {
			e.printStackTrace();
			throw new NsRuntimeException("物理删除异常", e);
		}
		return NsResponse.ok(t);
	}

	@ApiOperation(value = "逻辑删除")
	@PostMapping(value = "remove")
	public NsResponse<T> remove(@RequestBody final T t) {
		try {
			final String id =  NsBeanUtils.getFieldValue(t, "id");
			this.service.removeByIds(id);
		} catch (final Exception e) {
			e.printStackTrace();
			throw new NsRuntimeException("逻辑删除异常", e);
		}
		return NsResponse.ok(t);
	}

	@ApiOperation(value = "通过ID查询数据")
	@PostMapping(value = "queryById")
	public NsResponse<T> queryById(@RequestBody final T t) {
		T entity = null;
		try {
			entity = this.service.getById((Long) NsBeanUtils.getFieldValue(t, "id"));
		} catch (final Exception e) {
			e.printStackTrace();
			throw new NsRuntimeException("通过ID查询数据异常", e);
		}
		return NsResponse.ok(entity);
	}

	@ApiOperation(value = "查询列表")
	@PostMapping(value = "queryList")
	public NsResponse<List<T>> queryList(@RequestBody final T entity) {
		List<T> list = new ArrayList<>();
		try {
			list = this.service.list(entity);
		} catch (final Exception e) {
			e.printStackTrace();
			throw new NsRuntimeException("查询列表异常", e);
		}
		return NsResponse.ok(list);
	}	
}

以上就是controller基类的常用方法的封装,这篇文章主要是将框架结构性,具体的代码就不展开说了,按照上面的结构做好基础框架后,后端开发人员创建的controller类只要继承AbstractNsController,那么基本常规操作就不用写代码了,controller类中只要写一个分页即可,其实分页查询也是可以放到基类中,但考虑到很多情况下分页可能需要做额外的处理就没有放进去。
下面以用户模块做例子,对应的controller类如下
UserController.java

@Api(tags = "用户表")
@RestController
@RequestMapping("user")
public class UserController extends AbstractNsController<IDemoUserService, DemoUser> {

	@ApiOperation(value = "查询分页列表")
	@PostMapping(value = "/queryPage")
	public NsResponse<Page> queryPage(@RequestBody final Page<User> page) {
		try {
			this.service.queryPage(page);
		} catch (final Exception e) {
			e.printStackTrace();
			throw new NsRuntimeException(e);
		}
		return NsResponse.ok(page);
	}
}	

此Controller类中只有一个分页的方法,其他的方法都没有,但是我们可以直接通过基类接口直接获取、新增数据,例如以获取用户集合为例。
数据表中的内容:在这里插入图片描述
接口: http://127.0.0.1:8091/demo/user/queryList
假设我们要搜性别为0的所有人员,只要如下图所示前台将条件传入即可。
在这里插入图片描述
通过这个案例可以发现,只要将按照这种结构封装,那么后端人员可以大大减少基础模块的开发量,而且也能与前端人员统一基础模块的接口命名,以避免同类型操作由于不同开发人员的习惯随意取名字。

以上代码的简化还只是第一步,最关键的是分页列表、查询、新增、编辑等模块在前后端的简化,例如通过封装分页,让所有的查询只需要通过配置就可以实现,而不需要开发人员具体的去写代码;新增、编辑功能也是通过配置就可以实现前端页面的展示,分页的列展示以及排序都可以通过配置来实现,而不需要前后端开发人员每做一个模块都去复制一份代码,然后根据不同功能需求修修补补。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值