当前位置:   article > 正文

Mybatis和Mybatisplus

mybatis和mybatisplus

1.1 Mybatis概述

1.1.1【Mybatis概念】

问题1:Mybatis是什么,能干嘛?

MyBatis 是一款优秀的 持久层框架,用于简化 JDBC 开发(对jdbc代码进行了封装)

框架:框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型

持久层:JavaEE三层架构:表现层、业务层、持久层

问题2:JDBC存在的缺点?

  • 硬编码

    • 注册驱动、获取连接

    • SQL语句

  • 操作繁琐

    • 手动设置参数

    • 手动封装结果集

问题3:Mybatis是如何进行规避的?

  • 硬编码可以配置到配置文件

  • 操作繁琐的地方mybatis都自动完成

2.1 MyBatisPlus简介

2.1.1【MyBatisPlus简介】

问题1:MyBatisPlus具体有哪些特性?

MyBatisPlus特性

  • 无侵入:只做增强不做改变,不会对现有工程产生影响

  • 强大的 CRUD 操作:内置通用 Mapper,少量配置即可实现单表CRUD 操作

  • 支持 Lambda:编写查询条件无需担心字段写错

  • 支持主键自动生成

  • 内置分页插件

  • ……

2.2 标准CRUD制作

2.2 【标准CRUD制作】

问题1:MyBatisPlus内置通用Mapper提供了哪些API?

 

问题2:Lombok插件的功能?

可以自动生成实体类的GET、SET方法

Lombok,一个Java类库,提供了一组注解,简化POJO实体类开发。

  1. <dependency>
  2. <groupId>org.projectlombok</groupId>
  3. <artifactId>lombok</artifactId>
  4. <version>1.18.12</version>
  5. </dependency>

常用注解:

@Data,为当前实体类在编译期设置对应的get/set方法,无参/无参构造方法,toString方法,hashCode方法,equals方法等

