前言:
根据狂神视频整理
为什么要学它,因为mybatisplus可以节省我们大量的工作时间,所有的CRUD代码他都可以自动完成,所以才说是懒人后端的福音😁
这个是中国人开发的,所以官方写得比较好理解,官方配置文档地址
点点我
1. 简介
MyBatis-Plus (简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
2. 快速入门
对应的数据库脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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) );DELETE FROM user ;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' );
在pom里导入mp(这里是比较早的版本,推荐使用较新的版本
1 2 3 4 5 <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.0.5</version > </dependency >
不要同时导入mybatis 和mp,可能会引起版本冲突
数据库配置
1 2 3 4 spring.datasource.username =root spring.datasource.password =root spring.datasource.url =jdbc:mysql://localhost:3306/dbtest?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8 spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver
这里的时区设置的是东八区
写实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import com.baomidou.mybatisplus.annotation.*;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.util.Date;@Data @AllArgsConstructor @NoArgsConstructor public class User { private Long id; private String name; private Integer age; private String email; }
编写mapper
1 2 3 4 5 @Mapper public interface UserMapper extends BaseMapper <User > { }
在springboot的启动类中添加扫描注解
1 2 3 4 5 6 7 8 9 @SpringBootApplication @MapperScan("com.mercury.mybatisplusdemo1.mapper") public class Application { public static void main (String[] args) { SpringApplication.run(Application.class, args); } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @SpringBootTest class MybatisPlusDemo1ApplicationTests { @Autowired private UserMapper userMapper; @Test void contextLoads () { List<User> users = userMapper.selectList(null ); users.forEach(System.out::println); } }
输出
1 2 3 4 5 User(id=1 , name=Jone, age=18 , email=test1@baomidou .com) User(id=2 , name=Jack, age=20 , email=test2@baomidou .com) User(id=3 , name=Tom, age=28 , email=test3@baomidou .com) User(id=4 , name=Sandy, age=21 , email=test4@baomidou .com) User(id=5 , name=Billie, age=24 , email=test5@baomidou .com)
这里配置一下日志信息方便查看
1 2 #配置日志 mybatis-plus.configuration .log-impl=org.apache .ibatis .logging .stdout .StdOutImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4b65d9f4] was not registered for synchronization because synchronization is not active JDBC Connection [HikariProxyConnection@1702089463 wrapping com.mysql.cj.jdbc.ConnectionImpl@4052c8c2] will not be managed by Spring ==> Preparing: SELECT id,name,age,email FROM user ==> Parameters: <== Columns: id, name, age, email <== Row: 1 , Jone, 18 , test1@baomidou .com <== Row: 2 , Jack, 20 , test2@baomidou .com <== Row: 3 , Tom, 28 , test3@baomidou .com <== Row: 4 , Sandy, 21 , test4@baomidou .com <== Row: 5 , Billie, 24 , test5@baomidou .com <== Total: 5 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4b65d9f4] User(id=1 , name=Jone, age=18 , email=test1@baomidou .com) User(id=2 , name=Jack, age=20 , email=test2@baomidou .com) User(id=3 , name=Tom, age=28 , email=test3@baomidou .com) User(id=4 , name=Sandy, age=21 , email=test4@baomidou .com) User(id=5 , name=Billie, age=24 , email=test5@baomidou .com)2022 -05 -19 17 :19 :55.314 INFO 23644 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...2022 -05 -19 17 :19 :55.324 INFO 23644 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
3. 主键生成策略
1 2 3 4 5 6 7 8 9 10 11 @Test public void insert () { User user = new User(); user.setName("mercury02" ); user.setAge(3 ); user.setEmail("william@163.com" ); int insert = userMapper.insert(user); System.out.println(insert); System.out.println(user); }
插入结果
1 2 3 ==> Preparing: INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? ) ==> Parameters: 1527226339787800577 (Long), mercury(String), 3 (Integer), chenning_william@163. com(String) <== Updates: 1
这里默认 使用的是分布式系统唯一id生成 雪花算法(SnowFlake)
雪花算法的由来:
Twitter使用scala语言开源了一种分布式 id 生成算法——SnowFlake算法,被翻译成了雪花算法。
因为自然界中并不存在两片完全一样的雪花的,每一片雪花都拥有自己漂亮独特的形状、独一无二。雪花算法也表示生成的ID如雪花般独一无二。
3.1 主键自增
配置主键自增
实体类的字段上
1 2 3 4 5 6 7 8 public class User { @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; private String email; }
同时数据库里面也要设置为主键自增
can can 源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public enum IdType { AUTO(0 ), NONE(1 ), INPUT(2 ), ID_WORKER(3 ), UUID(4 ), ID_WORKER_STR(5 ); private int key; private IdType (int key) { this .key = key; } public int getKey () { return this .key; } }
4. 更新操作
1 2 3 4 5 6 7 @Test public void update () { User user = new User(); user.setId(5l ); user.setName("william" ); userMapper.updateById(user); }
1 2 3 ==> Preparing: UPDATE user SET name=? WHERE id=? ==> Parameters: william(String), 5 (Long) <== Updates: 1
5. 自动填充
创建时间,修改时间,这些操作都是自动化完成的
在国际标准中所有的数据库表gmt_create gmt_modified几乎所有的表都要配置上,而且需要自动化
5.1 数据库级别的修改
增加create_time update_time字段
可以看到默认的时间
5.2 代码级别的修改
首先还原数据库的设置,将默认CURRENT_TIMESTAMP和更新取消掉
实体类字段属性上添加注解
1 2 3 4 5 @TableField(fill = FieldFill.INSERT) private Date createTime;@TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
编写handler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Slf4j @Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill (MetaObject metaObject) { log.info("start insert fill ..." ); this .setFieldValByName("createTime" ,new Date() ,metaObject); this .setFieldValByName("updateTime" ,new Date(),metaObject); } @Override public void updateFill (MetaObject metaObject) { log.info("start update fill ..." ); this .setFieldValByName("updateTime" ,new Date(),metaObject); } }
6. 乐观锁配置
乐观锁:总认为不会出现问题,无论干什么都不会去上锁,如果出现了问题,再次更新值测试
悲观锁:它总认为总是出现问题,无论干什么都会去上锁,然后去操作
什么时候需要乐观锁呢?当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
取出记录时,获取当前version
更新时,带上这个version
执行更新时, set version = newVersion where version = oldVersion(整数类型下 newVersion = oldVersion + 1)
如果version不对,就更新失败
数据库里增加version字段 (默认初版为1)
实体类中增加
1 2 @Version private Integer version ;
注册组件
写个config来统一配置,新版的注册组件有所不同,详情看官方的配置文档
1 2 3 4 5 6 7 8 9 10 11 @MapperScan("com.mercury.mybatisplusdemo1.mapper") @EnableTransactionManagement @Configuration public class MybatisPlusConfig { @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor () { return new OptimisticLockerInterceptor(); } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Test public void testOptimisticLocker () { User user1 = userMapper.selectById(1l ); user1.setName("mercury" ); user1.setEmail("mercury@163.com" ); User user2 = userMapper.selectById(1l ); user2.setName("william" ); user2.setEmail("william@163.com" ); userMapper.updateById(user2); userMapper.updateById(user1); }
7. 查询操作
7.1 单个和多个查询
1 2 3 4 5 6 7 8 9 10 @Test public void testSelectById () { User user = userMapper.selectById(1l ); System.out.println(user); List<User> users = userMapper.selectBatchIds(Arrays.asList(1 , 2 , 3 )); users.forEach(System.out::println); }
7.2 条件查询
使用map实现
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void testSelectBycondition () { HashMap<String, Object> Map = new HashMap<>(); Map.put("name" ,"william" ); Map.put("age" ,18 ); List<User> users = userMapper.selectByMap(Map); users.forEach(System.out::println); }
1 2 3 4 5 ==> Preparing: SELECT id,name,age,email,version,deleted,create_time,update_time FROM user WHERE name = ? AND age = ? ==> Parameters: william(String), 18 (Integer) <== Columns: id, name, age, email, version, deleted, create_time, update_time <== Row: 5 , william, 18 , test5@baomidou .com, 1 , 0 , 2022 -05 -21 13 :15 :42 , 2022 -05 -22 13 :00 :53 <== Total: 1
7.3 分页查询
分页查询的几种实现方式
原始的limit进行分页
pageHelper第三方插件
MP分页
配置分页查询的组件(配置在config里)
1 2 3 4 5 6 7 8 9 10 11 12 @Bean public PaginationInterceptor paginationInterceptor () { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); return paginationInterceptor; }
然后直接使用page对象即可
1 2 3 4 5 6 7 8 9 10 @Test public void testpage () { Page<User> page = new Page<>(2 ,5 ); userMapper.selectPage(page,null ); page.getRecords().forEach(System.out::println); System.out.println(page.getTotal()); }
1 2 3 4 5 6 7 8 9 10 ==> Preparing: SELECT COUNT (1 ) FROM user ==> Parameters: <== Columns: COUNT(1 ) <== Row: 7 ==> Preparing: SELECT id,name,age,email,version,create_time,update_time FROM user WHERE LIMIT 5 ,5 ==> Parameters: <== Columns: id, name, age, email, version, create_time, update_time <== Row: 1527903812435374084 , mercury03, 3 , william@163. com, 1 , 2022 -05 -22 13 :27 :46 , 2022 -05 -22 13 :27 :46 <== Row: 1527903812435374085 , mercury04, 3 , mercury@163. com, 1 , 2022 -05 -22 13 :28 :11 , 2022 -05 -22 13 :28 :11 <== Total: 2
8. 删除操作
8.1 单个和批量以及条件删除
1 2 3 4 5 6 7 8 @Test public void testdelete () { userMapper.deleteById(1l ); }
8.2 逻辑删除
数据库增加deleted字段
实体类中增加注解
1 2 @TableLogic private Integer deleted;
增加配置
1 2 3 4 5 @Bean public ISqlInjector sqlInjector () { return new LogicSqlInjector(); }
application配置
1 2 3 4 mybatis-plus.global-config.db-config.logic-delete-value = 1 mybatis-plus.global-config.db-config.logic-not-delete-value = 0
测试,运行删除id为1的数据
1 2 3 ==> Preparing: UPDATE user SET deleted=1 WHERE id=? AND deleted=0 ==> Parameters: 1 (Long) <== Updates: 1
那既然没有实际的删除,那么查询的时候可以查询出来吗?
1 2 3 4 ==> Preparing: SELECT id,name,age,email,version,deleted,create_time,update_time FROM user WHERE id=? AND deleted=0 ==> Parameters: 1 (Long) <== Total: 0
9. 性能分析插件
我们在平时的开发中会遇到一些慢sql ,MP也提供性能分析插件,如果超过这个时间就会停止,狂神视频所讲的插件已经移除,官网现在推荐使用p6spy,具体的配置参考官方文档
10. 条件构造器
前面的所讲的都是一些简单查询,那么我们想实现相对较复杂的查询的时候那么该怎么操作呢,这个时候我们就可以使用mp提供的条件构造器了
下面六个示例基本够用了,如果不够,依然那句话,去查看官方文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 @Test void contextLoadstest1 () { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper .isNotNull("name" ) .isNotNull("email" ) .ge("age" ,12 ); userMapper.selectList(wrapper).forEach(System.out::println); }@Test void contextLoadstest2 () { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("name" ,"william" ); System.out.println(userMapper.selectOne(wrapper)); }@Test void contextLoadstest3 () { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.between("age" ,20 ,30 ); System.out.println(userMapper.selectCount(wrapper)); }@Test void contextLoadstest4 () { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.notLike("name" ,"e" ) .likeRight("email" ,"t" ); List<Map<String, Object>> maps = userMapper.selectMaps(wrapper); maps.forEach(System.out::println); }@Test void contextLoadstest5 () { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.inSql("id" ,"select id from user where id <3" ); List<Object> objects = userMapper.selectObjs(wrapper); objects.forEach(System.out::println); }@Test void contextLoadstest6 () { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.orderByDesc("id" ); List<User> users = userMapper.selectList(wrapper); users.forEach(System.out::println); }
11. 代码生成器
这里是旧版的生成器,3.5.1+版本的生成器和旧版的生成器有很大的不同,并且不兼容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 public class CodeGenerator { public static void main (String[] args) { AutoGenerator mpg = new AutoGenerator(); GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir" ); gc.setOutputDir(projectPath+"/src/main/java" ); gc.setAuthor("mercury" ); gc.setOpen(false ); gc.setFileOverride(false ); gc.setServiceName("%sService" ); gc.setIdType(IdType.ID_WORKER); gc.setDateType(DateType.ONLY_DATE); gc.setSwagger2(true ); mpg.setGlobalConfig(gc); DataSourceConfig dsc = new DataSourceConfig(); dsc.setUsername("root" ); dsc.setPassword("root" ); dsc.setUrl("jdbc:mysql://localhost:3306/db?useSSL=false&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8" ); dsc.setDriverName("com.mysql.cj.jdbc.Driver" ); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc); PackageConfig pc = new PackageConfig(); pc.setModuleName("study" ); pc.setParent("com.wsk" ); pc.setEntity("pojo" ); pc.setMapper("mapper" ); pc.setService("service" ); pc.setController("controller" ); mpg.setPackageInfo(pc); StrategyConfig strategy = new StrategyConfig(); strategy.setInclude("admin" ,"danyuan" ,"building" ,"room" ); strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setEntityLombokModel(true ); strategy.setLogicDeleteFieldName("deleted" ); TableFill gmtCreate = new TableFill("gmt_create" , FieldFill.INSERT); TableFill gmtUpdate = new TableFill("gmt_update" , FieldFill.INSERT_UPDATE); ArrayList<TableFill> tableFills = new ArrayList<>(); tableFills.add(gmtCreate); tableFills.add(gmtUpdate); strategy.setTableFillList(tableFills); strategy.setVersionFieldName("version" ); strategy.setRestControllerStyle(true ); strategy.setControllerMappingHyphenStyle(true ); mpg.setStrategy(strategy); mpg.execute(); } }