一、MyBatisPlus

  • MyBatisPlus 能大量的节省我们些CRUD的时间,所有的CRUD都能通过MyBatisPlus来自动完成
  • MyBatis Plus 简称 MP 是一个MyBatis的增强工具包,制作增强不做改变,为简化开发,提高生产率而生
  • MyBatis 官网
  • MyBatis文档

    二、快速入门

  1. 初始化工程
  2. 创建一个springboot项目,选上lombok,spring web
  3. 导入mybatis plus 依赖

    1. <dependency>
    2. <groupId>com.baomidou</groupId>
    3. <artifactId>mybatis-plus-boot-starter</artifactId>
    4. <version>3.0.5</version>
    5. </dependency>
    6. <dependency>
    7. <groupId>mysql</groupId>
    8. <artifactId>mysql-connector-java</artifactId>
    9. </dependency>
  4. 创建一个表,随便创建一个用户表

  5. 编写 application.properties 配置文件

    1. # MySQL 5 配置
    2. spring.datasource.username=root
    3. spring.datasource.password=0000
    4. spring.datasource.url=jdbc:mysql://localhost:3306/mp
    5. spring.datasource.driver-class-name=com.mysql.jdbc.Driver

    1.png

  6. 使用MyBatis之后

    • 创建实体类 User
    • 创建UserMapper接口 继承 BaseMapper 【这个Mapper就是Dao】
    • 在UserMapper接口上添加@Repository 注解
    • 在Application主方法类 上添加
      • //扫描Mapper文件
      • @MapperScan(“com.yixuexi._02_spring_boot_mybatisplus.dao”)
    • 使用

2.png

1. 细节

  • MyBatis和MyBatis-spring 依赖就不要加入到项目中了。MyBatisPlus自动维护
  • 在真实开发中,表的字段一般都会有 version(乐观锁) deleted(逻辑删除) gmt_create(创建时间),gmt_modified(修改时间)

    2. 坑:

  • springboot的MySQL start 的mysql依赖版本是 8.0 版本,和电脑上的不匹配的话会报错,所以最好别选mysqlStart,自己添加mysql5.0的依赖

  • 需要在主启动类上去扫描我们的mapper包下所有的接口
    • @MapperScan(“com.yixuexi._02_spring_boot_mybatisplus.dao”)
  • 如果是MySQL8.0的话,那么spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

    3. 配置日志

    因为现在所有的SQL是不可见的,我们希望通过日志的方式将SQL输出出来
    # 配置日志
    mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    3.png

三、insert update

1. insert 插入

  1. @Test
  2. void insertPlus(){
  3. User user = new User();
  4. user.setId(7);
  5. user.setAge(18);
  6. user.setName("仝子瑜");
  7. user.setEmail("2298320493@qq.com");
  8. // 如果不设置id的话,会帮我们自动生成id值 这个id值很大很大
  9. int insert = userMapper.insert(user);
  10. // 输出受影响的行数
  11. System.out.println(insert);
  12. }

为什么不设置id,自动插入的值会是很大很大的呢? 123561315643213216541

  1. 在实体类主键字段上添加 @TableId(type = IdType.AUTO)
  2. 数据库字段一定要是自增的 auto_increment
  3. 上面设置了后,插入的User对象中,就算id属性赋值了,也会按照它的自增
  4. 其余的IdType源码解释

未命名图片.png
一旦手动输入[ IdType.INPUT ]之后 就需要自己写ID,如果不写就是NULL【其实这个也可以用来自增,因为实体类是null,到数据库里面会根据数据库的值进行自增】

3.update更新

  1. @Test
  2. void updatePlus(){
  3. User user = new User();
  4. user.setId(1);
  5. user.setEmail("2298320493@qq.com");
  6. user.setName("zhangsan");
  7. user.setAge(15);
  8. // 注意!这里传入的值是一个User对象,通过这个user对象的id进行更新
  9. userMapper.updateById(user);
  10. }

update的SQL语句都是MyBatisPlus自己的动态SQL ,通过对属性的ifnull 进行判断。

