1. General Service extraction
Through the idea of general Mapper, that is to extract some duplicate code and make it into a framework or tool. Then, when a Dao needs to be used, it can be solved by directly inheriting this.
Analyze how this general Mapper is made.
It can be seen that Mapper in our Dao layer does not define any methods, but can be used in the Service layer summary, such as:
This is what general Mapper does for us. How does it do it? Take a closer look
We can use the SelectAll method as an example to see that the general Mapper uses reflection and other technologies to build Sql statements for operation
Let's take a look at the string splicing of the selectAll method:
Then let's take a look at how to make a general Service
From the method defined by our Service layer:
Except that our POJO s are different, all other methods are the same. This is our breakthrough
We can write an abstract class and an implementation class to implement these general methods. Then we can learn from the general mapper and inherit some CURDP interfaces. In this way, our Service only needs to inherit our CoreService, which inherits all CURDP interfaces. In this way, our specific implementation class not only references our interface, Then inherit one of our abstract classes, so that we can complete the writing of this general Service.
The specific structure of the code is as follows:
Code of CoreService:
package com.yxinmiracle.core.service; public interface CoreService<T> extends DeleteService<T>, InsertService<T>, PagingService<T>, SelectService<T>, UpdateService<T> { }
Abstract class code:
public abstract class CoreServiceImpl<T> implements CoreService<T> { //General mapepr protected Mapper<T> baseMapper; //Entity class of operation protected Class<T> clazz; public CoreServiceImpl(Mapper<T> baseMapper, Class<T> clazz) { this.baseMapper = baseMapper; this.clazz = clazz; } @Override public int delete(T record) { return baseMapper.delete(record); } @Override public int deleteById(Object id) { return baseMapper.deleteByPrimaryKey(id); } @Override public int insert(T record) { return baseMapper.insertSelective(record); } @Override public List<T> selectAll() { return baseMapper.selectAll(); } @Override public T selectByPrimaryKey(Object id) { return baseMapper.selectByPrimaryKey(id); } @Override public List<T> select(T record) { return baseMapper.select(record); } @Override public int updateByPrimaryKey(T record) { return baseMapper.updateByPrimaryKeySelective(record); } @Override public PageInfo<T> findByPage(Integer pageNo, Integer pageSize) { PageHelper.startPage(pageNo, pageSize); List<T> list = baseMapper.selectAll(); PageInfo<T> pageInfo = new PageInfo<T>(list); return pageInfo; } @Override public PageInfo<T> findByPage(Integer pageNo, Integer pageSize, T record) { Example example = new Example(clazz); Example.Criteria criteria = example.createCriteria(); Field[] declaredFields = record.getClass().getDeclaredFields(); for (Field declaredField : declaredFields) { try { //In case of id annotation and transient annotation, you do not need to set the value and skip directly. if (declaredField.isAnnotationPresent(Transient.class) || declaredField.isAnnotationPresent(Id.class)) { //encounter continue; } //Attribute descriptor record getClass() PropertyDescriptor propDesc = new PropertyDescriptor(declaredField.getName(), record.getClass()); //To get this value, first get the method object of the read method, and call to get the value inside Object value = propDesc.getReadMethod().invoke(record); //Object value = propDesc.getValue(declaredField.getName()); //If string if (value != null && value.getClass().getName().equals("java.lang.String")) { Column columnAnnotation = declaredField.getAnnotation(Column.class); //Judge if the length is 1, the execution = sign int length = columnAnnotation.length(); if (length == 1) { criteria.andEqualTo(declaredField.getName(), value); } else { criteria.andLike(declaredField.getName(), "%" + value + "%"); } } } catch (Exception e) { e.printStackTrace(); } } PageHelper.startPage(pageNo, pageSize); List<T> ts = baseMapper.selectByExample(example); PageInfo<T> info = new PageInfo<T>(ts); return info; } @Override public PageInfo<T> findByPageExample(Integer pageNo, Integer pageSize, Object example) { PageHelper.startPage(pageNo, pageSize); List<T> list = baseMapper.selectByExample(example); PageInfo<T> info = new PageInfo<T>(list); return info; } }
In this way, we can complete the development of some basic functions without writing a line of code in our service interface and service implementation class. For example, CURD in a single table can be completed in one minute as long as the hand speed is fast. As before, a Mapper needs to write an xml and an interface, and then the service layer needs to call the DAO layer, Now these codes don't need to be written:
- Why use abstract classes?
Because abstract classes cannot be instantiated, they cannot be managed by the spring container, causing errors in multiple objects of the same type.
- How do we know whose Mapper is in the abstract class?
We can't assign values by using @ Autowired annotation. Firstly, we don't know what type Mapper is. Secondly, the general framework we write ourselves doesn't use third-party things as much as possible. We still use Java Native things. Since it is abstract, we can use constructors and subclasses to assign values to the parent class.
- The interface inherited in the abstract class does not need to rewrite all the methods in the interface, which is also conducive to the scalability of our framework
2. General Controller extraction
The same way as general Service
Code structure:
ICoreController Code:
package com.yxinmiracle.core; public interface ICoreController<T> extends ISelectController<T>, IInsertController<T>, IPagingController<T>, IDeleteController<T>, IUpdateController<T> { }
AbstractCoreController Code:
public abstract class AbstractCoreController<T> implements ICoreController<T> { //Caller's service protected CoreService<T> coreService; //Type of caller protected Class<T> clazz; public AbstractCoreController(CoreService<T> coreService, Class<T> clazz) { this.coreService = coreService; this.clazz = clazz; } /** * Delete record * * @param id * @return */ @DeleteMapping("/{id}") @Override public Result deleteById(@PathVariable(name = "id") Object id) { coreService.deleteById(id); return new Result(true, StatusCode.OK, "Delete succeeded"); } /** * Add record * * @param record * @return */ @PostMapping @Override public Result insert(@RequestBody T record) { coreService.insert(record); return new Result(true, StatusCode.OK, "Added successfully"); } /** * Paging query record * * @param pageNo * @param pageSize * @return */ @GetMapping(value = "/search/{page}/{size}") @Override public Result<PageInfo<T>> findByPage(@PathVariable(name = "page") Integer pageNo, @PathVariable(name = "size") Integer pageSize) { PageInfo<T> pageInfo = coreService.findByPage(pageNo, pageSize); return new Result<PageInfo<T>>(true, StatusCode.OK, "Paging query succeeded", pageInfo); } @PostMapping(value = "/search/{page}/{size}") @Override public Result<PageInfo<T>> findByPage(@PathVariable(name = "page") Integer pageNo, @PathVariable(name = "size") Integer pageSize, @RequestBody T record) { PageInfo<T> pageInfo = coreService.findByPage(pageNo, pageSize, record); return new Result<PageInfo<T>>(true, StatusCode.OK, "Conditional paging query succeeded", pageInfo); } @Override @GetMapping("/{id}") public Result<T> findById(@PathVariable(name = "id") Object id) { T t = coreService.selectByPrimaryKey(id); return new Result<T>(true, StatusCode.OK, "Query single data successfully", t); } @Override @GetMapping public Result<List<T>> findAll() { List<T> list = coreService.selectAll(); return new Result<List<T>>(true, StatusCode.OK, "Query all data successfully", list); } //Update data @Override @PutMapping public Result updateByPrimaryKey(@RequestBody T record) { coreService.updateByPrimaryKey(record); return new Result(true, StatusCode.OK, "Update succeeded"); } }
Use:
@RestController @RequestMapping("/brand") public class BrandController extends AbstractCoreController<Brand> { @Autowired private BrandService brandService; @Autowired public BrandController(BrandService brandService) { super(brandService, Brand.class); } }
In this way, there is no need to knock on the controller layer.