目录
Mybatis-Plus
jpa tk-mapper mybatis-plus
1、概述
1.1、简介
MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
1.2、特性
- 参考官网文档,地址:https://www.mybatis-plus.com/
- 狂神说B站视频教程地址:https://www.bilibili.com/video/BV17E411N7KN
2、快速体验
2.1、搭建数据库
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
`name` VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
INSERT INTO `user` (id, `name`, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');
2.2、创建SpringBoot项目
-
添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--mysql数据库驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!--mybatis-plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-extension</artifactId> <version>3.0.5</version> </dependency> <!--代码生成器依赖--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version> </dependency>
高版本的驱动兼容低版本的数据库,8版本的驱动需要增加时区的配置
原来
pojo–dao(mybatis配置文件、Mapper.xml配置文件)–service–controller
现在
pojo–dao(只需要继承指定泛型的接口:BansMapper)–service–controller
代码
User.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
//数据库主键id自动生成:uuid,自增,雪花算法,redis,zookeeper
@TableId(type = IdType.ID_WORKER)
private Long id;
private String name;
private Integer age;
private String email;
}
UserMapper.java
@Mapper
@Repository
public interface UserMapper extends BaseMapper<User> {
}
application.yml
spring:
datasource:
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/demo?useUniocde=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT%2B8
#开启mybatis-plus控制台日志输出
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
测试
@SpringBootTest
class MybatisPlusTestApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
//测试Mybatis-Plus环境是否搭建成功
List<User> userList = userMapper.selectList(null);
for (User user : userList) {
System.out.println(user.toString());
}
}
}
3、逐步击破
3.1、插入操作
主键id
-
主键生成策略:uuid,自增,雪花算法,redis,zookeeper
-
mybatis-plus所带的主键生成策略:
@TableId
@TableId(type = IdType.ID_WORKER) private Long id;
-
分析
public enum IdType { AUTO(0),//数据库id自增 NONE(1),//该类型为未设置主键类型 INPUT(2),//手动输入,该类型可以通过自己注册自动填充插件进行填充 //以下3种类型、只有当插入对象ID 为空,才自动填充 ID_WORKER(3),//雪花算法 UUID(4),//uuid ID_WORKER_STR(5);//字符串全局唯一id private int key; private IdType(int key) { this.key = key; } public int getKey() { return this.key; } }
3.2、修改操作
动态sql
@Test
void testUpdate(){
User user = new User();
user.setId(5L);
user.setName("麻腾飞");
user.setAge(18);
int i = userMapper.updateById(user);
}
时间自动填充
-
数据库级别
@Test void testUpdate(){ User user = new User(); user.setId(5L); user.setName("飞哥"); user.setAge(9); int i = userMapper.updateById(user); }
-
注解级别(Mybatis-plus自带注解)
//User.java @TableField(fill = FieldFill.INSERT)//添加数据时,自动给createTime字段添加当前系统时间 private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE)//添加或修改数据时,自动给editTime字段添加当前系统时间 private Date editTime;
//编写处理器来为注解自定义填充策略 @Slf4j @Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { log.info("start fill insert..."); this.setFieldValByName("createTime",new Date(),metaObject); this.setFieldValByName("editTime",new Date(),metaObject); } @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("editTime",new Date(),metaObject); } }
3.3、乐观锁
什么是乐观锁、悲观锁?自旋锁尝试多次提交
-
乐观锁:当要更新一条记录时,希望这条记录没有被别人更新
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
测试Mybatis-plus乐观锁插件
-
乐观锁执行成功测试
//实体类加上@Version注解 @Version private Integer version;
//编写配置类注册乐观锁插件 @Configuration @EnableTransactionManagement public class MyConfig { @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } //注册乐观锁插件,3.5.x版本的 // @Bean // public MybatisPlusInterceptor mybatisPlusInterceptor() { // MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // return interceptor; // } }
//测试乐观锁成功 @Test void testOptimisticSuccess(){ //先根据id获取记录,获取当前的version字段的值 User user = userMapper.selectById(3L); user.setName("飞哥"); int i = userMapper.updateById(user); }
-
乐观锁执行成功测试01
@Test void testOptimisticSuccess(){ //先根据id获取记录,获取当前的version字段的值 User user = userMapper.selectById(2L); //模拟插队线程 user.setAge(10); userMapper.updateById(user); user.setName("飞哥"); userMapper.updateById(user); }
总结:使用乐观锁插件更新记录,只需要查询一次记录即可
-
乐观锁执行失败
//测试乐观锁失败 @Test void testOptimisticError(){ //先根据id获取记录,获取当前的version字段的值 User user = userMapper.selectById(1L); user.setName("飞哥"); //模拟插队线程 User user1 = userMapper.selectById(1L); user1.setAge(10); userMapper.updateById(user1); userMapper.updateById(user); }
3.4、查询操作
//测试常用查询操作
@Test
void test01(){
//查询所有数据
List<User> userList1 = userMapper.selectList(null);
//根据唯一id查询
User user1 = userMapper.selectById(1L);
//通过多个id查询多条记录
List<User> userList2 = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
//通过Map集合查询记录
Map<String, Object> map = new HashMap<>();
map.put("name","Jone");
List<User> userList3 = userMapper.selectByMap(map);
for (User user : userList3) {
System.out.println(user.toString());
}
}
3.5、分页查询
-
原始的limit
-
分页插件:pageHelper
-
Mybatis-plus自带的分页插件
//编写配置类配置分页插件 @Configuration @EnableTransactionManagement @MapperScan("com.mtf.mapper") public class MyConfig { // 旧版 @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false // paginationInterceptor.setOverflow(false); // 设置最大单页限制数量,默认 500 条,-1 不受限制 // paginationInterceptor.setLimit(500); // 开启 count 的 join 优化,只针对部分 left join // paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true)); return paginationInterceptor; } // 最新版 // @Bean // public MybatisPlusInterceptor mybatisPlusInterceptor() { // MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2)); // return interceptor; // } }
//测试分页插件 @Test void testPage(){ Page<User> userPage = new Page<>(); userPage.setCurrent(1); userPage.setSize(2); userMapper.selectPage(userPage, null); for (User record : userPage.getRecords()) { System.out.println(record.toString()); } }
3.6、删除操作
物理删除
- 物理删除就是普通的删除操作和之前的查询操作类似,把数据从数据库中移除
逻辑删除
-
逻辑删除数据并不是把数据从数据库中移除,而是通过改变变量的值让该行数据失效
#开启mybatis-plus控制台日志输出 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-value: 0 #逻辑已删除设置为0 logic-not-delete-value: 1 #逻辑未删除设置为1
@Configuration @EnableTransactionManagement @MapperScan("com.mtf.mapper") public class MyConfig { //注册逻辑删除实例 @Bean public ISqlInjector sqlInjector(){ return new LogicSqlInjector(); } }
//删除操作 @Test void testDelete(){ userMapper.deleteById(5L); }
3.7、性能分析插件
用来检测项目开发过程中存在的慢sql,MP的性能分析插件能够设置sql的执行时间,如果一些sql执行时间超过设定值就会停止运行!
#设置开发环境
spring:
profiles:
active:test
@Configuration
@EnableTransactionManagement
@MapperScan("com.mtf.mapper")
public class MyConfig {
//注册性能分析插件
@Bean
@Profile({"dev","test"}) //设置dev 和 test 开发环境开启
public PerformanceInterceptor performanceInterceptor(){
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(1);//单位毫秒
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
}
3.8、条件查询器Wrapper
@SpringBootTest
public class WrapperTest {
@Autowired
private UserMapper userMapper;
// 查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于12
@Test
void test01(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.isNotNull("name")
.isNotNull("email")
.ge("age",12);
List<User> userList = userMapper.selectList(wrapper);
for (User user : userList) {
System.out.println(user.toString());
}
}
// 查询名字Jone
@Test
void test02(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name","Jone");
//返回一个结果调用selectOne,返回多个结果调用selectList
User user = userMapper.selectOne(wrapper);
System.out.println(user.toString());
}
// 查询年龄在 20 ~ 30 岁之间的用户
@Test
void test03(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.ge("age",10)
.le("age",22);
//wrapper.between("age",10,22);
List<User> userList = userMapper.selectList(wrapper);
for (User user : userList) {
System.out.println(user.toString());
}
}
//其他功能参考官方文档
}
3.9、代码自动生成器(AutoGenerator )
通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
package com.mtf;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
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 java.util.ArrayList;
/**
* @BelongsProject: Mybatis_Plus_Test
* @BelongsPackage: com.mtf
* @Author: Matengfei
* @CreateTime: 2022-08-10 20:41
* @Description: TODO
* @Version: 1.0
*/
public class CodeGenerator {
public static void main(String[] args) {
// 需要构建一个 代码自动生成器 对象
AutoGenerator mpg = new AutoGenerator();
//1、全局配置
GlobalConfig globalConfig = new GlobalConfig();
//指定代码生成的物理位置
globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
//设置作者
globalConfig.setAuthor("Matengfei");
globalConfig.setOpen(false);
// 是否覆盖
globalConfig.setFileOverride(false);
//去掉Service名称前面的I
globalConfig.setServiceName("%sService");
globalConfig.setIdType(IdType.ID_WORKER);
globalConfig.setDateType(DateType.ONLY_DATE);
globalConfig.setSwagger2(true);
mpg.setGlobalConfig(globalConfig);
//2、数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/demo?useUnicode=true&useSSL=false&characterEncoding=utf8");
dataSourceConfig.setDriverName("com.mysql.jdbc.Driver");
dataSourceConfig.setUsername("root");
dataSourceConfig.setPassword("123456");
//3、包的配置
PackageConfig pc = new PackageConfig();
//只需要改实体类名字 和包名 还有 数据库配置即可
pc.setModuleName("blog"); pc.setParent("com.kuang");
pc.setEntity("entity"); pc.setMapper("mapper");
pc.setService("service"); pc.setController("controller");
mpg.setPackageInfo(pc);
//4、策略配置
StrategyConfig strategy = new StrategyConfig();
//表名
strategy.setInclude("blog_tags","course","links","sys_settings","user_record"," user_say");
// 设置要映射的表名
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
// 自动lombok
strategy.setLogicDeleteFieldName("deleted");
// 自动填充配置
TableFill gmtCreate = new TableFill("create_time", FieldFill.INSERT);
TableFill gmtModified = new TableFill("edit_time", FieldFill.INSERT_UPDATE);
ArrayList<TableFill> tableFills = new ArrayList<>();
tableFills.add(gmtCreate); tableFills.add(gmtModified);
strategy.setTableFillList(tableFills);
// 乐观锁
strategy.setVersionFieldName("version");
strategy.setRestControllerStyle(true);
strategy.setControllerMappingHyphenStyle(true);
// localhost:8080/hello_id_2
mpg.setStrategy(strategy);
mpg.execute(); //执行
}
}