Mybatis Plus版本升级及项目业务开发规范简要说明

本文介绍了Mybatis Plus从逆向工程到版本升级后的业务开发规范。建议使用Mybatis Plus的代码生成器,避免手动实现CRUD,推荐通过IService接口调用Service实现类以利用其提供的CRUD方法。此外,讨论了如何使用条件构造器进行复杂查询,并提出了自定义分页和实体类常量的编写规范。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Mybatis Plus版本升级及项目业务开发规范简要说明

码农: Alias

> 版本升级说明:此次将Mybatis Plus的版本由2.x升级到3.1.2版本,最新版本是3.2,由于3.2版本的sql分析打印插件变动需要额外配置,故暂时不升级到3.2版本(因为懒)。

一.从Mybatis逆向工程说起

  • MyBatis-Plus 的代码生成器可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码。执行代码生成类,生成代码,如图:
    在这里插入图片描述
  1. 延用之前项目开发的规范,在我们逆向工程代码生成策略中主要有这两个:
 strategy.setSuperEntityClass("xxx.xxx.dao.SuperEntity"); 
 strategy.setSuperMapperClass("xxx.xxx.dao.SuperMapper");
 strategy.setSuperServiceImplClass("xxx.xxx.service.SuperService");

​ 前两行代码主要是用来设定生成的实体类与mapper接口的超类。其中SuperMapper继承了MP的BaseMapper类,该类已经封装了常规的CRUD方法(因此,提醒各位,DAO层常规的CRUD接口可以不必再次实现,以达到常规CRUD方法去SQL化、去XML配置化)。第三行代码主要是设置Service实现类的超类SuperService。

​ 使用了3.1.2版本的MP生成的Service接口提供了通用 Service CRUD 封装IService接口,进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆。如果存在自定义通用 Service 方法的可能,可以创建 IBaseService 继承 Mybatis-Plus 提供的基类。目前CMS项目的SuperService类就是自定义通用Service方法的类,我在该类的基础上继承了ServiceImpl,扩展了ServiceImpl提供的功能。

  • Service样例代码

    /**
     * <p>
     * 文章内容扩展表,增加扩展字段 服务类
     * </p>
     *
     * @author alias
     * @since 2019-12-05
     */
    public interface IInfoExtendService extends IService<InfoExtend> {
    
    }
    

​ 接着我们看看Service接口实现类,该类继承了SuperService类差不多。该类也聚合BaseMapper、ServiceImpl等类的功能。

  • ServiceImpl源码:
@SuppressWarnings("unchecked")
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {

    protected Log log = LogFactory.getLog(getClass());

    @Autowired
    protected M baseMapper;
    
    .....
}
  • Service实现类样例代码:

    /**
     * <p>
     * 文章内容扩展表,增加扩展字段 服务实现类
     * </p>
     *
     * @author alias
     * @since 2019-12-05
     */
    @Service
    public class InfoExtendServiceImpl extends SuperService<InfoExtendMapper, InfoExtend> implements IInfoExtendService {
    
    }
    
  • SuperService源码:

    public abstract class SuperService<M extends BaseMapper<T>,T> extends ServiceImpl<M,T>  {
    
        private static final Log logger = LogFactory.getLog(SuperService.class);
    
        @Autowired
        protected M superMapper;
     
        ......
    }
    

二.版本升级后,从实际业务开发入手

由上面可知,我们的业务接口层继承了IService接口,接口的实现类继承了SuperService类。因此,我们在开发时只需注入接口类即可使用到MP提供的CRUD方法(包括BaseMapper、IService等方法)。推荐业务层之间的CRUD方法调用只通过Servie接口调用,因为注入Service接口后基本无所不能了= =,话不多说,上代码。

1.我们以KnInfoExtend、KnInfo这两张表为例子说明。

