Mybatis Plus 入坑(含最新3.X配置)


簡介

Mybatis-Plus(簡稱MP)是一個 Mybatis 的增強工具,在 Mybatis 的基礎上只做增強不做改變,為簡化開發、提高效率而生。
使用它可以簡化單表的操作, 節省開發時間, 國人寫的文檔已經非常通俗易懂了, 所以這里只是對其進行一些規范,便於多人協作開發
如果不了解mp, 請先閱讀官方文檔, 大約耗時半小時以內
mp最新的mybatis版本是3.4.6, mybatis-spring版本是1.3.2 比我們的全局配置稍微高了一些, 看了下相關的更新以及自測,發現是兼容的, 但是不排除有些操作不支持, 支持的操作已經足夠簡化開發了
現在mp最新的版本已經升級到3.X, 所以這里2.X的文檔不在維護了

名詞解釋

GlobalConfig -> 指的是mp的全局配置文件, 版本2.X和 3.X的變化還是比較大, 可以直接看源碼的注釋
BaseMapper -> BaseMapper里面維護了許多常用的方法, 例如根據主鍵查詢記錄, 根據根據主鍵修改記錄等等, 普通mapper只需要繼承這個BaseMapper即可獲得通用的方法
Wrapper -> 條件構造抽象類, 用來生成sql的條件
LogicSqlInjector -> 邏輯sql處理器, (邏輯刪除)
PaginationInterceptor -> 分頁插件, 底層是物理分頁

實體類命名

GlobalConfig 中默認表名、字段名、使用下划線命名
eg. TrainCourse 對應的表名為 train_course
成員 courseType 對應表字段名 course_type
這些可以通過注解覆蓋
類 : @TableName("對應的表名")
字段: @TableField("對應的字段名")


@Data
public class Course {
<span class="hljs-comment">/**
 * 主鍵
 */</span>
<span class="hljs-meta">@TableId</span>(value = <span class="hljs-string">"course_id"</span>, type = IdType.AUTO)
<span class="hljs-keyword">private</span> Long courseId;

<span class="hljs-keyword">private</span> Integer courseType;
<span class="hljs-keyword">private</span> String title;

<span class="hljs-comment">/**
 * 城市
 */</span>
<span class="hljs-keyword">private</span> String city;

<span class="hljs-comment">// 因為mp在使用的時候如果直接使用str, 等於就是使用了魔法值, 不便於維護, 所以統一在實體類中維護</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String COURSE_ID = <span class="hljs-string">"course_id"</span>;
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String COURSE_TYPE = <span class="hljs-string">"course_type"</span>;

}

  1. 如果是表中不存在的字段 , 務必打上@TableField(exist = false)
  2. 如果是表示是否刪除的字段, 如deleted, 則也要打上@TableLogic 名稱和全局一樣, 如果和數據庫字段名不符合全局的映射, 則需要手動指定
  3. 數據庫中只有0,1的值, 使用Boolean類型

XML和接口

xml和接口是完全兼容之前mybatis的寫法, 所以不會影響之前的邏輯。如果xml手動定義的名稱和mp的BaseMapper中定義的一樣, 則手動指定的會覆蓋BaseMapper中的。
即順序為 自己在xml中定義的方法 > BaseMapper中的方法
如果僅僅使用到了BaseMapper中的方法, 則可以不配置XML配置文件

使用示例

在構建好實體類后, 不要在條件查詢器中直接使用魔法值, 不容易維護

// 根據主鍵id查詢
Coursecourse = mCourseMapper.selectById(id);

// 使用查詢器
Wrapper<Course> query = new EntityWrapper<>();
if (courseType != null) {
// 這里的寫法其實有點像jooq, 生成的sql為: where course_type = courseType
query.eq(Course.COURSE_TYPE, courseType);
}
query.eq(Course.NAME, name);
// deleted 使用 true 或者 false , 盡量不要使用0,1 因為0,1是魔法值
query.eq(Course.DELETED, true);
// 查詢count的數量
int result = mCourseMapper.selectCount(query);

邏輯刪除插件

一旦使用了邏輯刪除插件, 以后默認的select都會帶上 deleted = 0, 即只會查出不標記為刪除的數據, 所以不需要手動在寫

Wrapper<Test> query = new EntityWrapper<>();
query.eq(Test.DELETED, false); //這句話是多余的, 因為配置了邏輯刪除插件, 默認在select的時候會加上 deleted = 0 這個條件