4. 自动填充

  • 创建时间,修改时间!这些操作一般都是自动化完成的,我们不希望手动更新!
  • 阿里巴巴开发手册:所有的数据库表:gmt_create【create_time】 ,gmt_modified 【update_time】几乎所有的表都要配置上,而且需要自动化!

    方式一:数据库级别 (工作中不建议使用)

  1. 在创建表时添加两个字段 create_time update_time
  2. 字段类型 datetime
  3. 给字段加上默认约束,获得当前时间,和更新时更新

    方式二:代码级别

  4. 删除数据库字段的默认值

  5. 实体类的字段属性上需要增加注解 ```java /**
    • 字段插入时,自动填充 / @TableField(fill = FieldFill.INSERT) private Date createTime; /*
    • 字段更新时,自动填充 */ @TableField(fill = FieldFill.UPDATE) private Date updateTime;
  1. 3. 编写处理器 处理注解【人家已经写好了,我们稍微改改】
  2. - 官网地址: [https://baomidou.com/guide/auto-fill-metainfo.html](https://baomidou.com/guide/auto-fill-metainfo.html)
  3. - 一定要记得在处理器上面添加@Component注解,将组件添加到容器中
  4. ```java
  5. /**
  6. * @date: 2021/1/5 0:02
  7. * @author: 易学习
  8. * @Component: 一定不要忘记把组件添加到容器中
  9. */
  10. @Component
  11. public class MyDateObjectHandler implements MetaObjectHandler {
  12. /**
  13. * 插入时的填充策略
  14. * @param metaObject
  15. */
  16. @Override
  17. public void insertFill(MetaObject metaObject) {
  18. /**
  19. * setFieldValByName:填充你要指定的值
  20. * 第一个参数:要填充的字段名
  21. * 第二个参数:要填充的值
  22. * 第三个参数:metaObject
  23. * 这里要写两个 把更新的字段也写上
  24. */
  25. this.setFieldValByName("createTime",new Date(),metaObject);
  26. this.setFieldValByName("updateTime",new Date(),metaObject);
  27. }
  28. /**
  29. * 更新时的填充策略
  30. * @param metaObject
  31. */
  32. @Override
  33. public void updateFill(MetaObject metaObject) {
  34. // 和上面一样,只是不需要设置 createTime了
  35. this.setFieldValByName("updateTime",new Date(),metaObject);
  36. }
  37. }

四、乐观锁 & 悲观锁

乐观锁:顾名思义十分乐观,他总是认为不会出现问题,无论干什么都不会去上锁!如果出现了问题就再次更新值测试。
悲观锁:顾名思义十分悲观,他总是认为总是出现问题,无论干什么都会去上锁!再去上锁。

乐观锁实现方式:

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, SQL: set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

乐观锁:现查询,获得版本号 version = 1

— A 线程

update user set name = ‘仝子瑜’,version = version + 1 where id = 22 and version = 1

—- B 线程,抢先修改,这个时候 version = 2 导致A 修改失败

update user set name = ‘张三’,version = version + 1 where id = 22 and version = 1

1. 测试MP中的乐观锁

  1. 给数据库中增加version 字段 int 类型 默认值为1
  2. 实体类添加对应的字段,并且加上@version注解,表明这是乐观锁
  3. 注册组件(最新版也需要注册,3.0.5需要注册组件) ```java /**
  • @date: 2021/1/5 19:01
  • @author: 易学习
  • @Configuration: 表明这个是一个配置类,这样的话,之前注册的处理器的扫描 @MapperScan()也可以放到这里
  • @MapperScan: 扫描处理器,这里扫描的是之前那个 createTime 和 updateTime
  • @EnableTransactionManagement: 事务控制 */

@MapperScan(“com.yixuexi.mybatisplus.mapper”) @EnableTransactionManagement @Configuration public class MyBatisPlusConfig {

  1. /**
  2. * 注册乐观锁插件
  3. */
  4. @Bean
  5. public OptimisticLockerInterceptor optimisticLockerInterceptor(){
  6. return new OptimisticLockerInterceptor();
  7. }

}

  1. 4. 测试
  2. ```java
  3. /**
  4. * 测试乐观锁,成功案例
  5. */
  6. @Test
  7. void happyTest(){
  8. // 1.查询用户信息
  9. User user1 = userMapper.selectById(1);
  10. // 2.修改用户信息
  11. user1.setName("马保国");
  12. user1.setAge(69);
  13. user1.setEmail("mabaoguo@163.com");
  14. // 3.执行更新操作
  15. userMapper.updateById(user1);
  16. }
  1. /**
  2. * 测试乐观锁,失败案例
  3. */
  4. @Test
  5. void happyTest2(){
  6. // 线程1
  7. User user1 = userMapper.selectById(1);
  8. user1.setName("马保国");
  9. user1.setAge(69);
  10. user1.setEmail("mabaoguo@163.com");
  11. // 线程2 模拟另外一个线程执行了插队操作
  12. User user2 = userMapper.selectById(1);
  13. user2.setName("旭旭宝宝");
  14. user2.setAge(35);
  15. // 线程2 抢先更新
  16. userMapper.updateById(user2);
  17. // 线程1执行更新操作
  18. // 如果没有乐观锁,就会覆盖上面的旭旭宝宝,成为马保国
  19. // 因为设置了乐观锁 没有覆盖 马保国没有被进行更新,因为where的version=? 不成立
  20. userMapper.updateById(user1);
  21. }

五、查询

1. 查询

  1. /**
  2. * * selectById(id)测试根据id查询一个
  3. */
  4. @Test
  5. void testSelectById(){
  6. User user = userMapper.selectById(1);
  7. System.out.println(user);
  8. }
  9. /**
  10. * selectBatchIds(集合) 测试查询批量
  11. */
  12. @Test
  13. void testSelectBatchIds(){
  14. List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
  15. System.out.println(users);
  16. }
  17. /**
  18. * 条件查询:
  19. */
  20. @Test
  21. void testSelectByMap(){
  22. HashMap<String,Object> userHashMap = new HashMap<>();
  23. //自定义要查询的条件
  24. userHashMap.put("name","张五");
  25. List<User> users = userMapper.selectByMap(userHashMap);
  26. System.out.println(users);
  27. }

