基于SpringBoot中通用Mapper源码解读以及设计通用Service和Controller

15 篇文章 0 订阅
5 篇文章 0 订阅

1、通用的Service抽取

​ 通过通用Mapper的思想,也就是将一些重复的代码抽出来,制作成一个框架或者工具,之后当某个Dao需要使用的时候,直接继承这个就可以解决了。

​ 分析一下这个通用Mapper是怎么制作的。image-20210809204115874

​ 可以看见我们的Dao层中的Mapper并没有定义任何的方法,但是却可以在Service层总结进行使用,如:

image-20210809204215208

​ 这都是通用Mapper帮我们做的事情,至于是怎么做的呢? 进一步看看

​ 我们可以通过SelectAll这个方法作为例子来进行查看,可以发现通用Mapper就是利用了反射等技术来构建Sql语句进行操作的

image-20210809210528619

​ 下面查看一下selectAll方法的字符串拼接:

image-20210809210558550


​ 那接下来看一看通用Service应该怎么制作

​ 从我们的Service层定义的方法中来看:

image-20210809210946781

​ 除了我们的POJO是不一样的之外,其他不管是方法还是别的都是一样的,这就是我们的突破口

​ 我们可以编写一个抽象类,也是一个实现类,去实现这些通用的方法,之后我们可以学习通用mapper一样,继承一些CURDP的接口,这样我们的Service只需要继承我们的CoreService,这个CoreService是继承了CURDP所有接口,这样我们具体的实现类就除了引用我们的接口之外,再去继承一个我们的抽象类,这样我们就可以完成这个通用Service的编写。

​ 具体编写代码的结构如下:

image-20210810094657646

​ CoreService的代码:

package com.yxinmiracle.core.service;

public interface CoreService<T> extends
        DeleteService<T>,
        InsertService<T>,
        PagingService<T>,
        SelectService<T>,
        UpdateService<T> {

}

​ 抽象类的代码:

public abstract class CoreServiceImpl<T> implements CoreService<T> {
    //通用mapepr
    protected Mapper<T> baseMapper;
    //操作的实体类
    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 {
                //遇到 id注解 和 transient注解 不需要进行值的设置 直接跳过。
                if (declaredField.isAnnotationPresent(Transient.class) || declaredField.isAnnotationPresent(Id.class)) {
                    //遇到
                    continue;
                }
                //属性描述器  record.getClass()
                PropertyDescriptor propDesc = new PropertyDescriptor(declaredField.getName(), record.getClass());
                //获取这个值  先获取读方法的方法对象,并调用获取里面的值
                Object value = propDesc.getReadMethod().invoke(record);
                //Object value = propDesc.getValue(declaredField.getName());
                //如果是字符串
                if (value != null && value.getClass().getName().equals("java.lang.String")) {
                    Column columnAnnotation = declaredField.getAnnotation(Column.class);
                    //判断如果是长度为1 则 执行=号
                    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;
    }
    
}

​ 这样一来,在我们的Service接口以及Service的实现类中一行代码都不用写就可以完成一些基本功能的开发,比如,单表中的CURD,基本手上只要手速快,一分钟就可以完成,不用像以前一样一个Mapper需要写一个xml,写一个接口,之后service层还要去调用DAO层,现在这些代码都不需要写了:

image-20210810095130054

  • 为什么要用抽象类?

因为抽象类是不能实例化的,这样就不能交给spring容器进行管理,引起多个相同类型对象的错误。

  • 那抽象类中的Mapper我们怎么知道是谁的Mapper呢?

我们不能通过使用@Autowired注解的方式来进行赋值,首先我们并不知道Mapper是什么类型的,其次我们自己写的通用框架尽量不用使用第三方的东西,我们还是使用Java原生的东西来进行。既然是抽象来,我们就可以利用构造函数,使用子类来给父类进行赋值

  • 在抽象类中继承的接口,是不需要将接口中的全部方法进行重写的,这样一来也很利于我们框架的扩展性

2、通用的Controller抽取

​ 跟通用Service也是一样的方式

​ 代码结构:

微信截图_20210810100309

ICoreController代码:

package com.yxinmiracle.core;


public interface ICoreController<T> extends
        ISelectController<T>,
        IInsertController<T>,
        IPagingController<T>,
        IDeleteController<T>,
        IUpdateController<T> {
}

AbstractCoreController代码:

public abstract class AbstractCoreController<T> implements ICoreController<T> {

    //调用方的service
    protected CoreService<T> coreService;
    //调用方的类型
    protected Class<T> clazz;

    public AbstractCoreController(CoreService<T> coreService, Class<T> clazz) {
        this.coreService = coreService;
        this.clazz = clazz;
    }

    /**
     * 删除记录
     *
     * @param id
     * @return
     */
    @DeleteMapping("/{id}")
    @Override
    public Result deleteById(@PathVariable(name = "id") Object id) {
        coreService.deleteById(id);
        return new Result(true, StatusCode.OK, "删除成功");
    }

    /**
     * 添加记录
     *
     * @param record
     * @return
     */
    @PostMapping
    @Override
    public Result insert(@RequestBody T record) {
        coreService.insert(record);
        return new Result(true, StatusCode.OK, "添加成功");
    }

    /**
     * 分页查询记录
     *
     * @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, "分页查询成功", 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, "条件分页查询成功", 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, "查询单个数据成功", t);
    }

    @Override
    @GetMapping
    public Result<List<T>> findAll() {
        List<T> list = coreService.selectAll();
        return new Result<List<T>>(true, StatusCode.OK, "查询所有数据成功", list);
    }

    //更新数据
    @Override
    @PutMapping
    public Result updateByPrimaryKey(@RequestBody T record) {
        coreService.updateByPrimaryKey(record);
        return new Result(true, StatusCode.OK, "更新成功");
    }
}

​ 使用:

@RestController
@RequestMapping("/brand")
public class BrandController extends AbstractCoreController<Brand> {
    @Autowired
    private BrandService brandService;

    @Autowired
    public BrandController(BrandService brandService) {
        super(brandService, Brand.class);
    }
}

​ 这样一来controller层也是一行都不用敲了。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Boot可以很方便地整合通用Mapper,只需要在pom.xml添加通用Mapper的依赖,然后在配置文件配置数据源和通用Mapper的相关属性即可。 具体步骤如下: 1. 在pom.xml添加通用Mapper的依赖: ```xml <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> <version>2.1.5</version> </dependency> ``` 2. 在配置文件配置数据源和通用Mapper的相关属性: ```yaml spring: datasource: url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: root driver-class-name: com.mysql.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true mapper: mappers: - tk.mybatis.mapper.common.Mapper not-empty: false identity: MYSQL ``` 其mapper.mappers指定了要使用的Mapper接口,这里使用了通用MapperMapper接口;mapper.identity指定了主键生成策略,这里使用了MySQL的自增长主键。 3. 在Mapper接口继承通用MapperMapper接口即可使用通用Mapper提供的方法: ```java public interface UserMapper extends Mapper<User> { } ``` 这样就可以使用通用Mapper提供的方法来进行数据库操作了,例如: ```java @Autowired private UserMapper userMapper; public void addUser(User user) { userMapper.insert(user); } public void updateUser(User user) { userMapper.updateByPrimaryKeySelective(user); } public void deleteUser(Long id) { userMapper.deleteByPrimaryKey(id); } public User getUser(Long id) { return userMapper.selectByPrimaryKey(id); } public List<User> getUsers() { return userMapper.selectAll(); } ``` 以上就是Spring Boot整合通用Mapper的基本步骤,希望能对你有所帮助。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值