一、快速入门
导入依赖,具体版本根据自己需要可以选择:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>Latest Version</version>
</dependency>
导入mybatis-plus就不用导入mybatis。
数据源配置,这里直接使用spring boot 自带的数据源。
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/db_test/useSSL=false&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
mybatis-plus中,提供了一个BaseMapper,这个mapper整合了单表的CRUD操作,只要使用Mapper集成,就可以直接使用,同时集成了分页的插件,不过要配置拦截器才能使用。主要扫描接口,@MapperScan("com.mg.mapper"):
/*
* Copyright (c) 2011-2020, hubin (jobob@qq.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.baomidou.mybatisplus.core.mapper;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
/*
:`
.:,
:::,,.
:: `::::::
::` `,:,` .:`
`:: `::::::::.:` `:';,`
::::, .:::` `@++++++++:
`` :::` @+++++++++++#
:::, #++++++++++++++`
,: `::::::;'##++++++++++
.@#@;` ::::::::::::::::::::;
#@####@, :::::::::::::::+#;::.
@@######+@:::::::::::::. #@:;
, @@########':::::::::::: .#''':`
;##@@@+:##########@::::::::::: @#;.,:.
#@@@######++++#####'::::::::: .##+,:#`
@@@@@#####+++++'#####+::::::::` ,`::@#:`
`@@@@#####++++++'#####+#':::::::::::@.
@@@@######+++++''#######+##';::::;':,`
@@@@#####+++++'''#######++++++++++`
#@@#####++++++''########++++++++'
`#@######+++++''+########+++++++;
`@@#####+++++''##########++++++,
@@######+++++'##########+++++#`
@@@@#####+++++############++++;
;#@@@@@####++++##############+++,
@@@@@@@@@@@###@###############++'
@#@@@@@@@@@@@@###################+:
`@#@@@@@@@@@@@@@@###################'`
:@#@@@@@@@@@@@@@@@@@##################,
,@@@@@@@@@@@@@@@@@@@@################;
,#@@@@@@@@@@@@@@@@@@@##############+`
.#@@@@@@@@@@@@@@@@@@#############@,
@@@@@@@@@@@@@@@@@@@###########@,
:#@@@@@@@@@@@@@@@@##########@,
`##@@@@@@@@@@@@@@@########+,
`+@@@@@@@@@@@@@@@#####@:`
`:@@@@@@@@@@@@@@##@;.
`,'@@@@##@@@+;,`
``...``
_ _ /_ _ _/_. ____ / _
/ / //_//_//_|/ /_\ /_///_/_\ Talk is cheap. Show me the code.
_/ /
*/
/**
* <p>
* Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
* </p>
* <p>
* 这个 Mapper 支持 id 泛型
* </p>
*
* @author hubin
* @since 2016-01-23
*/
public interface BaseMapper<T> {
/**
* <p>
* 插入一条记录
* </p>
*
* @param entity 实体对象
*/
int insert(T entity);
/**
* <p>
* 根据 ID 删除
* </p>
*
* @param id 主键ID
*/
int deleteById(Serializable id);
/**
* <p>
* 根据 columnMap 条件,删除记录
* </p>
*
* @param columnMap 表字段 map 对象
*/
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/**
* <p>
* 根据 entity 条件,删除记录
* </p>
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* <p>
* 删除(根据ID 批量删除)
* </p>
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
/**
* <p>
* 根据 ID 修改
* </p>
*
* @param entity 实体对象
*/
int updateById(@Param(Constants.ENTITY) T entity);
/**
* <p>
* 根据 whereEntity 条件,更新记录
* </p>
*
* @param entity 实体对象 (set 条件值,不能为 null)
* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
*/
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
/**
* <p>
* 根据 ID 查询
* </p>
*
* @param id 主键ID
*/
T selectById(Serializable id);
/**
* <p>
* 查询(根据ID 批量查询)
* </p>
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
/**
* <p>
* 查询(根据 columnMap 条件)
* </p>
*
* @param columnMap 表字段 map 对象
*/
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/**
* <p>
* 根据 entity 条件,查询一条记录
* </p>
*
* @param queryWrapper 实体对象
*/
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* <p>
* 根据 Wrapper 条件,查询总记录数
* </p>
*
* @param queryWrapper 实体对象
*/
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* <p>
* 根据 entity 条件,查询全部记录
* </p>
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* <p>
* 根据 Wrapper 条件,查询全部记录
* </p>
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* <p>
* 根据 Wrapper 条件,查询全部记录
* 注意: 只返回第一个字段的值
* </p>
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* <p>
* 根据 entity 条件,查询全部记录(并翻页)
* </p>
*
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* <p>
* 根据 Wrapper 条件,查询全部记录(并翻页)
* </p>
*
* @param page 分页查询条件
* @param queryWrapper 实体对象封装操作类
*/
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
}
在Mapper中直接集成即可:
package com.mg.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mg.pojo.User;
/**
* 实现基本的接口,就可以使用CRUD
*/
public interface UserMapper extends BaseMapper<User> {
}
简单一波测试:
mybatis-plus,已经帮忙写好了单表的CRUD和方法。
二、日志配置
所有的SQL都是不可见的,需要查看SQL执行顺序等等。
#日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
可以根据日期,去分析相关问题。
三、CRUD扩展
1、插入,ID会自动添加
@Test
public void test01(){
User user = new User();
user.setName("LMR");
user.setAge(4);
user.setEmail("Lmr@123.com");
int insert = userMapper.insert(user);
System.out.println(insert);
System.out.println(user.toString());
}
主键生成策略:
- UUID
- 自增ID
- 雪花算法(分布式)
- 中间件生成(redis、zookeeper)
雪花算法:
是Twitter开源的分布式ID生成算法,结果是一个Long的ID。核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5bit是数据中心,5bit是机器ID),12bit作为毫秒内的流水号(意味着每个节点在每秒可以产生4096个ID),最后一个符号位永远是0。
可以配置ID生成策略:
@TableId(type = IdType.AUTO)
private Long id;
枚举:
/*
* Copyright (c) 2011-2020, hubin (jobob@qq.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.baomidou.mybatisplus.annotation;
import lombok.Getter;
/**
* <p>
* 生成ID类型枚举类
* </p>
*
* @author hubin
* @since 2015-11-10
*/
@Getter
public enum IdType {
/**
* 数据库ID自增
*/
AUTO(0),
/**
* 该类型为未设置主键类型
*/
NONE(1),
/**
* 用户输入ID
* 该类型可以通过自己注册自动填充插件进行填充
*/
INPUT(2),
/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
/**
* 全局唯一ID (idWorker)
*/
ID_WORKER(3),
/**
* 全局唯一ID (UUID)
*/
UUID(4),
/**
* 字符串全局唯一ID (idWorker 的字符串表示)
*/
ID_WORKER_STR(5);
private int key;
IdType(int key) {
this.key = key;
}
}
2、更新操作
@Test
public void test02(){
User user = new User();
user.setId(5L);
user.setName("MG BASE GOGO!");
userMapper.updateById(user);
}
自动填充:
例如修改时间、添加时间都需要自动填充进去,不能手动更新。
- 数据库级别的修改:在表中新增字段,不太建议使用(测试中发现,就算更显操作了,但是值没有改变,则不会填充时间)
- 代码级别的修改,编写处理器进行处理。
@TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
package com.mg.handler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import javax.annotation.security.DenyAll; import java.util.Date; @Component @Slf4j public class MyMeteObjectHandler implements MetaObjectHandler { /** * 插入时候的填充策略 * @param metaObject */ @Override public void insertFill(MetaObject metaObject) { log.info("start insert fill。。。"); //字段、值、元数据 this.setFieldValByName("createTime", new Date(), metaObject); this.setFieldValByName("updateTime", new Date(), metaObject); } /** * 更新时候的填充策略 * @param metaObject */ @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("updateTime", new Date(), metaObject); } }
乐观锁/悲观锁:
- 乐观锁:总是认为不会出现问题,所以不会去上锁,如果出现了问题,就在再次更新值 version字段
- 悲观锁:认为总是会出现问题,不论干什么都会先上锁在操作
mybatis-plus乐观锁插件:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
下面看看怎么配置:
1、数据库增加字段,version,默认值是1
2、实体类增加version字段和@Version注解
@Version
private Integer version;
3、注册组件(新版本MybatisPlus 为了统一管理插件,使用了一个 统一的插件管理类,只需要注册一个Bean就行了 MybatisPlusInterceptor)
package com.mg.config;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement
@MapperScan("com.mg.mapper")
@Configuration
public class MybatisPlusConfig {
@Bean
public OptimisticLockerInterceptor optimisticLockerInnerInterceptor(){
return new OptimisticLockerInterceptor();
}
}
4、测试,这里user更新就不会成功,因为版本已经被覆盖了
@Test
public void test04(){
//线程1
User user = userMapper.selectById(1L);
user.setName("123");
user.setEmail("2292@123.com");
//线程2
User user2 = userMapper.selectById(1L);
user2.setName("123_2");
user2.setEmail("2292@123.com_2");
userMapper.updateById(user2);
userMapper.updateById(user);
}
3、查询操作
简单查询:
@Test
public void test05(){
//单个查询
User user = userMapper.selectById(1L);
System.out.println("单个查询:" + user.toString());
//批量查询
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(u ->{
System.out.println("批量:" + u.toString());
});
//条件查询 使用map
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("name", "123_2");
paramMap.put("age", 18);
List<User> users1 = userMapper.selectByMap(paramMap);
users1.forEach(u->{
System.out.println("批量:" + u.toString());
});
}
分页查询:
mybatis-plus 内置分页插件:(如果是线上的话,一般情况下回自己手动写 count()查询的sql)
1、配置拦截器组件即可
// 旧版
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
return paginationInterceptor;
}
@Test
public void test06(){
//当前页、页面大小
Page<User> page = new Page<>(1, 5);
IPage<User> userIPage = userMapper.selectPage(page, null);
userIPage.getRecords().forEach(u->{
System.out.println("批量:" + u.toString());
});
}
4、删除
物理删除:
逻辑删除:
1、增加一个deleted字段
2、增加属性
@TableLogic
private Integer deleted;
3、配置组件
//逻辑删除
@Bean
public ISqlInjector sqlInjector(){
return new LogicSqlInjector();
}
#逻辑删除
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
4、测试,生成的sql不会查询出来
@Test
public void test07(){
userMapper.deleteById(1L);
}
四、性能分析插件
主要在平时使用的时候,遇到一些慢sql,mybatis-plus也提供了这样的插件,如果超过设定时间,则停止运行。发现新版本已经没有这个功能了,可能是因为提供这样功能的组件太多了,就给去掉了。
1、导入插件
@Profile({"dev","test"})
@Bean
public PerformanceInterceptor performanceInterceptor(){
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
//最大执行时间,超过则不执行
performanceInterceptor.setMaxTime(1);
//sql格式化
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
2、测试使用,超出设置时间,就会抛出异常
五、条件构造器
Wapper 条件构造器,十分重要,一些复杂的条件,可以使用这个替代。
1、测试1
@Test
public void test01(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNotNull("name").isNotNull("email").ge("age", 12);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(u->{
System.out.println(u);
});
}
2、测试2,查询一个 selectOne
@Test
public void test02(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "Tom");
User user = userMapper.selectOne(queryWrapper);
System.out.println(user);
}
3、测试3,查询数量selectCount
@Test
public void test03(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.between("age", "1", "30");
int n = userMapper.selectCount(queryWrapper);
System.out.println(n);
}
4、测试4,模糊查询
@Test
public void test04(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.notLike("name", "e")
.likeLeft("name", "e")
.likeRight("email", "1");
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
}
5、测试5,嵌入sql
@Test
public void test05(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id", "select id from user where id<3");
List<Object> objects = userMapper.selectObjs(queryWrapper);
objects.forEach(System.out::println);
}
6、测试6,排序
@Test
public void test06(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByAsc("id");
List<Object> objects = userMapper.selectObjs(queryWrapper);
objects.forEach(System.out::println);
}
六、代码自动生成器
引入依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
生成代码:
package com.mg;
import java.util.Arrays;
import java.util.HashMap;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
/**
* 代码生成器
*/
public class MGCode {
public static void main(String[] args) {
//构建代码生成器对象
AutoGenerator mpg = new AutoGenerator();
//配置策略
//1、全局配置
GlobalConfig gc = new GlobalConfig();
//设置生成地址
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("mg");
//是否打开资源管理器
gc.setOpen(false);
//是否覆盖
gc.setFileOverride(true);
//设置接口名字
gc.setServiceName("%sService");
//主键策略
gc.setIdType(IdType.ID_WORKER);
//日期类型
gc.setDateType(DateType.ONLY_DATE);
//是否开启文档
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
//2、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/db_test?useSSL=false&useUnicode=true&characterEncoding=utf-8");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
//3、配置一些包
PackageConfig pc = new PackageConfig();
//模块名称
pc.setModuleName("wjf");
//包名
pc.setParent("com.mg");
pc.setEntity("pojo");
pc.setMapper("mapper");
pc.setService("service");
pc.setServiceImpl("service.impl");
pc.setController("controller");
mpg.setPackageInfo(pc);
//4、策略配置
StrategyConfig strategy = new StrategyConfig();
//设置需要映射的表明 tb_user
strategy.setInclude("user", "tb_user");
//规则 驼峰
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
//Lombok
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
//逻辑删除字段
strategy.setLogicDeleteFieldName("deleted");
//自动填充配置
TableFill createTime = new TableFill("create_time", FieldFill.INSERT);
TableFill updateTime = new TableFill("update_time", FieldFill.INSERT_UPDATE);
strategy.setTableFillList(Arrays.asList(createTime, updateTime));
//乐观锁
strategy.setVersionFieldName("version");
//controller 相关
strategy.setRestControllerStyle(true);
strategy.setControllerMappingHyphenStyle(true);//localhost:8080/hello_id_2
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
//执行生成
mpg.execute();
}
}
生成的代码结构:
本文章是基于B站博主,狂神说的,所以mybatis-plus版本比较老,学习新版本的,可以看官网,里面的东西写的很清楚。MyBatis-Plus。