2. 分页查询

mp中也集成了分页插件
官网: https://baomidou.com/guide/interceptor.html#%E4%BD%BF%E7%94%A8%E6%96%B9%E5%BC%8F-%E4%BB%A5%E5%88%86%E9%A1%B5%E6%8F%92%E4%BB%B6%E4%B8%BE%E4%BE%8B

1. 加入组件

导入分页插件,在自己创建的MyBatisPlus配置类里面 (3.4.0版本)

  1. /**
  2. * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
  3. */
  4. @Bean
  5. public MybatisPlusInterceptor mybatisPlusInterceptor() {
  6. MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
  7. interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
  8. return interceptor;
  9. }
  10. @Bean
  11. public ConfigurationCustomizer configurationCustomizer() {
  12. return configuration -> configuration.setUseDeprecatedExecutor(false);
  13. }

(3.0.5版本) 分页插件组件

  1. @Bean
  2. public PaginationInterceptor paginationInterceptor(){
  3. return new PaginationInterceptor();
  4. }

2. 测试分页查询

  1. /**
  2. * 测试分页插件
  3. */
  4. @Test
  5. void testPage(){
  6. //创建一个Page对象
  7. // 有参构造参数:第一个:当前页。第二个:一次查几个
  8. Page<User> page = new Page(1,3);
  9. //通过page的对象getxxx()方法可以获得其余属性
  10. System.out.println(page.getTotal());
  11. // 调用selectPage()方法 第一个是page 对象,第二个是wrapper,没有就写null
  12. IPage<User> userIPage = userMapper.selectPage(page,null);
  13. System.out.println(userIPage);
  14. }

六、删除

未命名图片.png

  1. @Test
  2. void testDeleteByMap(){
  3. Map<String,Object> map = new HashMap();
  4. // 根据条件删除,条件是姓名为张三的记录
  5. map.put("name","张三");
  6. userMapper.deleteByMap(map);
  7. }

1. 逻辑删除

  1. 在数据表中增加deleted字段【默认值等于 0】
  2. 实体类中增加deleted属性 添加@TableLogic注解
  3. 添加组件【3.0.5需要 3.1.1以上版本不需要】 ```java /**
  • 逻辑删除组件 */ @Bean public ISqlInjector sqlInjector(){ return new LogicSqlInjector(); } ```
  1. 配置逻辑删除在application.properties中

    1. # 配置逻辑删除
    2. # 没有删除的值为0
    3. mybatis-plus.global-config.db-config.logic-delete-value=1
    4. # 删除的后的 属性为1
    5. mybatis-plus.global-config.db-config.logic-not-delete-value=0
  2. 测试删除

  • 此时查询也查不到 deleted为1 的用户了

    SELECT id,name,age,email,deleted,version,create_time,update_time FROM user WHERE id=? AND deleted=0 查询会自动在后面拼接一个 and deleted = 0.

未命名图片.png

七、条件构造器

用来写复杂的SQL
Wapper 是一个接口,底下有很多的实现类,查询用QueryWrapper类

具体方法看官网: https://baomidou.com/guide/wrapper.html#abstractwrapper
ge(“字段名”,值) 大于等于

  1. /**
  2. * @date: 2021/1/5 23:56
  3. * @author: 易学习
  4. */
  5. @SpringBootTest
  6. public class WrapperTest {
  7. @Autowired
  8. private UserMapper userMapper;
  9. /**
  10. * 1. 查询name不为空,并且邮箱不为空的用户,并且年龄>=12
  11. * isNotNull("字段") 该字段不为空
  12. * ge("字段",值) 大于等于
  13. */
  14. @Test
  15. void test1(){
  16. QueryWrapper<User> wrapper = new QueryWrapper<>();
  17. wrapper.isNotNull("name")
  18. .isNotNull("email")
  19. .ge("age",12);
  20. List<User> users = userMapper.selectList(wrapper);
  21. System.out.println(users);
  22. }
  23. /**
  24. * 2. 查询名字为 张五的记录
  25. */
  26. @Test
  27. void test2(){
  28. QueryWrapper<User> wrapper = new QueryWrapper<>();
  29. wrapper.eq("name","张五");
  30. List user = userMapper.selectList(wrapper);
  31. System.out.println(user);
  32. }
  33. /**
  34. * 3. 查询年龄在18-20之间的用户数量
  35. */
  36. @Test
  37. void test3(){
  38. QueryWrapper<User> wrapper = new QueryWrapper<>();
  39. wrapper.between("age",18,20);
  40. Integer integer = userMapper.selectCount(wrapper);
  41. System.out.println(integer);
  42. }
  43. /**
  44. * 4. 模糊查询,名字里面没有五的
  45. */
  46. @Test
  47. void test4(){
  48. QueryWrapper wrapper = new QueryWrapper();
  49. wrapper.notLike("name","五");
  50. List list = userMapper.selectMaps(wrapper);
  51. System.out.println(list);
  52. }
  53. }