1. MyBatis-Plus入门

1.1 认识MyBatis-Plus

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

  • 润物无声
    • 只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑。
  • 效率至上
    • 只需简单配置,即可快速进行 CRUD 操作,从而节省大量时间。
  • 丰富功能
    • 热加载、代码生成、分页、性能分析等功能一应俱全。

      1.2 初体验

      1、创建数据库:mybatis_plus。
      2、创建 user 表,并插入一些数据。 ```sql 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’);

  1. 3、设置 Java 编译器为 1.8,项目和文件的编码为 UTF-8maven 为自己本地的库。<br />4、创建 maven 项目,设置 springboot 版本为 2.2.1.RELEASE,引入 MyBatis-Plus 依赖(不要再次引入 MyBatis,避免版本差异问题)。
  2. ```xml
  3. <dependencies>
  4. <dependency>
  5. <groupId>org.springframework.boot</groupId>
  6. <artifactId>spring-boot-starter</artifactId>
  7. </dependency>
  8. <!-- 防止springboot 启动就停止 -->
  9. <dependency>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-web</artifactId>
  12. </dependency>
  13. <dependency>
  14. <groupId>org.springframework.boot</groupId>
  15. <artifactId>spring-boot-starter-test</artifactId>
  16. <scope>test</scope>
  17. <exclusions>
  18. <exclusion>
  19. <groupId>org.junit.vintage</groupId>
  20. <artifactId>junit-vintage-engine</artifactId>
  21. </exclusion>
  22. </exclusions>
  23. </dependency>
  24. <!--mybatis-plus-->
  25. <dependency>
  26. <groupId>com.baomidou</groupId>
  27. <artifactId>mybatis-plus-boot-starter</artifactId>
  28. <version>3.3.1</version>
  29. </dependency>
  30. <!--mysql运行时依赖-->
  31. <dependency>
  32. <groupId>mysql</groupId>
  33. <artifactId>mysql-connector-java</artifactId>
  34. <scope>runtime</scope>
  35. </dependency>
  36. <!--lombok用来简化实体类-->
  37. <dependency>
  38. <groupId>org.projectlombok</groupId>
  39. <artifactId>lombok</artifactId>
  40. <optional>true</optional>
  41. </dependency>
  42. </dependencies>

4、配置 application.yml 文件,注意:springboot2.0 内置 jdbc5 驱动,springboot2.1及以上 内置 jdbc8 驱动。

  1. spring:
  2. datasource:
  3. driver-class-name: com.mysql.cj.jdbc.Driver
  4. url: jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8
  5. username: root
  6. password: 123456

?serverTimezone=GMT%2B8 解决服务器时间无法解析的问题

java.sql.SQLException: The server time zone value ‘Öйú±ê׼ʱ¼ä’ is unrecognized or represents more
5、在 SpringBoot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹。
6、创建包 entity 编写实体类 User。

  1. @Data
  2. public class User {
  3. private Long id;
  4. private String name;
  5. private Integer age;
  6. private String email;
  7. }

7、创建包 mapper 编写 Mapper 接口:UserMapper。

  1. @Repository
  2. public interface UserMapper extends BaseMapper<User> {
  3. }

8、添加测试类,进行功能测试,查看结果。

  1. @SpringBootTest
  2. class MybatisPlusApplicationTests {
  3. //因为找不到注入的对象,类是动态创建的,但是程序可以运行,在 dao 层接口上添加 @Repository 注解
  4. @Autowired
  5. private UserMapper userMapper;
  6. @Test
  7. void contextLoads() {
  8. //selectList中的参数是 MP 内置的条件封装器 Wrapper
  9. List<User> users = userMapper.selectList(null);
  10. users.forEach(System.out::println);
  11. }
  12. }

image.png
9、查看 sql 输出日志。

  1. #查看sql输出日志
  2. mybatis-plus:
  3. configuration:
  4. log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

1.3 插入操作

  1. @SpringBootTest
  2. class MybatisPlusApplicationTests {
  3. //因为找不到注入的对象,类是动态创建的,但是程序可以运行,在 dao 层接口上添加 @Repository 注解
  4. @Autowired
  5. private UserMapper userMapper;
  6. @Test
  7. void contextLoads() {
  8. //selectList中的参数是 MP 内置的条件封装器 Wrapper
  9. List<User> users = userMapper.selectList(null);
  10. users.forEach(System.out::println);
  11. User user = new User();
  12. user.setAge(12);
  13. user.setEmail("2343242@qq.com");
  14. user.setName("威少");
  15. int result = userMapper.insert(user);
  16. System.out.println("影响的行数:" + result);
  17. System.out.println("id:" + user); //id自动回填
  18. }
  19. }

