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>;
}
- 如果是表中不存在的字段 , 务必打上@TableField(exist = false)
- 如果是表示是否删除的字段, 如deleted, 则也要打上@TableLogic 名称和全局一样, 如果和数据库字段名不符合全局的映射, 则需要手动指定
- 数据库中只有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<Interceptor> interceptors = <span class="hljs-keyword">new</span> ArrayList<>();
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方法
- 不需要定义一个静态变量了, 如果在成员变量上打了@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);
}
- 之前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配置)的更多相关文章
- oracle入坑日记<六>自增列创建和清除(含序列和触发器的基础用法)
0 前言 用过 SQLserver 和 MySQL 的自增列(auto_increment),然而 Oracle 在建表设置列时却没有自增列. 查阅资料后发现 Oracle 的自增列需要手动编写. ...
- oracle入坑日记<二>认识oracle(含sqlplus基础使用)
1.SID(数据库实例) 1.1. oracle安装的时候有一项叫[全局数据库名]的填写项,这个就是oracle的SID也是数据库的唯一标识符: 1.2.一个oracle数据库有且只有一个SID(一般 ...
- 入“坑”mybatis后如何挣脱?
既然已经入"坑"mybatis了,你竟然还想着挣脱,我是不会让你挣脱的~ 当然我有一个算是挣脱的办法.那就是把它学会.理解透.这样我们也就不用在坑里一直徘徊,也算得上是一种挣脱吧! ...
- webpack入坑之旅(一)不是开始的开始
最近学习框架,选择了vue,然后接触到了vue中的单文件组件,官方推荐使用 Webpack + vue-loader构建这些单文件 Vue 组件,于是就开始了webpack的入坑之旅.因为原来没有用过 ...
- 【Xbox one S】开箱&开机&初入坑心得
再来一发水贴,先上产品标准照镇贴: 前言 身为一个资深单机游戏玩家,常年混迹在PC平台,但内心深处一直对主机有种迷之向往,感觉那才是单机游戏的正处之地,坐沙发上拿着手柄对着电视跌宕起伏才是正确的游戏姿 ...
- 入坑IT十年(二)技术以外
上一篇博客里提到:技术越来越简单,发布后不久,就看到<技术并不是越来越简单>,这显然是打擂台来了. 技术究竟是不是越来越简单?其实这个问题,要看你究竟是以什么角度来思考这个问题.我们可以举 ...
- 大神都在看的RxSwift 的完全入坑手册
大神都在看的RxSwift 的完全入坑手册 2015-09-24 18:25 CallMeWhy callmewhy 字号:T | T 我主要是通过项目里的 Rx.playground 进行学习和了解 ...
- oracle入坑日记<四>表空间
1 表空间是什么 1.1.数据表看做的货品,表空间就是存放货品的仓库.SQLserver 用户可以把表空间看做 SQLserver 中的数据库. 1.2.引用[日记二]的总结来解释表空间. 一个数 ...
- oracle入坑日记<三>用户详解(角色理解)
1 用户是什么 1.1.权限管理是Oracle的精华,不同用户登录到同一数据库中,可能看到不同数量的表,拥有不同的权限.Oracle 的权限分为系统权限和数据对象权限,共一百多种.如果把Oracl ...
随机推荐
- ElasticSearch 增删改查
HTTP 协议本身语义:GET 获取资源.POST 新建资源(也可以用于更新资源).PUT 更新资源.DELETE 删除资源. ES通过HTTP Restful方式管理数据:1.格式:#操作 /ind ...
- Caused by: android.view.InflateException: Binary XML file line #18: Binary XML file line #18: Error inflating class android.widget.CheckedTextView
困扰了我一天啊 终于吧 这个大bug 给解决掉了 可能是 当时懵逼了 竟然忘记重新构造了!!尴尬了 直接把项目的 build 文件删除重新构造了一边!!
- canvas像素的操作
###在canvas中的像素操作 到目前为止,我们尚未深入了解Canvas画布真实像素的原理,事实上, 你可以直接通过ImageData对象操纵像素数据,直接读取或将数据数组写入该对象中 ###得到场 ...
- css---7自定义字体
1.Adobe illustrator AI是一种应用于出版.多媒体和在线图像的工业标准矢量插画的软件,是一款非常好的矢量图形处理工具. 该软件主要应用于印刷出版.海报书籍排版.专业插画.多媒体图像处 ...
- https://stackoverflow.com/与程序相关的IT技术问答网站
https://stackoverflow.com/ Stack Overflow是一个与程序相关的IT技术问答网站.用户可以在网站免费提交问题,浏览问题,索引相关内容,在创建主页的时候使用简单的HT ...
- centos下 安装python相关
centos 安装python相关 python3 https://blog.csdn.net/tanxiaob/article/details/78725301 yum -y install zli ...
- cordova开发笔记
搜狐邮箱APP 使用了cordova框架,遇到了一些列问题,稍微总结记录下 扩展支持appInBrowser,用来以新窗口方式打开外链url 解决跨域问题(cordova默认当前域为localhost ...
- 02.万恶之源-python 运算符和编码
一.流程控制语句if: 第一种语法: (最基本的语法) if 条件: 代码块/结果1 结果2 # 如果条件是真(True)执行结果为1,然后结果为2,如果条件为错(False), 直接结果2. 第二种 ...
- gradle 排除冲突依赖包
1 , 如何查看jar包依赖源 2 结果显示: 3 排除
- 第十七篇:csv拆分、csv转excel方法
首先对微软的office功能表示敬佩!可能是这些办公软件太过平常化,所以体会不到他有多牛!csv格式数据以前没接触过,百度百科定义,Comma-Separated Values,CSV,逗号分隔值,或 ...