代码展示:

  1. import lombok.*;
  2. /*
  3. 1 生成getter和setter方法:@Getter@Setter
  4. 生成toString方法:@ToString
  5. 生成equals和hashcode方法:@EqualsAndHashCode
  6. 2 统一成以上所有:@Data
  7. 3 生成空参构造: @NoArgsConstructor
  8. 生成全参构造: @AllArgsConstructor
  9. 4 lombok还给我们提供了builder的方式创建对象,好处就是可以链式编程。 @Builder【扩展】
  10. */
  11. @Data
  12. public class User {
  13. private Long id;
  14. private String name;
  15. private String password;
  16. private Integer age;
  17. private String tel;

2.3 标准分页功能制作

2.3.1 【标准分页功能制作】

问题1:MyBatisPlus实现分页功能步骤?

分页功能接口

 MyBatisPlus分页使用

①:设置分页拦截器作为Spring管理的bean

  1. import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
  2. import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. @Configuration
  6. public class MybatisPlusConfig {
  7. @Bean
  8. public MybatisPlusInterceptor mybatisPlusInterceptor(){
  9. //1 创建MybatisPlusInterceptor拦截器对象
  10. MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();
  11. //2 添加分页拦截器
  12. mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
  13. return mpInterceptor;
  14. }
  15. }

②:执行分页查询

  1. //分页查询
  2. @Test
  3. void testSelectPage(){
  4. //1 创建IPage分页对象,设置分页参数
  5. IPage<User> page=new Page<>(1,3);
  6. //2 执行分页查询
  7. userDao.selectPage(page,null);
  8. //3 获取分页结果
  9. System.out.println("当前页码值:"+page.getCurrent());
  10. System.out.println("每页显示数:"+page.getSize());
  11. System.out.println("总页数:"+page.getPages());
  12. System.out.println("总条数:"+page.getTotal());
  13. System.out.println("当前页数据:"+page.getRecords());
  14. }

问题2:Mybatis分页底层实现原理?

拦截器

3.1 条件查询的三种格式

3.1.1 【条件查询的三种格式】

问题1:条件查询的三种格式?

 

方式一:按条件查询

  1. //方式一:按条件查询
  2. QueryWrapper<User> qw=new QueryWrapper<>();
  3. qw.lt("age", 18);
  4. List<User> userList = userDao.selectList(qw);
  5. System.out.println(userList);

方式二:lambda格式按条件查询

  1. //方式二:lambda格式按条件查询
  2. QueryWrapper<User> qw = new QueryWrapper<User>();
  3. qw.lambda().lt(User::getAge, 10);
  4. List<User> userList = userDao.selectList(qw);
  5. System.out.println(userList);

方式三:lambda格式按条件查询(推荐)

  1. //方式三:lambda格式按条件查询
  2. LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
  3. lqw.lt(User::getAge, 10);
  4. List<User> userList = userDao.selectList(lqw);
  5. System.out.println(userList);

并且关系(and)

  1. //并且关系
  2. LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
  3. //并且关系:1030岁之间
  4. lqw.lt(User::getAge, 30).gt(User::getAge, 10);
  5. List<User> userList = userDao.selectList(lqw);
  6. System.out.println(userList);

或者关系(or)

  1. //或者关系
  2. LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
  3. //或者关系:小于10岁或者大于30
  4. lqw.lt(User::getAge, 10).or().gt(User::getAge, 30);
  5. List<User> userList = userDao.selectList(lqw);
  6. System.out.println(userList);

3.2 条件查询null判定

3.2.1 【条件查询null判定】

问题1:搜索场景,在多条件查询中,有条件的值为空应该怎么解决?

if语句控制条件追加

  1. Integer minAge=10; //将来有用户传递进来,此处简化成直接定义变量了
  2. Integer maxAge=null; //将来有用户传递进来,此处简化成直接定义变量了
  3. LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
  4. if(minAge!=null){
  5. lqw.gt(User::getAge, minAge);
  6. }
  7. if(maxAge!=null){
  8. lqw.lt(User::getAge, maxAge);
  9. }
  10. List<User> userList = userDao.selectList(lqw);
  11. userList.forEach(System.out::println);

问题2:MyBatisPlus是如何对NULL值进行处理的?

条件参数控制

  1. Integer minAge=10; //将来有用户传递进来,此处简化成直接定义变量了
  2. Integer maxAge=null; //将来有用户传递进来,此处简化成直接定义变量了
  3. LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
  4. //参数1:如果表达式为true,那么查询才使用该条件
  5. lqw.gt(minAge!=null,User::getAge, minAge);
  6. lqw.lt(maxAge!=null,User::getAge, maxAge);
  7. List<User> userList = userDao.selectList(lqw);
  8. userList.forEach(System.out::println);

条件参数控制(链式编程)

  1. Integer minAge=10; //将来有用户传递进来,此处简化成直接定义变量了
  2. Integer maxAge=null; //将来有用户传递进来,此处简化成直接定义变量了
  3. LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
  4. //参数1:如果表达式为true,那么查询才使用该条件
  5. lqw.gt(minAge!=null,User::getAge, minAge)
  6. .lt(maxAge!=null,User::getAge, maxAge);
  7. List<User> userList = userDao.selectList(lqw);
  8. userList.forEach(System.out::println);

3.3 查询投影,设置【查询字段、分组、分页】

3.3.1 【查询投影】

问题1:如何指定需要查询字段?

查询结果包含模型类中部分属性

  1. /*LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
  2. lqw.select(User::getId, User::getName, User::getAge);*/
  3. //或者
  4. QueryWrapper<User> lqw = new QueryWrapper<User>();
  5. lqw.select("id", "name", "age", "tel");
  6. List<User> userList = userDao.selectList(lqw);
  7. System.out.println(userList);

查询结果包含模型类中未定义的属性

  1. QueryWrapper<User> lqw = new QueryWrapper<User>();
  2. lqw.select("count(*) as count, tel");
  3. lqw.groupBy("tel");
  4. List<Map<String, Object>> userList = userDao.selectMaps(lqw);
  5. System.out.println(userList);

问题2:如果查询投影无法满足需求,应该如何处理?

在Mapper接口中,自定义SQL和查询接口

3.4 查询条件设置

3.4.1 【查询条件设置】

问题1:多条件查询有哪些组合?

多条件查询有哪些组合?

  • 范围匹配(> 、 = 、between)

  • 模糊匹配(like)

  • 空判定(null)

  • 包含性匹配(in)

  • 分组(group)

  • 排序(order)

  • ……

查询条件案例:

用户登录(eq匹配)

  1. LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
  2. //等同于=
  3. lqw.eq(User::getName, "Jerry").eq(User::getPassword, "jerry");
  4. User loginUser = userDao.selectOne(lqw);
  5. System.out.println(loginUser);

  购物设定价格区间、户籍设定年龄区间(le ge匹配 或 between匹配)

  1. LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
  2. //范围查询 lt le gt ge eq between
  3. lqw.between(User::getAge, 10, 30);
  4. List<User> userList = userDao.selectList(lqw);
  5. System.out.println(userList);

查信息,搜索新闻(非全文检索版:like匹配)

  1. LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
  2. //模糊匹配 like
  3. lqw.likeLeft(User::getName, "J");
  4. List<User> userList = userDao.selectList(lqw);
  5. System.out.println(userList);

统计报表(分组查询聚合函数)

  1. QueryWrapper<User> qw = new QueryWrapper<User>();
  2. qw.select("gender","count(*) as nums");
  3. qw.groupBy("gender");
  4. List<Map<String, Object>> maps = userDao.selectMaps(qw);
  5. System.out.println(maps);

3.5 映射匹配兼容性

3.5.1 【映射匹配兼容性】

问题1:如何处理数据表字段和Java实体属性不匹配问题?

表字段与编码属性设计不同步

  • 在模型类属性上方,使用@TableField属性注解,通过value属性,设置当前属性对应的数据库表中的字段关系。

 

问题2:如何处理数据表字段不存在,Java实体属性存在问题?

编码中添加了数据库中未定义的属性

  • 在模型类属性上方,使用@TableField注解,通过exist属性,设置属性在数据库表字段中是否存在,默认为true。此属性无法与value合并使用。

 

问题3:如何实现数据表中字段不参与查询,保证数据安全性?

  • 在模型类属性上方,使用@TableField注解,通过==select==属性:设置该属性是否参与查询。此属性与select()映射配置不冲突。

 

问题4:如何处理 表名和实体名称不一致问题?

  • 模型类上方,使用@TableName注解,通过==value==属性,设置当前类对应的数据库表名称。

 

  1. @Data
  2. @TableName("tbl_user")
  3. public class User {
  4. /*
  5. id为Long类型,因为数据库中id为bigint类型,
  6. 并且mybatis有自己的一套id生成方案,生成出来的id必须是Long类型
  7. */
  8. private Long id;
  9. private String name;
  10. @TableField(value = "pwd",select = false)
  11. private String password;
  12. private Integer age;
  13. private String tel;
  14. @TableField(exist = false) //表示online字段不参与CRUD操作
  15. private Boolean online;
  16. }

4.1 id生成策略

4.1.1 【id生成策略】

问题1:一个ID一般来说有哪些要求?

  • 全局唯一:不能出现重复的ID号,既然是唯一标识,这是最基本的要求。

  • 趋势递增:确保生成的ID是对于某个用户或者业务是按一定的数字有序递增的。

  • 高可用性:确保任何时候都能正确的生成ID。

  • 带时间:ID里面包含时间,一眼扫过去就知道哪天的交易。

问题2:id生成策略有哪些?

  1. UUID

    Java自带的生成UUID的方式就能生成一串唯一随机32(32个16进制数字)位长度数据,而且够我们用N亿年,全球唯一。

  2. 数据库自增ID

  3. 中间件

  4. 开源算法Snowflake

    指定机器 & 同一时刻 & 某一并发序列,是唯一的,据此可生成一个64 bits的唯一ID(long)。

id生成策略控制(@TableId注解)

  • 名称:@TableId

  • 类型:属性注解

  • 位置:模型类中用于表示主键的属性定义上方

  • 作用:设置当前类中主键属性的生成策略

  • 相关属性

                type:设置主键属性的生成策略,值参照IdType枚举值

 全局策略配置

  1. mybatis-plus:
  2. global-config:
  3. db-config:
  4. id-type: assign_id
  5. table-prefix: tbl_

id生成策略全局配置

 

表名前缀全局配置

 

问题3:实际工作最常用的id生成策略?

看具体需求,推荐使用snowflake

4.2 多数据操作(删除与查询)

4.2.2 【多数据操作(删除与查询)】

问题1:mp如何实现批量删除?

  1. //删除指定多条数据
  2. List<Long> list = new ArrayList<>();
  3. list.add(1402551342481838081L);
  4. list.add(1402553134049501186L);
  5. list.add(1402553619611430913L);
  6. userDao.deleteBatchIds(list);

问题2:mp如何实现批量删除?

  1. //查询指定多条数据
  2. List<Long> list = new ArrayList<>();
  3. list.add(1L);
  4. list.add(3L);
  5. list.add(4L);
  6. userDao.selectBatchIds(list);

4.3 逻辑删除

4.3.1 【逻辑删除】

问题1:逻辑删除使用场景?

在实际环境中,如果想删除一条数据,是否会真的从数据库中删除该条数据?

  • 删除操作业务问题:业务数据从数据库中丢弃

问题2:如何实现逻辑删除?

逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,数据保留在数据库中

 

逻辑删除案例

①:数据库表中添加逻辑删除标记字段

 

②:实体类中添加对应字段,并设定当前字段为逻辑删除标记字段

  1. import com.baomidou.mybatisplus.annotation.*;
  2. import lombok.Data;
  3. @Data
  4. public class User {
  5. private Long id;
  6. //逻辑删除字段,标记当前记录是否被删除
  7. @TableLogic
  8. private Integer deleted;

③:配置逻辑删除字面值

  1. mybatis-plus:
  2. global-config:
  3. db-config:
  4. table-prefix: tbl_
  5. # 逻辑删除字段名
  6. logic-delete-field: deleted
  7. # 逻辑删除字面值:未删除为0
  8. logic-not-delete-value: 0
  9. # 逻辑删除字面值:删除为1
  10. logic-delete-value: 1

逻辑删除本质:逻辑删除的本质其实是修改操作。如果加了逻辑删除字段,查询数据时也会自动带上逻辑删除字段。

  注意:如果不想自动带上逻辑删除字段,需要自己编写SQL。

 

4.4 乐观锁

4.4.1 【乐观锁】

问题1:加锁的场景?乐观锁使用场景?

业务并发现象带来的问题:秒杀

mp提供的乐观锁适用于小型的并发

问题2:mp乐观锁 实现流程和底层原理?

原理:update set xx=yy , version=version+1 where version=num;

mp通过拦截器进行增强

操作流程:

  1. 先查询version

  2. 修改操作(并发修改只有一个能成功,通过version的值来保证)

 

乐观锁案例

①:数据库表中添加锁标记字段

 

②:实体类中添加对应字段,并设定当前字段为逻辑删除标记字段

  1. import com.baomidou.mybatisplus.annotation.TableField;
  2. import com.baomidou.mybatisplus.annotation.TableLogic;
  3. import com.baomidou.mybatisplus.annotation.Version;
  4. import lombok.Data;
  5. @Data
  6. public class User {
  7. private Long id;
  8. @Version
  9. private Integer version;

③:配置乐观锁拦截器实现锁机制对应的动态SQL语句拼装

  1. import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
  2. import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
  3. import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. @Configuration
  7. public class MpConfig {
  8. @Bean
  9. public MybatisPlusInterceptor mpInterceptor() {
  10. //1.定义Mp拦截器
  11. MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
  12. //2.添加乐观锁拦截器
  13. mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
  14. return mpInterceptor;
  15. }

④:使用乐观锁机制在修改前必须先获取到对应数据的verion方可正常进行

  1. @Test
  2. public void testUpdate() {
  3. /*User user = new User();
  4. user.setId(3L);
  5. user.setName("Jock666");
  6. user.setVersion(1);
  7. userDao.updateById(user);*/
  8. //1.先通过要修改的数据id将当前数据查询出来
  9. //User user = userDao.selectById(3L);
  10. //2.将要修改的属性逐一设置进去
  11. //user.setName("Jock888");
  12. //userDao.updateById(user);
  13. //1.先通过要修改的数据id将当前数据查询出来
  14. User user = userDao.selectById(3L); //version=3
  15. User user2 = userDao.selectById(3L); //version=3
  16. user2.setName("Jock aaa");
  17. userDao.updateById(user2); //version=>4
  18. user.setName("Jock bbb");
  19. userDao.updateById(user); //verion=3?条件还成立吗?
  20. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/201351
推荐阅读
相关标签
  

闽ICP备14008679号