// 經過測試發現, 這會帶來一個問題, 比如就是要查詢刪除的記錄, 使用下面的這句話是無效的, 所以一旦使用了邏輯刪除插件, 如果要查詢刪除了的記錄, 請手寫xml
query.eq(Test.DELETED, true); // 效果就是 WHERE deleted=0 AND (deleted = 1) 這樣明顯是查不出任何一條記錄的

2.X版本

示例配置

mp只是用自己的 MybatisSqlSessionFactoryBean 替代了 mybatis-spring的 SqlSessionFactoryBean 其余的配置都不需要改動
再 globalConfig 中, 務必加入邏輯刪除插件, 因為我們的大多數操作都是邏輯刪除。物理刪除請手動寫xml。

3.X 版本

3.X版本和2.X版本的差別還是比較大的, 主要的差別如下
升級指南可以參考 https://my.oschina.net/u/241218/blog/1838534
其中有一點, 就是下面這個方法讓我有點困惑
UpdateWrapper 特有方法

方法名 說明
set SQL SET 字段(一個字段使用一次)

因為發現在使用中並沒有使用到這個方法, 連BaseMapper里面的update, 根據下面的解釋, 也用不到, 猜測是要自己實現一個注入方法了, 這里沒有深究

配置示例

這里采用java config的方式來配置(個人喜好, 加上java config可以做一些環境判斷, 上面那個打印sql每次都需要手動打開注釋, 麻煩)
Spring Boot的方式非常簡單, 參考官網即可, 所以這里給出的是Spring mvc的配置方式, 使用的DataSource是阿里的druid
這里就直接貼配置文件了, 下面的包含了多數據源的配置

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig;
import com.baomidou.mybatisplus.extension.injector.LogicSqlInjector;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import java.util.ArrayList;
import java.util.List;
import org.apache.ibatis.plugin.Interceptor;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**

  • @author yanghuan
    */

@Configuration
public class MybatisPlusConfig {

// 環境標志, 區分dev or prod
@Autowired
private String projectStage;

<span class="hljs-comment">/**
 * 數據源a 相關信息
 */</span>
<span class="hljs-meta">@Value</span>(<span class="hljs-string">"${a.url}"</span>)
<span class="hljs-keyword">private</span> String aUrl;

<span class="hljs-meta">@Value</span>(<span class="hljs-string">"${a.username}"</span>)
<span class="hljs-keyword">private</span> String aUsername;

<span class="hljs-meta">@Value</span>(<span class="hljs-string">"${a.password}"</span>)
<span class="hljs-keyword">private</span> String aPassword;

<span class="hljs-comment">/**
 * 數據源b 相關信息
 */</span>
<span class="hljs-meta">@Value</span>(<span class="hljs-string">"${b.url}"</span>)
<span class="hljs-keyword">private</span> String bUrl;

<span class="hljs-meta">@Value</span>(<span class="hljs-string">"${b.username}"</span>)
<span class="hljs-keyword">private</span> String bUsername;

<span class="hljs-meta">@Value</span>(<span class="hljs-string">"${b.password}"</span>)
<span class="hljs-keyword">private</span> String bPassword;

<span class="hljs-comment">// 創建數據源a</span>
<span class="hljs-meta">@Bean</span>(initMethod = <span class="hljs-string">"init"</span>, destroyMethod = <span class="hljs-string">"close"</span>)
<span class="hljs-function"><span class="hljs-keyword">public</span> DruidDataSource <span class="hljs-title">aDataSource</span><span class="hljs-params">()</span></span>{
    DruidDataSource d = <span class="hljs-keyword">new</span> DruidDataSource();
    d.setUrl(aUrl);
    d.setUsername(aUsername);
    d.setPassword(aPassword);
    <span class="hljs-keyword">return</span> d;
}

<span class="hljs-meta">@Bean</span>(initMethod = <span class="hljs-string">"init"</span>, destroyMethod = <span class="hljs-string">"close"</span>)
<span class="hljs-function"><span class="hljs-keyword">public</span> DruidDataSource <span class="hljs-title">bDataSource</span><span class="hljs-params">()</span></span>{
    DruidDataSource d= <span class="hljs-keyword">new</span> DruidDataSource();
    d.setUrl(bUrl);
    d.setUsername(bUsername);
    d.setPassword(bPassword);
    <span class="hljs-keyword">return</span> d;
}

<span class="hljs-comment">// 創建全局配置</span>
<span class="hljs-meta">@Bean</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> GlobalConfig <span class="hljs-title">mpGlobalConfig</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-comment">// 全局配置文件</span>
    GlobalConfig globalConfig = <span class="hljs-keyword">new</span> GlobalConfig();
    DbConfig dbConfig = <span class="hljs-keyword">new</span> DbConfig();
    <span class="hljs-comment">// 默認為自增</span>
    dbConfig.setIdType(IdType.AUTO);
    <span class="hljs-comment">// 手動指定db 的類型, 這里是mysql</span>
    dbConfig.setDbType(DbType.MYSQL);
    globalConfig.setDbConfig(dbConfig);
    <span class="hljs-keyword">if</span> (!ProjectStageUtil.isProd(projectStage)) {
        <span class="hljs-comment">// 如果是dev環境,則使用 reload xml的功能,方便調試</span>
        globalConfig.setRefresh(<span class="hljs-keyword">true</span>);
    }
    <span class="hljs-comment">// 邏輯刪除注入器</span>
    LogicSqlInjector injector = <span class="hljs-keyword">new</span> LogicSqlInjector();
    globalConfig.setSqlInjector(injector);
    <span class="hljs-keyword">return</span> globalConfig;
}

