在开发过程中,我们常常会想,也许每天都是重复的CRUD类的工作,而基于面向对象又有很丰富的特性,例如:封装,继承,多态等特性。那么完全可以将通用的部分的代码抽离出来。极大的简化web开发。
什么样的功能可以被抽离?其实对于大家平常开发都会用的功能都可以抽离出来,一张表的增删改查。分页查询,批量操作等。对于缓存的操作,也可以抽离出来。尽可能的解除开发的耦合性,
对于通用功能的实现,也许对于不同的公司,不同的业务都有不同的实现,那么就简单的介绍一下我的实现吧。
项目环境:springboot2.14
tk.mybatis 通用mapper
lombok
如何去配置通用的mapper 以及部署环境就不在这里过多的阐述了
下面来看一下具体的实现。
通用的 api- controller 接口的实现 。(在这个层级里主要是对crud操作的实现)
/**
* 通用的rest接口 用于拓展 api controller 在继承层级中 第二层 业务解耦合性 使简单业务逻辑开发更容易
* 懒人必备 ....!!!!
*
* @author 徐塬峰
* @email [email protected]
* @date 2019-06-30
* @description
**/
@RestController
public abstract class BaseApiController, ID extends Serializable> extends SimpleController {
/**
* 注入service 层 实现通用的crud方法
*/
@Autowired
protected S service;
/**
* 插入
* entity
* @return
*/
@RequestMapping("/save")
@ApiOperation(value = "通用的api插入接口", notes = "插入一条数据")
public JsonResult save(T entity) {
service.save(entity);
return JsonResult.ok();
}
/**
* 更新 不为null的字段
*
* @param entity
* @return
*/
@RequestMapping("/update")
@ApiOperation(value = "通用的更新接口", notes = "更新一条数据")
public JsonResult update(T entity) {
service.updateByEntity(entity);
return JsonResult.ok();
}
/**
* 根据id删除一条数据
*
* @param id
* @return
*/
@RequestMapping("/deleteById/{id}")
@ApiOperation(value = "通用的删除接口", notes = "根据id来删除一条数据")
public JsonResult deleteById(@PathVariable("id") ID id) {
service.deleteById(id);
return JsonResult.ok();
}
/**
* 删除一条数据
* @param entity
* @return
*/
@RequestMapping("/deleteEntity")
public JsonResult deleteEntity(T entity) {
return JsonResult.ok(service.deleteByEntity(entity));
}
/**
* 查询出所有的数据
*
* @return
*/
@RequestMapping("/selectAll")
@ApiOperation(value = "通用的查询接口", notes = "查询出所有的表数据")
public JsonResult selectAll() {
return JsonResult.ok(service.selectAll());
}
/**
* 根据id查询出一条数据
*
* @return
*/
@RequestMapping("/selectOne/{id}")
@ApiOperation(value = "通用的查询接口", notes = "查询出所有的表数据")
public JsonResult selectOne(@PathVariable("id") ID id) {
return JsonResult.ok(service.selectById(id));
}
/**
* 分页 查询出所有数据 与 bootstrap-table整合 !!!
*/
@ResponseBody
@RequestMapping("/pageList")
public JsonResult queryAll(Integer page,Integer pageSize)
{
return JsonResult.ok(service.queryAll(page,pageSize));
}
}
通用的controller接口的实现(在这一层级里主要是对要跳转的页面进行封装)
/**
* @author 徐塬峰
* @email [email protected]
* @date 2019-06-30
* @description 用于拓展controller 第二层
* 懒人必备 ....!!!!
**/
@Controller
@Api(hidden = true)
public abstract class BaseController> extends SimpleController {
/**
* 基础路径
*/
protected String basePathPrefix;
/**
* 添加
*/
protected String PAGE_ADD = "/add";
/**
* 列表
*/
protected String PAGE_LIST = "/list";
/**
* 显示详情
*/
protected String PAGE_DETAILS = "/details";
/**
* 编辑
*/
protected String PAGE_EDIT = "/edit";
/**
* 错误页面
*/
protected String PAGE_ERROR = "error/500";
/**
* 跳转到添加页面
*
* @return
*/
@SysLog
@RequestMapping("/add")
public String add() {
return basePathPrefix + PAGE_ADD;
}
/**
* 跳转到编辑页面
*
* @return
*/
@SysLog
@RequestMapping("/edit")
public String edit() {
return basePathPrefix + PAGE_EDIT;
}
/**
* 跳转到列表
*
* @return
*/
@SysLog
@RequestMapping("/list")
public String list() {
return basePathPrefix + PAGE_LIST;
}
/**
* 跳转到列表
*
* @return
*/
@SysLog
@RequestMapping("/details")
public String details() {
return basePathPrefix + PAGE_DETAILS;
}
// protected SpringDataWebProperties.Pageable getPageRequest() {
// String offsetStr = request.getParameter("offset");
// String limitStr = request.getParameter("limit");
// if (StringKit.isBlank(offsetStr) && StringKit.isBlank(limitStr)) {
// // 表示不分页
// return null;
// } else {
// int offset = 0;
// int limit = 5;// 相当于pageSize
// int pageNumber = 0;// 如果是pageRequest对象,页码起始值为0
// if (StringKit.isNotBlank(limitStr)) {
// limit = Integer.valueOf(limitStr);
// }
// if (StringKit.isNotBlank(offsetStr)) {
// // 转为页码
//// offset = Integer.valueOf(offsetStr);
//// pageNumber = offset / limit;
// offset = Integer.valueOf(offsetStr);
// pageNumber = offset - 1;
// }
// return PageRequest.of(pageNumber, limit, getSort());
// }
// }
}
继承层级最高的SimpleController (对项目中controller通用的资源进行注入,例如request session 日志等都可以放在这一层级里)
/**
* @author 徐塬峰
* @email [email protected]
* @date 2019-07-07
* @description 用于拓展 controller 第一层
**/
public class SimpleController {
/**
* 统一日期转换处理
*
* @param binder
*/
@InitBinder
public void initBinder(WebDataBinder binder) {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat,
true));// CustomDateEditor为自定义日期编辑器
}
/**
* 日志对象
*
* @since 1.0.0
*/
protected static Logger log = LoggerFactory.getLogger(SimpleController.class);
@Autowired
protected HttpServletRequest request;
@Autowired
protected HttpSession session;
/**
* HttpServletResponse
*
* @since 1.0.0
*/
@Autowired
protected HttpServletResponse response;
/**
* Spring applicationContext
*
* @since 1.0.0
*/
protected ApplicationContext applicationContext;
/**
* 获取webBasePath【如 http://127.0.0.1:8080/projectname】
*
* @return webBasePath
* @since 1.0.2
*/
protected String getWebBasePath() {
return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
}
/**
* 获取来访的ip地址
*
* @param request
* @return ip地址
* @since 1.0.6
*/
protected String getRemortIP(HttpServletRequest request) {
if (request.getHeader("x-forwarded-for") == null) {
return request.getRemoteAddr();
}
return request.getHeader("x-forwarded-for");
}
/**
* 获取经过nginx后的ip地址
*
* @param request
* @return ip地址
* @since 4.0.1
*/
protected String getNginxForwardIp(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
if (!CommonUtils.isEmpty(ip)) {
ip = ip.split(",")[0];
}
return ip;
}
}
通用的Service层 crudService 主要是定义通用的接口
/**
* @author 徐塬峰
* @email [email protected]
* @date 2019-07-24
* @description
**/
public interface CrudService {
T selectOne(T entity);
void deleteById(ID id);
int deleteByEntity(T entity);
int updateByEntity(T entity);
void deleteBatch(ID[] ids);
void save(T Entity);
List selectAll();
T selectById(ID id);
PageResult queryAll(Integer page, Integer pageSize);
}
通用的BaseService
/**
* @author 徐塬峰
* @email [email protected]
* @date 2019-06-30
* @description
**/
public interface BaseService {
}
对于通用的service的具体实现 serviceImpl ,同样在这一层级里注入的通用的mapper,crud的操作也封装在这里
/**
* @author 徐塬峰
* @email [email protected]
* @date 2019-07-23
* @description 通用的crud service 用于拓展 service 第一层 实现简单的crud方法 需要继承 MyMapper
* 注意 有些service 可能用不到 crud 那么请选择 BaseServiceImpl 通用的业务抽离 解耦合
**/
@Slf4j
public abstract class CrudServiceImpl, ID extends Serializable> implements CrudService {
@Autowired
protected S mapper;
protected Class tClass;
/**
* 查询出一条数据
*
* @param entity
* @return
*/
@Override
public T selectOne(T entity) {
return mapper.selectOne(entity);
}
/**
* 删除一条数据 根据id
*
* @param id
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteById(ID id) {
try {
val entity = tClass.newInstance();
BeanUtils.setProperty(entity, "id", id);
mapper.deleteByPrimaryKey(entity);
} catch (InstantiationException e) {
throw new RuntimeException("删除一条数据 创建对象" + tClass + "发生了错误");
} catch (IllegalAccessException e) {
throw new RuntimeException("删除一条数据 创建对象" + tClass + "发生了错误");
} catch (InvocationTargetException e) {
throw new RuntimeException("BeanUtils设置对象的id值发生了错误");
}
}
/**
* 删除一条数据
*
* @param entity
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteByEntity(T entity) {
return mapper.deleteByPrimaryKey(entity);
}
/**
* 更新不为null的字段
*
* @param entity
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int updateByEntity(T entity) {
return mapper.updateByPrimaryKeySelective(entity);
}
/**
* 批量删除
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteBatch(ID[] ids) {
if (ArrayUtils.isEmpty(ids)) {
return;
}
try {
for (ID id : ids) {
val entity = tClass.newInstance();
//再通过BeanUtils来给对象设置id值
BeanUtils.setProperty(entity, "id", id);
mapper.deleteByPrimaryKey(entity);
}
} catch (InstantiationException e) {
throw new RuntimeException("批量删除 创建对象" + tClass + "发生了错误");
} catch (IllegalAccessException e) {
throw new RuntimeException("批量删除 创建对象" + tClass + "发生了错误");
} catch (InvocationTargetException e) {
throw new RuntimeException("BeanUtils设置对象的id值发生了错误");
}
}
/**
* 插入一条数据到数据库
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void save(T entity) {
mapper.insertSelective(entity);
}
/**
* 查询出所有的数据
*
* @return
*/
@Override
public List selectAll() {
return mapper.selectAll();
}
/***
* 根据id查询出一条数据
* @param id
* @return
*/
@Override
public T selectById(ID id) {
try {
val entity = tClass.newInstance();
BeanUtils.setProperty(entity, "id", id);
return mapper.selectOne(entity);
} catch (InstantiationException e) {
throw new RuntimeException(" 创建对象" + tClass + "发生了错误");
} catch (IllegalAccessException e) {
throw new RuntimeException(" 创建对象" + tClass + "发生了错误");
} catch (InvocationTargetException e) {
throw new RuntimeException("BeanUtils设置对象的id值发生了错误");
}
}
/**
* 分页查询所有数据
* @param page
* @param pageSize
*/
@Override
public PageResult queryAll(Integer page, Integer pageSize) {
PageHelper.startPage(page,pageSize);
List list= mapper.selectAll();
PageInfo pageList = new PageInfo(list);
PageResult pageResult = new PageResult();
pageResult.setPage(page);
pageResult.setRecords(pageList.getPages());
pageResult.setRows(list);
pageResult.setRecords(pageList.getTotal());
return pageResult;
}
}
通用的Mapper,可以在此基础上进行你的拓展
/**
* 通用的mapper工具类 用于拓展mapper 第一层
* @param
*/
public interface MyMapper extends Mapper, MySqlMapper {
}
在完成上述的封装框架之后,web开发就变得更加简单了。
Controller层只需要继承BaseController,并重写basePathfix 路径地址 即可 获得crud template 前端页面地址。
@Controller
@RequestMapping("/drawCrash")
@Api(hidden = true)
public class DrawCrashController extends BaseController {
public DrawCrashController()
{
basePathPrefix="xuanwang/wallet/drawCrash";
}
}
Controller-api层则需要传入service ,model 类,主键类型
@RestController
@RequestMapping("/user")
@Api(value = "用户接口", tags = {"用户接口"})
public class UserApiController extends BaseApiController {
/**
* 封号
*/
@RequestMapping("/ban/{userId}")
public JsonResult banUser(@PathVariable("userId") Long userId) {
return JsonResult.ok(service.banUser(userId));
}
}
简单的介绍就到这里吧,如需要更多的案列欢迎 项目代码: https://github.com/RAOE/next1-boot
群号:673926093,看到这里你是不是迫不及待的想要去封装自己的springboot拉。如果有更好的框架实现思路也欢迎交流沟通哦