image.png
注意: 数据库插入 id 值默认为:全局唯一 id,随机生成。

1.4 更新操作

  1. @Test
  2. void updateUser() {
  3. User user = new User();
  4. user.setId(1L);
  5. user.setAge(23);
  6. int result = userMapper.updateById(user);
  7. System.out.println("影响的行数:" + result);
  8. }

注意:update 生成的 sql 是:update user set age = ? where id = ?;

2. 数据库分库分表策略

2.1 背景

随着业务规模的不断扩大,需要选择合适的方案去应对数据规模的增长,以应对逐渐增长的访问压力和数据量。数据库的扩展方式主要包括:业务分库、主从复制,数据库分表。

2.2 业务分库

业务分库 就是根据业务模块将数据分散到不同的数据库服务器下。例如:一个简单的电商网站,包括用户、商品、订单三个业务模块,可以将用户数据、商品数据、订单数据分开放到三台不同的数据库服务器上,而不是将所有数据都放在一台数据库服务器上,这样的就变成了3个数据库同时承担压力,系统的吞吐量自然就提高了。
带来的问题

  • 无法进行不同数据库的表 join 操作。
  • 业务分库后,表分散到不同的数据库中,无法通过事务统一修改,考虑分布式事务等等。
  • 成本变高了,本来 1 台服务器能搞定的事情变成了 3 台。

image.png

2.3 主从复制和读写分离

读写分离 的基本原理是将数据库读写操作分散到不同的节点上,其基本实现是:

  • 数据库服务器搭建主从集群,一主一从、一主多从都可以。
  • 数据库主机负责读写操作,从机只负责读操作。
  • 数据库主机通过复制将数据同步到从机,每台数据库服务器都存储了所有的业务数据。
  • 业务服务器将写操作发给数据库主机,将读操作发给数据库从机。

image.png

2.4 数据库分表

将不同业务数据分散存储到不同的数据库服务器,能够支撑百万甚至千万用户规模的业务,但如果业务继续发展,同一业务的单表数据也会达到单台数据库服务器的处理瓶颈。例如,淘宝的几亿用户数据,如果全部存放在一台数据库服务器的一张表中,肯定是无法满足性能要求的,此时就需要对单表数据进行拆分。单表数据拆分有两种方式:垂直分表水平分表
image.png

  • 垂直分表:
    • 分的是列。
    • 垂直分表适合将表中某些不常用且占了大量空间的列拆分出去。
    • 例如,前面示意图中的 nickname 和 description 字段,假设我们是一个婚恋网站,用户在筛选其他用户的时候,主要是用 age 和 sex 两个字段进行查询,而 nickname 和 description 两个字段主要用于展示,一般不会在业务查询中用到。description 本身又比较长,因此我们可以将这两个字段独立到另外一张表中,这样在查询 age 和 sex 时,就能带来一定的性能提升。
  • 水平分表:

    • 分的是行。
    • 水平分表适合表行数特别大的表,有的公司要求单表行数超过 5000 万就必须进行分表,这个数字可以作为参考,但并不是绝对标准,关键还是要看表的访问性能。对于一些比较复杂的表,可能超过 1000 万就要分表了;而对于一些简单的表,即使存储数据超过 1 亿行,也可以不分表。
    • 当看到表的数据量达到千万级别时,就要警惕了。

      水平分表相比垂直分表,引入更多的复杂性:

  • 主键自增:

    • 以最常见的用户 ID 为例,可以按照 1000000 的范围大小进行分段,1 ~ 999999 放到表 1中,1000000 ~ 1999999 放到表2中,以此类推。
    • 复杂点:分段大小的选取。分段太小会导致切分后子表数量过多,增加维护复杂度;分段太大可能会导致单表依然存在性能问题,一般建议分段大小在 100 万至 2000 万之间,具体需要根据业务选取合适的分段大小。
    • 优点:可以随着数据的增加平滑地扩充新的表。例如,现在的用户是 100 万,如果增加到 1000 万,只需要增加新的表就可以了,原有的数据不需要动。
    • 缺点:分布不均匀,假如按照 1000 万来进行分表,有可能某个分段实际存储的数据量只有 1000 条,而另外一个分段实际存储的数据量有 900 万条。
  • Hash:
    • 同样以用户 ID 为例,假如我们一开始就规划了 10 个数据库表,路由算法可以简单地用 user_id % 10 的值来表示数据所属的数据库表编号,ID 为 985 的用户放到编号为 5 的子表中,ID 为 10086 的用户放到编号为 6 的子表中。
    • 复杂点:初始表数量的选取。表数量太多维护比较麻烦,表数量太少又可能导致单表性能存在问题。
    • 优点:表分布比较均匀。
    • 缺点:扩充新的表很麻烦,所有数据都要重分布。
  • 雪花算法:分布式 ID 生成器
    • 雪花算法是由 Twitter 公布的分布式主键生成算法,它能够保证不同表的主键的不重复性,以及相同表的主键的有序性。
    • 核心思想:
      • 长度共 64bit(一个 long 型)。
      • 首先是一个符号位,1bit标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0。
      • 41bit 时间截(毫秒级),存储的是时间截的差值(当前时间截 - 开始时间截),结果约等于 69.73 年。
      • 10bit 作为机器的ID(5个bit是数据中心,5个 bit 的机器ID,可以部署在 1024 个节点)。
      • 12bit 作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID)。
    • 优点:整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞,并且效率较高。