<span class="hljs-meta">@Bean</span>(name = <span class="hljs-string">"aSqlSessionFactory"</span>)
<span class="hljs-function"><span class="hljs-keyword">public</span> MybatisSqlSessionFactoryBean <span class="hljs-title">aSqlSessionFactory</span><span class="hljs-params">(
    DruidDataSource aDataSource,
    GlobalConfig globalConfig)</span> </span>{
    <span class="hljs-keyword">return</span> getSessionFactoryBean(aDataSource, globalConfig);
}

<span class="hljs-comment">/**
 * MapperScannerConfigurer 是 BeanFactoryPostProcessor 的一個實現,如果配置類中出現 BeanFactoryPostProcessor ,會破壞默認的
 * post-processing, 如果不加static, 會導致整個都提前加載, 這時候, 取不到projectStage的值
 *
 * <span class="hljs-doctag">@return</span>
 */</span>
<span class="hljs-meta">@Bean</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> MapperScannerConfigurer <span class="hljs-title">aMapperScannerConfigurer</span><span class="hljs-params">()</span> </span>{
    MapperScannerConfigurer configurer = <span class="hljs-keyword">new</span> MapperScannerConfigurer();
    configurer.setBasePackage(<span class="hljs-string">"com.a"</span>);
    <span class="hljs-comment">// 設置為上面的 factory name</span>
    configurer.setSqlSessionFactoryBeanName(<span class="hljs-string">"bSqlSessionFactory"</span>);
    <span class="hljs-keyword">return</span> configurer;
}

<span class="hljs-meta">@Bean</span>(name = <span class="hljs-string">"bSqlSessionFactory"</span>)
<span class="hljs-function"><span class="hljs-keyword">public</span> MybatisSqlSessionFactoryBean <span class="hljs-title">bSqlSessionFactory</span><span class="hljs-params">(
    DruidDataSource bDataSource,
    GlobalConfig mpGlobalConfig)</span> </span>{
    <span class="hljs-keyword">return</span> getSessionFactoryBean(bDataSource, mpGlobalConfig);
}

<span class="hljs-function"><span class="hljs-keyword">private</span> MybatisSqlSessionFactoryBean <span class="hljs-title">getSessionFactoryBean</span><span class="hljs-params">(
    aDataSource aDataSource,
    GlobalConfig globalConfig)</span> </span>{
    MybatisSqlSessionFactoryBean sqlSessionFactoryBean = <span class="hljs-keyword">new</span> MybatisSqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(aDataSource);
    sqlSessionFactoryBean.setGlobalConfig(globalConfig);
    <span class="hljs-comment">// 源碼里面如果有configuration, 不會注入BaseMapper里面的方法, 所以這里要這樣寫</span>
    MybatisConfiguration configuration = <span class="hljs-keyword">new</span> MybatisConfiguration().init(globalConfig);
    configuration.setMapUnderscoreToCamelCase(<span class="hljs-keyword">true</span>);
    sqlSessionFactoryBean.setConfiguration(configuration);
    List&lt;Interceptor&gt; interceptors = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();
    PaginationInterceptor paginationInterceptor = <span class="hljs-keyword">new</span> PaginationInterceptor();
    <span class="hljs-comment">// 設置分頁插件</span>
    interceptors.add(paginationInterceptor);
    <span class="hljs-keyword">if</span> (!ProjectStageUtil.isProd(projectStage)) {
        <span class="hljs-comment">// 如果是dev環境,打印出sql, 設置sql攔截插件, prod環境不要使用, 會影響性能</span>
        PerformanceInterceptor performanceInterceptor = <span class="hljs-keyword">new</span> PerformanceInterceptor();
        interceptors.add(performanceInterceptor);
    }
    sqlSessionFactoryBean.setPlugins(interceptors.toArray(<span class="hljs-keyword">new</span> Interceptor[<span class="hljs-number">0</span>]));
    <span class="hljs-keyword">return</span> sqlSessionFactoryBean;
}

