什么是MybatisPlus MybatisPlus可以节省大量时间,所有的CRUD代码都可以自动化完成
MyBatis-Plus是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
==特性:==
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
快速入门 创建数据库mybatis_plus 创建user表 1 2 3 4 5 6 7 8 9 10 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) );
插入数据 1 2 3 4 5 6 7 8 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' );
初始化项目 快速初始化一个springboot项目
添加依赖 引用spring boot starter 父工程
1 2 3 4 5 6 <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.6.5</version > <relativePath /> </parent >
引入spring-boot-starter、spring-boot-starter-test、mybatis-plus-boot-starter、h2依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.5.1</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > </dependency > </dependencies >
连接数据库 1 2 3 4 5 6 7 spring: datasource: username: root password: 123456 url: jdbc:mysql:///mybatis_plus?userUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.cj.jdbc.Driver
在spring boot启动类中添加@MapperScan注解,扫描Mapper文件夹:
1 2 3 4 5 6 7 8 9 @SpringBootApplication @MapperScan("com.wen.mybatis_plus.mapper") public class MybatisPlusApplication { public static void main (String[] args) { SpringApplication.run(MybatisPlusApplication.class, args); } }
编码 1 2 3 4 5 6 7 8 9 import lombok.Data;@Data public class User { private Long id; private String name; private Integer age; private String email; }
1 2 3 4 5 6 7 8 9 10 import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.wen.mybatis_plus.pojo.User;import org.apache.ibatis.annotations.Mapper;@Mapper public interface UserMapper extends BaseMapper <User> { }
开始使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @SpringBootTest class MybatisPlusApplicationTests { @Autowired private UserMapper userMapper; @Test public void testSelect () { System.out.println("--------selectAll method test-------" ); List<User> userList = userMapper.selectList(null ); userList.forEach(System.out::println); }
控制台输出:
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 )
小结 以上几个步骤就已经实现User表的CRUD功能,甚至连XML文件都不用编写,以上步骤可以看出集成MyBatis-Plus非常的简单,只需要引入starter工程,并配置mapper扫描路径即可。方法都是MyBatis-Plus写好的,直接引用即可。
配置日志 所有的SQL都是不可见的,所以在后台是希望看到SQL是怎么执行的,就必须要配置日志。
1 2 3 4 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
自定义ID生成器 在用户ID上添加@TableId注解,里面的值便是使用主键自动生成的方法
1 2 3 4 5 6 7 8 9 @Data public class User { @TableId(type = IdType.ASSIGN_ID) private Long id; private String name; private Integer age; private String email; }
点击 IdType查看源码看有哪些自动生成方法
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 @Getter public enum IdType { AUTO(0 ), NONE(1 ), INPUT(2 ), ASSIGN_ID(3 ), ASSIGN_UUID(4 ); private final int key; IdType(int key) { this .key = key; } }
自动填充 方式一:数据库级别 1、在表中新增字段create_time,update_time
默认:current_timestamp
根据当前时间戳更新
2.实体类同步创建这两个属性
1 2 private Data createTime;private Data updateTime;
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void testUpdate () { User user = new User (); user.setId(5l ); user.setName("id:5,修改过后" ); user.setAge(25 ); int i = userMapper.updateById(user); System.out.println(i); }
这样更新后更新时间就会按当前时间自动更新
方式二:代码级别 默认:空
不根据当前时间戳更新
注解填充字段 @TableField(.. fill = FieldFill.INSERT)
生成器策略部分也可以配置!
1 2 3 4 5 @TableField(fill = FieldFill.INSERT) private Data createTime;@TableField(fill = FieldFill.INSERT_UPDATE) private Data updateTime;
自定义实现类 MyMetaObjectHandler 处理这个注解
这里下面有三种方法,可以都试一试,容易出bug
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill (MetaObject metaObject) { log.info("start insert fill ...." ); this .strictInsertFill(metaObject, "createTime" , LocalDateTime.class, LocalDateTime.now()); this .strictUpdateFill(metaObject, "createTime" , () -> LocalDateTime.now(), LocalDateTime.class); this .fillStrategy(metaObject, "createTime" , LocalDateTime.now()); } @Override public void updateFill (MetaObject metaObject) { log.info("start update fill ...." ); this .strictUpdateFill(metaObject, "updateTime" , LocalDateTime.class, LocalDateTime.now()); this .strictUpdateFill(metaObject, "updateTime" , () -> LocalDateTime.now(), LocalDateTime.class); this .fillStrategy(metaObject, "updateTime" , LocalDateTime.now()); } }
乐观锁 乐观锁实现方式:
取出记录时,获取当前version
更新时,带上这个version
执行更新时,set version = newVersion where version = oldVersion
如果version不对,就更新失败
==当要更新一条记录时,是希望这条记录没有被更新的==
1.数据库中添加version字段
int类型 默认为1
2.同步实体类
1 2 @Version private Integer version;
3.spring boot 的注解方式
1 2 3 4 5 6 7 8 9 10 11 12 @Configuration @MapperScan("com.wen.mybatis_plus.mapper") @EnableTransactionManagement public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor (); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor ()); return interceptor; } }
4.测试乐观锁
1 2 3 4 5 6 7 8 9 10 11 @Test void testOptimisticLocker_success () { User user = userMapper.selectById(1l ); user.setName("tian" ); user.setAge(21 ); userMapper.updateById(user); }
该操作下version从1变为2 测试成功
注意
updateById操作的这个Entity必须是从数据库select出来的才有效,不然version不更新
5.测试失败
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test void testOptimisticLocker_failure () { User user = userMapper.selectById(1l ); user.setName("tian" ); user.setAge(21 ); User user2 = userMapper.selectById(1l ); user2.setName("xiaotian" ); user2.setAge(19 ); userMapper.updateById(user2); userMapper.updateById(user); }
userMapper.updateById(user2)时version从1变为2
userMapper.updateById(user)时version2不等于oldversion1 所以更新失败,不进行操作
查询 通过id 1 2 3 4 5 6 @Test public void testSelectById () { User user = userMapper.selectById(1L ); System.out.println(user); }
批量查询 1 2 3 4 5 6 @Test public void selectBatchIds () { List<User> users = userMapper.selectBatchIds(Arrays.asList(1 , 2 , 3 )); users.forEach(System.out::println); }
参数是Collection也就是集合,这里使用的是Arrays集合
条件查询 1 2 3 4 5 6 7 8 9 10 @Test public void selectByMap () { HashMap<String,Object> map = new HashMap <>(); map.put("name" ,"小文" ); map.put("age" ,20 ); List<User> users = userMapper.selectByMap(map); users.forEach(System.out::println); }
map类的参数(字段名,参数)会被MySQLPlus自动组合成查询条件
分页查询 1、配置拦截器组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Bean public MybatisPlusInterceptor paginationInterceptor () { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor (); PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor (); pageInterceptor.setOverflow(false ); pageInterceptor.setMaxLimit(500L ); pageInterceptor.setDbType(DbType.MYSQL); interceptor.addInnerInterceptor(pageInterceptor); return interceptor; }
2、分页组件测试
1 2 3 4 5 6 7 8 @Test public void testMybatisPlus_Page () { Page<User> page = new Page <>(1 , 4 ); userMapper.selectPage(page,null ); page.getRecords().forEach(System.out::println); }
1 2 3 4 5 6 System.out.println("当前页:" + page.getCurrent()); System.out.println("总页数:" + page.getPages()); System.out.println("记录数:" + page.getTotal()); System.out.println("是否有上一页:" + page.hasPrevious()); System.out.println("是否有下一页:" + page.hasNext());
逻辑删除 普通删除和查询一致
1、在数据表中增加一个deleted字段
默认为0,表示没有被删除
2、同步实体类,在实体类上加上@TableLogic 注解
1 2 @TableLogic private Integer deleted;
3、配置application.yml文件
1 2 3 4 5 6 7 mybatis-plus: global-config: db-config: logic-delete-field: flag logic-delete-value: 1 logic-not-delete-value: 0
4.测试
1 2 3 4 5 @Test public void testDeleteById () { userMapper.deleteById(4L ); }
查看日志输出可以看到,delete的语句已经发生了更改
实质上就是update(修改)语句,将deleted字段从1修改为0
5、对Id为4的用户进行查询
查看日志输出可以看到,seletc的语句以经发生了更改
增加了deleted的判断语句,判断deleted是否为1,为1则能搜索,0则不能
小结 增加了逻辑删除后,删除改为更新,查询也会判断是否被逻辑删除
条件构造器 1、查询name、邮箱不为空且年龄大于等于20的用户
1 2 3 4 5 6 7 8 9 10 @Test void WrapperTest () { QueryWrapper<User> wrapper = new QueryWrapper <>(); wrapper .isNotNull("name" ) .isNotNull("email" ) .ge("age" ,12 ); userMapper.selectList(wrapper).forEach(System.out::println); }
2、查询姓名为小文的用户
1 2 3 4 5 6 7 8 @Test void WrapperTest2 () { QueryWrapper<User> wrapper = new QueryWrapper <>(); wrapper.eq("name" ,"小文" ); User user = userMapper.selectOne(wrapper); System.out.println(user); }
3、查询年龄在19-23之间的用户
1 2 3 4 5 6 7 8 @Test void WrapperTest3 () { QueryWrapper<User> wrapper = new QueryWrapper <>(); wrapper.between("age" , 19 , 23 ); Long count = userMapper.selectCount(wrapper); System.out.println(count); }
4、模糊查询
1 2 3 4 5 6 7 8 9 @Test void WrapperTest4 () { QueryWrapper<User> wrapper = new QueryWrapper <>(); wrapper .notLike("name" ,"a" ) .likeRight("email" ,"t" ); List<Map<String, Object>> maps = userMapper.selectMaps(wrapper); maps.forEach(System.out::println); }
5、联表查询
1 2 3 4 5 6 7 8 @Test void WrapperTest5 () { QueryWrapper<User> wrapper = new QueryWrapper <>(); wrapper.inSql("id" ,"select id from user where id < 4" ); List<Object> objects = userMapper.selectObjs(wrapper); objects.forEach(System.out::println); }
6、通过ID进行排序
1 2 3 4 5 6 7 8 @Test void WrapperTest6 () { QueryWrapper<User> wrapper = new QueryWrapper <>(); wrapper.orderByAsc("id" ); List<User> users = userMapper.selectList(wrapper); users.forEach(System.out::println); }
评论区
欢迎你留下宝贵的意见,昵称输入QQ号会显示QQ头像哦~