​ KnInfoExtend相关的Service接口以及实现类在上面已经给出了,这里给我KnInfo相关的业务类。

  • KnInfo相关业务类及接口:

    public interface InfoService extends IService<Info> {
    	//以下接口是不推荐使用方法示范
        public Info saveInfo(InfoVO infoVo); 
        public InfoVO findInfo(String id);
    }
    
    @Service(value = "knInfoService")
    public class InfoServiceImpl extends SuperService<InfoMapper, Info> implements InfoService {
    	
        //不提倡的使用方式
        @Autowired
        private InfoExtendMapper InfoExtendMapper;
        
        //不提倡的使用方式
        @Autowired
        private InfoExtendServiceImpl InfoExtendServiceImpl;
        
        //提倡使用方式
        @Autowired
        private IInfoExtendService infoExtendService;
      
    }
    

以上说的不提倡的使用方式是基于使用Mybatis Plus框架时不提倡的使用方式,因为人家给你一辆法拉利,你用来当拖拉机。

对于纯粹使用mybatis框架的,以上不提倡做法是很常规的,包括我也不是这么做了= =

同学们,上面的不提倡的使用方式你们是否编写过,不管你承不承认,反正老衲年轻的时候确实有这么写过,这么写的原因可能如下:

  • 我在我的业务处理中只需要调用InfoMapper底层的CRUD的接口就行了

  • 有时候业务聚合需要,不得不依赖InfoExtendServiceImpl来调用其方法。

  • 曾有dalao提倡即使数据底层的CRUD的接口,可以在Service实现类依赖Mapper接口,在实现类中提供这个方法。实例如下:

      @Mapper
      public interface XxxMapper {
          //定义selectByXxxx方法
      	XXXDTO selectByXxxx(XXXDTO xxx);
      }
      
      // XxxMapper.xml文件巴拉巴拉一堆暴写,自己意会不言传哈
      
      @Service
      public class XxxServiceImpl implements IXxxService {
          @Autowired
          private XxxMapper xxxMapper;
          
          public XXXDTO selectByXxxx(XXXDTO xxx) {
              return xxxMapper.selectByXxxx(xxx);
          }
      }
      
      @Service
      public class YyyServiceImpl implements IYyyService {
          //@Autowired
         // private XxxMapper xxxMapper;
          
           @Autowired
          private IXxxService xxxService;
          
          public XXXDTO selectByXxxx(XXXDTO xxx) {
              return xxxService.selectByXxxx(xxx);
          }
      }
      
    

    有些同学是不是觉得没必要这样子,觉得这样子繁琐。

  • 扯回来,回归正题,基于Mybatis Plus框架开发,建议不要编写以上不推荐的使用方式。人家MP宗旨就是为简化开发、提高效率而生。现在我们看看为何只注入IInfoExtendService就能使用那么多功能的。

    • Service实现类提供的CRUD方法,即聚合了IService、ServiceImpl提供的方法。

在这里插入图片描述

  • 如果IService、ServiceImpl提供的方法不是你想要的,你想直接操作Mapper接口,肯定是可以,毕竟ServiceImpl已经注入了Mapper接口了
    在这里插入图片描述

  • 找茬,同学们肯定是想说,万一我要自定义条件查询呢,换做之前使用mybatis的习惯还是要在Mapper接口编写接口方法,在Xml文件巴拉巴拉写一堆。然后你这里就会想,这种情况怎么处理。方法肯定是有的,那就是使用MP的条件构造器,这里不多说,很好用的一个东西,自己看官方文档
    在这里插入图片描述

  • 接着找茬,用了一段时间,同学发现,复杂查询(比如关联查询)貌似条件构造器也支持不了,只能回归原始,手操sql了。那这时候,又回归了Mybatis使用时的做法,在Mapper接口编写接口方法,在Xml文件巴拉巴拉写一堆。接下来呢?我推荐的做法是只注入Service接口,那我在Mapper接口自定义的方法怎么调用呀?这里确实是找对茬了,不是说只注入Service接口的方式调用不了,只是我提供的方法比较笨重(大佬有好的方法告知一声)。

    • 方法一:类型强转
      KnInfoExtendMapper baseMapper = (KnInfoExtendMapper) knInfoExtendService.getBaseMapper();
      //调用自定义CRUD方法
      baseMapper.findKnInfoExtendByInfoId();
    
    • 方法二:在Service实现类,申明该方法,间接调用自定义的Mapper方法
        //KnInfoMapper接口:
    	KnInfoExtend findKnInfoExtendByInfoId(String id);
        //KnInfoExtendServiceImpl类:
    	@Override
    	public KnInfoExtend findKnInfoExtendByInfoId() {
            KnInfoExtend knInfoExtend = this.superMapper.findKnInfoExtendByInfoId("id");
            return knInfoExtend;
        }
    	//InfoServiceImpl类
        knInfoExtendService.findKnInfoExtendByInfoId("");
    
  • 至此,推荐业务层之间的CRUD方法调用只通过Servie接口调用大致有如下原因:

    • MP已提供通用的Service CRUD方法
    • MP的构造器是我们可以摆脱手操sql的烦恼(复杂查询可能就无法了)
    • MP的框架封装能力,MP已在在Service实现类中继承ServiceImpl类,该类内部聚合了BaseMapper,已经自动为你注入了。所以你在业务层再次注入就显得多余了。