<span class="hljs-comment">/**
 * b 的mapperscan
 * <span class="hljs-doctag">@return</span>
 */</span>
<span class="hljs-meta">@Bean</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> MapperScannerConfigurer <span class="hljs-title">bMapperScannerConfigurer</span><span class="hljs-params">()</span> </span>{
    MapperScannerConfigurer configurer = <span class="hljs-keyword">new</span> MapperScannerConfigurer();
    configurer.setBasePackage(<span class="hljs-string">"com.b"</span>);
    <span class="hljs-comment">// 設置為上面的 factory name</span>
    configurer.setSqlSessionFactoryBeanName(<span class="hljs-string">"bSqlSessionFactory"</span>);
    <span class="hljs-keyword">return</span> configurer;
}

}

使用方式和之前的類似, 只不過 EntityWrapper 改為了 QueryWrapper
以及有一些新的lambda方法

  1. 不需要定義一個靜態變量了, 如果在成員變量上打了@TableField注解, 這里會取注解中的值, 否則就是globalConfig里面的
@Test
public void testPluralLambda() {
    TableInfoHelper.initTableInfo(null, BdSystemUser.class);
    QueryWrapper<BdSystemUser> queryWrapper = new QueryWrapper<>();
    queryWrapper.lambda().eq(BdSystemUser::getName,"sss");
    queryWrapper.lambda().eq(BdSystemUser::getUserId,1);
    BdSystemUser user = mBdSystemUserMapper.selectOne(queryWrapper);
}
  1. 之前2.X版本對復雜的or查詢不能兼容, 即使用 Wrapper 的or方法, 默認是不加括號的
    不過對於負責的查詢, 還是推薦手寫sql, 這里展示一下如何使用mp來構建
@Test
public void test() {
    Wrapper<BdSystemUser> wrapper = new QueryWrapper<BdSystemUser>().lambda().eq(BdSystemUser::getName, 123)
        .or(c -> c.eq(BdSystemUser::getExternalStaff, 1).eq(BdSystemUser::getUserId, 2))
        .eq(BdSystemUser::getUserId, 1);
    BdSystemUser user = mBdSystemUserMapper.selectOne(wrapper);
// 對應的sql: SELECT * FROM bdsystem_user WHERE deleted = 0 AND NAME = ? OR ( externalstaff = ? AND userid = ? ) AND userid = ?
// 可以看到, or中的語句是在括號里的
}

3.可以把where 后的條件防止在一個map中

@Test
public void testCompare() {
    Map<String, Object> map = new HashMap<>();
    map.put("userid", 1);
    map.put("ldap", "luoshu");
    QueryWrapper<BdSystemUser> queryWrapper = new QueryWrapper<BdSystemUser>()
        .allEq(true, map, false);
    BdSystemUser user = mBdSystemUserMapper.selectOne(queryWrapper);
    // SELECT userid,name,ldap,stafftype,leader,departmenttype,groupid,externalstaff,mobile,created,lastmodified,deleted FROM bdsystem_user WHERE deleted=0 AND ldap = ? AND userid = ?
    // 可以看到, map中對應的值, 是where后的條件
}

更詳細的demo可以參照 demo

3.X 注意事項

  • BaseMapper的selectOne() 現在在返回多條語句的時候,會拋異常, 需要手動在條件中處理, 使用wrapper.last()方法.
  • 如果mp和tddl這樣的中間件進行集成, 在分頁插件的創建時候, 需要指定數據庫方言, 之前可以不指定的原因是mp通過解析jdbc url的時候可以獲取到對應的方言
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
 // 這邊因為接了tddl, 必須手動設置方言, 否則分頁會拋異常
paginationInterceptor.setDialectType(DbType.MYSQL.getDb());
原文地址:https://www.cnblogs.com/hinsy/p/9668684.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM