Mybatis Plus版本升級及項目業務開發規範簡要說明

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名,而是使用實體類生成常量。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章