简介

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. ElasticSearch 增删改查

    HTTP 协议本身语义:GET 获取资源.POST 新建资源(也可以用于更新资源).PUT 更新资源.DELETE 删除资源. ES通过HTTP Restful方式管理数据:1.格式:#操作 /ind ...

  2. Caused by: android.view.InflateException: Binary XML file line #18: Binary XML file line #18: Error inflating class android.widget.CheckedTextView

    困扰了我一天啊 终于吧 这个大bug  给解决掉了 可能是 当时懵逼了  竟然忘记重新构造了!!尴尬了 直接把项目的 build  文件删除重新构造了一边!!

  3. canvas像素的操作

    ###在canvas中的像素操作 到目前为止,我们尚未深入了解Canvas画布真实像素的原理,事实上, 你可以直接通过ImageData对象操纵像素数据,直接读取或将数据数组写入该对象中 ###得到场 ...

  4. css---7自定义字体

    1.Adobe illustrator AI是一种应用于出版.多媒体和在线图像的工业标准矢量插画的软件,是一款非常好的矢量图形处理工具. 该软件主要应用于印刷出版.海报书籍排版.专业插画.多媒体图像处 ...

  5. https://stackoverflow.com/与程序相关的IT技术问答网站

    https://stackoverflow.com/ Stack Overflow是一个与程序相关的IT技术问答网站.用户可以在网站免费提交问题,浏览问题,索引相关内容,在创建主页的时候使用简单的HT ...

  6. centos下 安装python相关

    centos 安装python相关 python3 https://blog.csdn.net/tanxiaob/article/details/78725301 yum -y install zli ...

  7. cordova开发笔记

    搜狐邮箱APP 使用了cordova框架,遇到了一些列问题,稍微总结记录下 扩展支持appInBrowser,用来以新窗口方式打开外链url 解决跨域问题(cordova默认当前域为localhost ...

  8. 02.万恶之源-python 运算符和编码

    一.流程控制语句if: 第一种语法: (最基本的语法) if 条件: 代码块/结果1 结果2 # 如果条件是真(True)执行结果为1,然后结果为2,如果条件为错(False), 直接结果2. 第二种 ...

  9. gradle 排除冲突依赖包

    1 , 如何查看jar包依赖源 2 结果显示: 3 排除

  10. 第十七篇:csv拆分、csv转excel方法

    首先对微软的office功能表示敬佩!可能是这些办公软件太过平常化,所以体会不到他有多牛!csv格式数据以前没接触过,百度百科定义,Comma-Separated Values,CSV,逗号分隔值,或 ...