简介

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

Mybatis Plus 入坑(含最新3.X配置)的更多相关文章

  1. oracle入坑日记<六>自增列创建和清除(含序列和触发器的基础用法)

    0   前言 用过 SQLserver 和 MySQL 的自增列(auto_increment),然而 Oracle 在建表设置列时却没有自增列. 查阅资料后发现 Oracle 的自增列需要手动编写. ...

  2. oracle入坑日记<二>认识oracle(含sqlplus基础使用)

    1.SID(数据库实例) 1.1. oracle安装的时候有一项叫[全局数据库名]的填写项,这个就是oracle的SID也是数据库的唯一标识符: 1.2.一个oracle数据库有且只有一个SID(一般 ...

  3. 入“坑”mybatis后如何挣脱?

    既然已经入"坑"mybatis了,你竟然还想着挣脱,我是不会让你挣脱的~ 当然我有一个算是挣脱的办法.那就是把它学会.理解透.这样我们也就不用在坑里一直徘徊,也算得上是一种挣脱吧! ...

  4. webpack入坑之旅(一)不是开始的开始

    最近学习框架,选择了vue,然后接触到了vue中的单文件组件,官方推荐使用 Webpack + vue-loader构建这些单文件 Vue 组件,于是就开始了webpack的入坑之旅.因为原来没有用过 ...

  5. 【Xbox one S】开箱&开机&初入坑心得

    再来一发水贴,先上产品标准照镇贴: 前言 身为一个资深单机游戏玩家,常年混迹在PC平台,但内心深处一直对主机有种迷之向往,感觉那才是单机游戏的正处之地,坐沙发上拿着手柄对着电视跌宕起伏才是正确的游戏姿 ...

  6. 入坑IT十年(二)技术以外

    上一篇博客里提到:技术越来越简单,发布后不久,就看到<技术并不是越来越简单>,这显然是打擂台来了. 技术究竟是不是越来越简单?其实这个问题,要看你究竟是以什么角度来思考这个问题.我们可以举 ...

  7. 大神都在看的RxSwift 的完全入坑手册

    大神都在看的RxSwift 的完全入坑手册 2015-09-24 18:25 CallMeWhy callmewhy 字号:T | T 我主要是通过项目里的 Rx.playground 进行学习和了解 ...

  8. oracle入坑日记<四>表空间

    1   表空间是什么 1.1.数据表看做的货品,表空间就是存放货品的仓库.SQLserver 用户可以把表空间看做 SQLserver 中的数据库. 1.2.引用[日记二]的总结来解释表空间. 一个数 ...

  9. oracle入坑日记<三>用户详解(角色理解)

    1   用户是什么 1.1.权限管理是Oracle的精华,不同用户登录到同一数据库中,可能看到不同数量的表,拥有不同的权限.Oracle 的权限分为系统权限和数据对象权限,共一百多种.如果把Oracl ...

随机推荐

  1. nodejs . module.exports

    //utils.js let a = 100; console.log(module.exports); //能打印出结果为:{} console.log(exports); //能打印出结果为:{} ...

  2. ionic 滚动条 ion-scroll 用于创建一个可滚动的容器

    ionic 滚动条 ion-scroll ion-scroll 用于创建一个可滚动的容器. 用法 <ion-scroll [delegate-handle=""] [dire ...

  3. Vue键盘事件

    键盘事件一 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <tit ...

  4. springboot EL @Value

    一,springboot中 看一下代码: @Controller public class HelloController { //读取枚举值 @Value("#{T(com.example ...

  5. 既然 start() 方法会调用 run() 方法,为什么我们调用 start() 方法,而不直接调用 run() 方法?

    当你调用 start() 方法时,它会新建一个线程然后执行 run() 方法中的代码.如果直接调用 run() 方法,并不会创建新线程,方法中的代码会在当前调用者的线程中执行

  6. vue 挂载点 实例 模板

    挂载点:vue实例 里面的el属性 对应的 id 所在的dom节点 模板:指的是挂载点内部的内容           模板可以写在挂载点内部           也可以写在属性里面 demo < ...

  7. What is the difference between HTTP_CLIENT_IP and HTTP_X_FORWARDED_FOR

    What is the difference between HTTP_CLIENT_IP and HTTP_X_FORWARDED_FOR? it is impossible to say. Dif ...

  8. PyQt6的在线安装与环境配置

    https://www.jianshu.com/p/185e277e0058 一,安装好Python,Pycharm 二,安装或更新pip C:\> python -m pip install ...

  9. scala中类的简单使用记录

    import scala.collection.mutable.ArrayBuffer /** * scala 中内部类的使用 */ class Classes { class Stu(name:St ...

  10. vue+Iview+gulp 生成文档说明

    1.安装npm gulp相关插件 比如:gulp.gulp-concat.gulp-htmlmin.gulp-cssmin.gulp-cheerio.gulp-clean 2. 编写gulpfile. ...