image.png

3. MP的主键策略

3.1 ASSIGN_ID

默认主键策略是:ASSIGN_ID(使用了雪花算法)

  1. @TableId(type = IdType.ASSIGN_ID)
  2. private String id;

3.2 AUTO 自增策略

  • 需要在创建数据表的时候设置 主键自增
  • 实体字段中配置 @TableId(type = IdType.AUTO)
    1. @TableId(type = IdType.AUTO)
    2. private Long id;

    要想影响所有实体的配置,可以设置全局主键配置

  1. mybatis-plus:
  2. configuration:
  3. log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #查看sql输出日志
  4. global-config:
  5. db-config:
  6. id-type: assign_id #所有实体主键策略

4. 自动填充

4.1 需求

项目中经常会用到的一些数据,比如 创建时间、更新时间等等,不需要我们自己写,就可以使用 MyBatis Plus 的自动填充功能。

4.2 具体步骤

1、在 user 表中添加 datetime 类型的新字段 create_time、update_time。
2、在实体类上增加字段并添加自动填充注解。

  1. @Data
  2. public class User {
  3. @TableId(type = IdType.ASSIGN_ID) //默认主键策略,使用雪花算法
  4. private Long id;
  5. private String name;
  6. private Integer age;
  7. private String email;
  8. @TableField(fill = FieldFill.INSERT)
  9. private Date createTime;
  10. @TableField(fill = FieldFill.INSERT_UPDATE)
  11. private Date updateTime;
  12. }

3、实现元对象处理器接口。

  1. //MyMetaObjectHandler会被Spring的上下文自动调用
  2. @Slf4j
  3. @Component //spring一启动,所表示的这个类的对象会被spring初始化出来
  4. public class MyMetaObjectHandler implements MetaObjectHandler {
  5. @Override
  6. public void insertFill(MetaObject metaObject) {
  7. log.info("start insert fill ....");
  8. this.setFieldValByName("createTime", new Date(), metaObject);
  9. this.setFieldValByName("updateTime", new Date(), metaObject);
  10. }
  11. @Override
  12. public void updateFill(MetaObject metaObject) {
  13. log.info("start update fill ....");
  14. this.setFieldValByName("updateTime", new Date(), metaObject);
  15. }
  16. }

4、执行插入测试,查看创建时间和更新时间是否自动填充。
image.png

5. 乐观锁

5.1 场景

