• SSM框架,MyBatis-Plus的学习(下)


    条件构造器

    使用MyBatis-Plus的条件构造器,可以构建灵活高效的查询条件,可以通过链式调用来组合多个条件。

    条件构造器的继承结构

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

    • AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
      • QueryWrapper : 查询/删除条件封装
      • UpdateWrapper : 修改条件封装
      • AbstractLambdaWrapper : 使用Lambda 语法
        • LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
        • LambdaUpdateWrapper : Lambda 更新封装Wrapper

    QueryWapper的使用

    使用案例:

    1. @org.junit.jupiter.api.Test
    2. public void test_select(){
    3. QueryWrapper queryWrapper = new QueryWrapper<>();
    4. // //模糊查询
    5. // queryWrapper.like("name","a");//名字中包含a字符
    6. // //范围查询
    7. // queryWrapper.between("age",20,30);
    8. // //判断不为空
    9. // queryWrapper.isNotNull("email");
    10. //结果的sql语句为:
    11. //SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (username LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
    12. //可以链式调用
    13. queryWrapper.like("username", "a")
    14. .between("age", 20, 30)
    15. .isNotNull("email");
    16. userMapper.selectList(queryWrapper);
    17. }

    使用步骤

    1. 创建一个QueryWrapper类型的对象,并指定其泛型(泛型为操作的数据对应的实体类类型)
    2. 用QueryWrapper类型的对象调用相应的添加条件的方法,在方法的参数列表中指定条件
    3. 最后将此QueryWrapper类型的对象加到MyBatis-Plus的crud方法的参数列表中,相应的crud方法便会对满足指定条件的数据操作

    QueryWrapper的方法

    • 关于升降序:

    升序降序的优先级由方法中参数的前后或者调用方法的先后决定

    • 关于and和or:

    调用的多个方法之间默认使用AND连接,在调用两个方法时在中间调用一个or方法即可

    1. @Test
    2. public void test04() {
    3. QueryWrapper queryWrapper = new QueryWrapper<>();
    4. //将年龄大于20并且用户名中包含有a或邮箱为null的用户信息修改
    5. //UPDATE t_user SET age=?, email=? WHERE username LIKE ? AND age > ? OR email IS NULL)
    6. queryWrapper
    7. .like("username", "a")
    8. .gt("age", 20)
    9. .or()
    10. .isNull("email");
    11. User user = new User();
    12. user.setAge(18);
    13. user.setEmail("user@atguigu.com");
    14. int result = userMapper.update(user, queryWrapper);
    15. System.out.println("受影响的行数:" + result);
    16. }
    • 关于指定列:

    查询时,默认是查询所有的列,要指定查询的列,调用QueryWrapper的select方法,参数传入要查询的列的列名即可

    1. @Test
    2. public void test05() {
    3. //查询用户信息的username和age字段
    4. //SELECT username,age FROM t_user
    5. QueryWrapper queryWrapper = new QueryWrapper<>();
    6. queryWrapper.select("username", "age");
    7. //selectMaps()返回Map集合列表,通常配合select()使用,避免User对象中没有被查询到的列值为null
    8. List> maps = userMapper.selectMaps(queryWrapper);
    9. maps.forEach(System.out::println);
    10. }
    • 关于condition判断组织条件:
    1. @Test
    2. public void testQuick3(){
    3. String name = "root";
    4. int age = 18;
    5. QueryWrapper queryWrapper = new QueryWrapper<>();
    6. //判断条件拼接
    7. //当name不为null拼接等于, age > 1 拼接等于判断
    8. //方案2: 拼接condition判断
    9. //每个条件拼接方法都condition参数,这是一个比较运算,为true追加当前条件!
    10. //eq(condition,列名,值)
    11. queryWrapper.eq(!StringUtils.isEmpty(name),"name",name)
    12. .eq(age>1,"age",age);
    13. }

    每个QueryWrapper的条件拼接方法中都可以在参数列表中加上一个布尔类型的参数,这个参数可以加入一个表达式,当这个参数的结果为true时该方法才执行。可以用来动态判断是否要拼接此条件。

    使用queryWrapper + 实体类形式可以实现修改,但是无法将列值修改为null值

    UpdateWrapper的使用

    UpdateWrapper的方法与QueryWrapper的方法类似,但是UpdateWrapper有一个set方法,可以指定数据的相应列做修改,并且可以做到将数据库中的数据修改为null

    1. @Test
    2. public void testQuick2(){
    3. UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    4. //将id = 3 的email设置为null, age = 18
    5. updateWrapper.eq("id",3)
    6. .set("email",null) // set 指定列和结果
    7. .set("age",18);
    8. //如果使用updateWrapper 实体对象写null即可!
    9. int result = userMapper.update(null, updateWrapper);
    10. System.out.println("result = " + result);
    11. }

    LambdaQueryWrapper和LambdaUpdateWrapper的使用

    相比于 QueryWrapper,LambdaQueryWrapper 使用了实体类的属性引用(例如 User::getName、User::getAge),而不是字符串来表示字段名,这提高了代码的可读性和可维护性

    1. LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();
    2. lambdaQueryWrapper.eq(User::getName, "John")
    3. .ge(User::getAge, 18)
    4. .orderByDesc(User::getCreateTime)
    5. .last("limit 10");
    6. List userList = userMapper.selectList(lambdaQueryWrapper);

    LambdaUpdateWrapper也是类似:

    1. @Test
    2. public void testQuick2(){
    3. UpdateWrapper updateWrapper = new UpdateWrapper<>();
    4. //将id = 3 的email设置为null, age = 18
    5. updateWrapper.eq("id",3)
    6. .set("email",null) // set 指定列和结果
    7. .set("age",18);
    8. //使用lambdaUpdateWrapper
    9. LambdaUpdateWrapper updateWrapper1 = new LambdaUpdateWrapper<>();
    10. updateWrapper1.eq(User::getId,3)
    11. .set(User::getEmail,null)
    12. .set(User::getAge,18);
    13. //如果使用updateWrapper 实体对象写null即可!
    14. int result = userMapper.update(null, updateWrapper);
    15. System.out.println("result = " + result);
    16. }

    MyBatis-Plus核心注解

    @TableName注解

    表名的注解,用来指定实体类对应的数据库中的表

    默认以实体类的名字来对应表,忽略大小写,在实体类名和数据表的名字相同时(忽略大小写),可以不写此注解

    当数据库的表名和实体类的命名不同时,在实体类上加上此注解,并在其value属性中指定其实体类对应的表名

    1. @TableName("sys_user") //对应数据库表名
    2. public class User {
    3. private Long id;
    4. private String name;
    5. private Integer age;
    6. private String email;
    7. }

    也可以全局设置前缀,如此在对应实体类对应的数据表时,会先加上前缀,再寻找数据表

    在application.yaml中:

    1. mybatis-plus: # mybatis-plus的配置
    2. global-config:
    3. db-config:
    4. table-prefix: sys_ # 表名前缀字符串

    @TableId

    加在主键上的注解,当表中的主键的列名与实体类中的表示主键的属性名不一致,并不能完成驼峰映射时,可以用其value属性用来指定实体类的主键属性名对应的表中的主键列名

    其type属性用来指定主键策略,即增加数据时,如何让数据库给增加的数据添加主键值。

    属性类型必须指定默认值描述
    valueString""主键字段名
    typeEnumIdType.NONE指定主键类型

    type属性可选值:

    描述
    AUTO数据库 ID 自增 (mysql配置主键自增长)
    ASSIGN_ID(默认)分配 ID(主键类型为 Number(Long )或 String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)
    • 使用AUTO,即使用主键自增长,表的主键要有设置自增长
    • 雪花算法可以随机生成一个不重复的long类型的数字
      • 使用雪花算法,数据库主键要是BIGINT类型的或者是VARCHAR(64)类型的
      • 实体类的主键的属性要是Long类型的

    也可以全局设置主键策略:

    1. mybatis-plus:
    2. configuration:
    3. # 配置MyBatis日志
    4. log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    5. global-config:
    6. db-config:
    7. # 配置MyBatis-Plus操作表的默认前缀
    8. table-prefix: t_
    9. # 配置MyBatis-Plus的主键策略
    10. id-type: auto

    @TableField注解

    加在非主键属性上,当表中的列名与实体类中的属性不一致,并不能完成驼峰映射时,可以用其value属性用来指定实体类的属性名对应的表中的列名

    除此之外,还有一个exit属性,用来表明此属性是否与数据表中的列名对应

    1. @TableName("sys_user")
    2. public class User {
    3. @TableId
    4. private Long id;
    5. @TableField("nickname")
    6. private String name;
    7. private Integer age;
    8. private String email;
    9. }
    属性类型必须指定默认值描述
    valueString""数据表字段名
    existbooleantrue是否为数据库表字段

    MyBatis-Plus拓展

    逻辑删除实现

    逻辑删除,即数据表中有一个列专门用来表示是否被删除,一个行被删除后此列的值便会改为用来表示已删除状态的值,通常,1表示逻辑已删除,0表示逻辑未删除

    前提:在创建数据表时,加一个表示逻辑删除的字段,默认值约束设置为0

    实体类添加逻辑删除属性:

    1. @Data
    2. public class User {
    3. // @TableId
    4. private Integer id;
    5. private String name;
    6. private Integer age;
    7. private String email;
    8. @TableLogic
    9. //逻辑删除字段 int mybatis-plus下,默认 逻辑删除值为1 未逻辑删除 0
    10. private Integer deleted;
    11. }

    还可以全局添加实体类的逻辑删除的属性,如此不用在实体类中声明逻辑删除的属性上加上@TableLogic注解

    1. mybatis-plus:
    2. global-config:
    3. db-config:
    4. logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
    5. logic-delete-value: 1 # 逻辑已删除值(默认为 1)
    6. logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

    设置完逻辑删除后,mybatis-plus中所有的delete语句会更改为update语句,更改其逻辑删除字段的字段值为1

    乐观锁实现

    乐观锁能解决数据并发问题

    MyBatis-Plus使用版本号方式实现乐观锁

    前提:创建数据表时,加上一个表示版本号的字段,默认值为1

    首先,添加版本号更新插件

    1. @Bean
    2. public MybatisPlusInterceptor mybatisPlusInterceptor() {
    3. MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    4. interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    5. return interceptor;
    6. }

    在实体类中加上一个表示版本号的属性,并在这个属性上加上@Version属性

    1. @Version
    2. private Integer version;

    防全表更新和删除操作实现

    针对 update 和 delete 语句 作用: 阻止恶意的全表更新删除

    添加防止全表更新和删除拦截器

    1. @Bean
    2. public MybatisPlusInterceptor mybatisPlusInterceptor() {
    3. MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    4. interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
    5. return interceptor;
    6. }
    7. }

    测试全部更新或者删除

    1. @Test
    2. public void testQuick8(){
    3. User user = new User();
    4. user.setName("custom_name");
    5. user.setEmail("xxx@mail.com");
    6. //Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation
    7. //全局更新,报错
    8. userService.saveOrUpdate(user,null);
    9. }

  • 相关阅读:
    JVM:(八)运行时数据区之方法区
    软件测试岗:阿里三面,幸好做足了准备,已拿offer
    Pandas常用数据结构
    ROS2进阶第三章 -- 机器人语音交互之ros集成科大讯飞中文语音库,实现语音控制机器人小车
    操作系统面试题
    拒绝服务攻击工具
    ACM练习——第一天
    DCM:一个能够改善所有应用数据交互场景的中间件新秀
    MyBatis基础操作
    c++征途 --- 引用
  • 原文地址:https://blog.csdn.net/2302_79468488/article/details/136591707