三.啰嗦多几句,常规写法规范定义

这里啰嗦多几句,讲讲一些常规的编写规范吧。

1.这里讲自定义分页的写法,分页其实也就是使用MP分页插件的实例的方式来开发吧

  • UserMapper.java 方法内容
public interface UserMapper{//可以继承或者不继承BaseMapper
    /**
     * <p>
     * 查询 : 根据state状态查询用户列表,分页显示
     * 注意!!: 如果入参是有多个,需要加注解指定参数名才能在xml中取值
     * </p>
     *
     * @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位(你可以继承Page实现自己的分页对象)
     * @param state 状态
     * @return 分页对象
     */
    IPage<User> selectPageVo(Page page, @Param("state") Integer state);
}
  • UserMapper.xml 等同于编写一个普通 list 查询,mybatis-plus 自动替你分页
<select id="selectPageVo" resultType="com.baomidou.cloud.entity.UserVo">
    SELECT id,name FROM user WHERE state=#{state}
</select>
  • UserServiceImpl.java 调用分页方法
public IPage<User> selectUserPage(Page<User> page, Integer state) {
   // 不进行 count sql 优化,解决 MP 无法自动优化 SQL 问题,这时候你需要自己查询 count 部分
   // page.setOptimizeCountSql(false);
   // 当 total 为小于 0 或者设置 setSearchCount(false) 分页插件不会进行 count 查询
   // 要点!! 分页返回的对象与传入的对象是同一个
   return userMapper.selectPageVo(page, state);
}

2.实体类常量使用

在我们的逆向工程工具中,我在生成策略加了strategy.setEntityColumnConstant(true);,这个就是在生成我们的实体类的时候,加上每个字段的常量(即数据库定义的colum名称),加上这个也是有用处的,这个举个栗子

  • Info实体类
package com.oppein.mtds.cms.info.dao.entity;


import com.oppein.mtds.cms.info.dao.SuperEntity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;

import java.util.Date;

@TableName("kn_info")
public class Info extends SuperEntity<Info> {

    private static final long serialVersionUID = 1L;

    /**
     * 栏目id
     */
	@TableField("category_id")
	private String categoryId;
    /**
     * 标题
     */
	private String title;

	/*文章父id*/
	@TableField("pid")
	private String pid;
    /**
     * 简短标题
     */
	@TableField("short_title")
	private String shortTitle;
    /**
     * tag标签
     */
	private String tag;
    ......

	public static final String PID = "pid";

	public static final String CATEGORY_ID = "category_id";

	public static final String TITLE = "title";

	public static final String SHORT_TITLE = "short_title";

	public static final String TAG = "tag";
    ......

}

  • 业务实现类业务处理:
  public void demo(InfoVO infoVO) {
        Wrapper wrapper = new QueryWrapper()
                //推荐写法
                .eq(Objects.nonNull(infoVO.getId()), Info.ID, infoVO.getId())
                //不推荐这样写,具体为啥,不用我解释了
                .eq(Objects.nonNull(infoVO.getId()), "id", infoVO.getId());
        List list = this.selectObjs(wrapper);
    }

上面在使用条件构造器进行查询的时候,eq方法的入参有column名,不推荐直接写column名,而是使用实体类生成常量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值