一件商品,成本价是 80 元,售价是 100 元。老板先是通知小李,说你去把商品价格增加 50 元。小李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到 150 元,价格太高,可能会影响销量。又通知小王,你把商品价格降低30元。
此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格 100 元;小王也在操作,取出的商品价格也是100 元。小李将价格加了 50 元,并将 100+50=150 元存入了数据库;小王将商品减了30元,并将 100-30=70 元存入了数据库。是的,如果没有锁,小李的操作就完全被小王的覆盖了。现在商品价格是 70 元,比成本价低 10 元。几分钟后,这个商品很快出售了1千多件商品,老板亏1多万。

  • 如果是乐观锁,小王保存价格前,会检查下价格是否被人修改过了。如果被修改过了,则重新取出的被修改后的价格,150元,这样他会将120元存入数据库。
  • 如果是悲观锁,小李取出数据后,小王只能等小李操作完之后,才能对价格进行操作,也会保证最终的价格是120元。

    5.2 模拟修改冲突

    1、数据库增加商品表并添加数据。

    1. CREATE TABLE product
    2. (
    3. id BIGINT(20) NOT NULL COMMENT '主键ID',
    4. name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
    5. price INT(11) DEFAULT 0 COMMENT '价格',
    6. version INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
    7. PRIMARY KEY (id)
    8. );
    9. INSERT INTO product (id, NAME, price) VALUES (1, '外星人笔记本', 100);

    2、创建对应实体类。

    1. @Data
    2. public class Product {
    3. private Long id;
    4. private String name;
    5. private Integer price;
    6. private Integer version;
    7. }

    3、创建对应 Mapper。

    1. @Repository
    2. public interface ProductMapper extends BaseMapper<Product> {
    3. }

    4、测试,模拟冲突。

    1. @Test
    2. void testConcurrentUpdate() {
    3. //1.小李
    4. Product p1 = productMapper.selectById(1L);
    5. System.out.println("小李取出的价格:" + p1.getPrice());
    6. //2.小王此时也取数据
    7. Product p2 = productMapper.selectById(1L);
    8. System.out.println("小王取出的价格:" + p2.getPrice());
    9. //3.小李将价格加了50元,存入了数据库
    10. p1.setPrice(p1.getPrice() + 50);
    11. productMapper.updateById(p1);
    12. //4.小王将价格减了30元,存入了数据库
    13. p2.setPrice(p2.getPrice() - 30);
    14. int result = productMapper.updateById(p2);
    15. //5.查看结果
    16. if (result == 0) { //更新失败,重试
    17. //重新获取数据
    18. p2 = productMapper.selectById(1L);
    19. //更新
    20. p2.setPrice(p2.getPrice() - 30);
    21. productMapper.updateById(p2);
    22. }
    23. //6.输出最终结果
    24. Product p3 = productMapper.selectById(1L);
    25. System.out.println("最后的结果:" + p3.getPrice());
    26. }

    5、查看最终结果,会覆盖。
    image.png

    5.3 解决方案

    1、数据库添加 version 字段。
    2、取出记录时,获取当前 version。

    1. SELECT id,`name`,price,`version` FROM product WHERE id=1

    3、更新时,version + 1,如果where语句中的version版本不对,则更新失败。

    1. UPDATE product SET price=price+50, `version`=`version` + 1 WHERE id=1 AND `version`=1

    5.4 乐观锁实现流程

    1、修改实体类,添加 @Version 注解。

    1. @Version
    2. private Integer version;

    2、创建配置文件,在 config 目录下创建 MyBatisPlusConfig,注册乐观锁插件。

    1. @EnableTransactionManagement
    2. @Configuration
    3. @MapperScan("com.xuwei.mapper")
    4. public class MyBatisPlusConfig {
    5. /**
    6. * 乐观锁插件
    7. * @return
    8. */
    9. @Bean
    10. public OptimisticLockerInterceptor optimisticLockerInterceptor() {
    11. return new OptimisticLockerInterceptor();
    12. }
    13. }

    3、重新测试,结果正确。
    image.png

    6. 查询

    6.1 通过多个id批量查询

    1. //通过多个id批量查询
    2. @Test
    3. void testSelectBatchIds() {
    4. List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
    5. users.forEach(System.out::println);
    6. }

    6.2 通过map封装查询条件

    1. //通过map封装查询
    2. @Test
    3. void testSelectByMap() {
    4. HashMap<String, Object> map = new HashMap<>();
    5. map.put("name", "hello");
    6. map.put("age", 18);
    7. List<User> users = userMapper.selectByMap(map);
    8. users.forEach(System.out::println);
    9. }

    image.png

    6.3 分页

    1、MyBatis-Plus 自带分页插件,在配置类中添加 @Bean 配置。

    1. /**
    2. * 分页插件
    3. * @return
    4. */
    5. @Bean
    6. public PaginationInterceptor paginationInterceptor() {
    7. return new PaginationInterceptor();
    8. }

    2、测试 selectPage 分页,通过 page 对象获取相关数据。

    1. //测试分页
    2. @Test
    3. void testselectPage() {
    4. Page<User> page = new Page<>(1,5);
    5. Page<User> pageParam = userMapper.selectPage(page, null); //获取分页对象
    6. pageParam.getRecords().forEach(System.out::println);
    7. System.out.println(pageParam.getCurrent());
    8. System.out.println(pageParam.getPages());
    9. System.out.println(pageParam.getSize());
    10. System.out.println(pageParam.getTotal());
    11. System.out.println(pageParam.hasNext());
    12. System.out.println(pageParam.hasPrevious());
    13. }

    image.png
    3、返回特定的列,而不是很多 null 值。

    1. //返回特定的列
    2. @Test
    3. void testselectMapsPage() {
    4. Page<Map<String, Object>> page = new Page<Map<String, Object>>(); //默认每页显示10条数据
    5. QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    6. queryWrapper.select("name", "age");
    7. Page<Map<String, Object>> pageParam = userMapper.selectMapsPage(page, queryWrapper);
    8. List<Map<String, Object>> records = pageParam.getRecords();
    9. records.forEach(System.out::println);
    10. System.out.println(pageParam.getCurrent());
    11. System.out.println(pageParam.getPages());
    12. System.out.println(pageParam.getSize());
    13. System.out.println(pageParam.getTotal());
    14. System.out.println(pageParam.hasNext());
    15. System.out.println(pageParam.hasPrevious());
    16. }

    image.png

    7. 删除

    7.1 通过id删除

    1. //通过id删除
    2. @Test
    3. void testDeleteById() {
    4. int result = userMapper.deleteById(5L);
    5. System.out.println(result);
    6. }

    7.2 批量删除

    1. //批量删除
    2. @Test
    3. void testDeleteBatchIds() {
    4. int result = userMapper.deleteBatchIds(Arrays.asList(8,9,10));
    5. System.out.println(result);
    6. }

    7.3 通过map删除

    1. //通过map条件删除
    2. @Test
    3. void testDeleteByMap() {
    4. HashMap<String, Object> map = new HashMap<>();
    5. map.put("name", "Helen");
    6. map.put("age", 18);
    7. int result = userMapper.deleteByMap(map);
    8. System.out.println(result);
    9. }

    7.4 逻辑删除

  • 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据。

  • 逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录。
    • 可以进行数据恢复。
    • 有关联数据,不便删除。

1、数据库修改,添加 deleted 字段。

  1. ALTER TABLE `user` ADD COLUMN `deleted` boolean DEFAULT false

2、实体类添加 deleted 字段,并加上 @TableLogic 注解。

  1. @TableLogic
  2. private Integer deleted;

3、测试删除语句。

  1. @Test
  2. public void testLogicDelete() {
  3. int result = userMapper.deleteById(1L);
  4. System.out.println(result);
  5. }

4、测试逻辑删除后的查询。

  1. @Test
  2. public void testLogicDeleteSelect() {
  3. List<User> users = userMapper.selectList(null);
  4. users.forEach(System.out::println);
  5. }

image.pngimage.png

8. 条件构造器

8.1 wapper入门

image.png

  • Wrapper : 条件构造抽象类,最顶端父类 。

    • AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件。
      • QueryWrapper : 查询条件封装。
      • UpdateWrapper : Update 条件封装。
    • AbstractLambdaWrapper : 使用Lambda 语法。

      • LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper。
      • LambdaUpdateWrapper : Lambda 更新封装Wrapper。

        8.2 ge、gt、le、lt、isNull、isNotNull

        1. //ge、gt、le等
        2. @Test
        3. void testDeleteWrapper() {
        4. QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        5. queryWrapper.isNull("name").ge("age", 12).isNotNull("email"); //ge表示大于等于,le表示小于等于
        6. int result = userMapper.delete(queryWrapper);
        7. System.out.println("delete return count = " + result);
        8. }

        8.3 eq、ne

        1. //eq、ne
        2. @Test
        3. void testselectOne() { //selectOne返回的是一条实体记录,出现多条会报错
        4. QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        5. queryWrapper.eq("name", "Tom");
        6. User user = userMapper.selectOne(queryWrapper);
        7. System.out.println(user);
        8. }
        image.png

        8.4 between、notBetween

        1. @Test
        2. void testselectCount() { //selectOne返回的是一条实体记录,出现多条会报错
        3. QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        4. queryWrapper.between("age", 10, 30);
        5. int result = userMapper.selectCount(queryWrapper);
        6. System.out.println(result);
        7. }

        8.5 like、notLike、likeLeft、likeRight

        1. @Test
        2. void testselectMaps() { //selectOne返回的是一条实体记录,出现多条会报错
        3. QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        4. queryWrapper.select("name", "age").like("name","e").likeRight("email", "5");
        5. List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
        6. maps.forEach(System.out::println);
        7. }

        8.6 in、notIn、inSql、notinSql、exists、notExists

        ```java @Test public void testSelectObjs() {

      QueryWrapper queryWrapper = new QueryWrapper<>(); // queryWrapper.in(“id”, 1, 2, 3); queryWrapper.inSql(“id”, “select id from user where id <= 3”);

      List objects = userMapper.selectObjs(queryWrapper);//返回值是Object列表 objects.forEach(System.out::println); }

      1. <a name="PT3Rw"></a>
      2. ### 8.7 or、and
      3. **注意:**这里使用的是 UpdateWrapper
      4. ```java
      5. //or、and
      6. @Test
      7. void testUpdateWrapper() { //selectOne返回的是一条实体记录,出现多条会报错
      8. //修改值
      9. User user = new User();
      10. user.setAge(33);
      11. user.setName("andy");
      12. //修改条件
      13. UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
      14. userUpdateWrapper.like("name", "h").or().between("age", 20, 30);
      15. int result = userMapper.update(user, userUpdateWrapper);
      16. System.out.println(result);
      17. }

      image.png

      8.8 Lambda表达式

      1. //lambda
      2. @Test
      3. void testUpdate2() { //selectOne返回的是一条实体记录,出现多条会报错
      4. //修改值
      5. User user = new User();
      6. user.setAge(23);
      7. user.setName("andy");
      8. //修改条件
      9. UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
      10. userUpdateWrapper
      11. .like("name", "h")
      12. .or(i -> i.like("name", "a").eq("age", 20));
      13. int result = userMapper.update(user, userUpdateWrapper);
      14. System.out.println(result);
      15. }

      image.png

      8.9 orderBy、orderByDesc、orderByAsc

      1. @Test
      2. public void testSelectListOrderBy() {
      3. QueryWrapper<User> queryWrapper = new QueryWrapper<>();
      4. queryWrapper.orderByDesc("age", "id");
      5. List<User> users = userMapper.selectList(queryWrapper);
      6. users.forEach(System.out::println);
      7. }

      8.10 set、setSql

      最终的sql会合并 user.setAge(),以及 userUpdateWrapper.set() 和 setSql() 中 的字段

      1. @Test
      2. public void testUpdateSet() {
      3. //修改值
      4. User user = new User();
      5. user.setAge(60);
      6. //修改条件
      7. UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
      8. userUpdateWrapper
      9. .like("name", "h")
      10. .set("name", "Peter")//除了可以查询还可以使用set设置修改的字段
      11. .setSql(" email = '123@qq.com'");//可以有子查询
      12. int result = userMapper.update(user, userUpdateWrapper);
      13. }

      image.png

      8.11 查询方式

      | 查询方式 | 说明 | | —- | —- | | setSqlSelect | 设置 SELECT 查询字段 | | where | WHERE 语句,拼接 + WHERE 条件 | | and | AND 语句,拼接 + AND 字段=值 | | andNew | AND 语句,拼接 + AND (字段=值) | | or | OR 语句,拼接 + OR 字段=值 | | orNew | OR 语句,拼接 + OR (字段=值) | | eq | 等于= | | allEq | 基于 map 内容等于= | | ne | 不等于<> | | gt | 大于> | | ge | 大于等于>= | | lt | 小于< | | le | 小于等于<= | | like | 模糊查询 LIKE | | notLike | 模糊查询 NOT LIKE | | in | IN 查询 | | notIn | NOT IN 查询 | | isNull | NULL 值查询 | | isNotNull | IS NOT NULL | | groupBy | 分组 GROUP BY | | having | HAVING 关键词 | | orderBy | 排序 ORDER BY | | orderAsc | ASC 排序 ORDER BY | | orderDesc | DESC 排序 ORDER BY | | exists | EXISTS 条件语句 | | notExists | NOT EXISTS 条件语句 | | between | BETWEEN 条件语句 | | notBetween | NOT BETWEEN 条件语句 | | addFilter | 自由拼接 SQL | | last | 拼接在最后,例如:last(“LIMIT 1”) |