Mybatis-Plus快速入门

MyBatisPlus

通过扫描实体类,并基于反射获取实体类信息作为数据库信息

  • 类名驼峰转下划线作为表名
  • 为id的字段作为主键
  • 变量名驼峰转下划线作为表的字段名

遵守这些约定MyBatisPlus就会自动生成字段,方便我们快速实现

一、快速入门

  • 起步依赖

MyBatisPlus官方提供了starter,其中集成了Mybatis和MybatisPlus的所有功能,并且实现了自动装配效果。
因此我们可以用MybatisPlus的starter代替Mybatis的starter:

<!--mybatisplus-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.2</version>
</dependency>
  • 定义Mapper

自定义的Mapper继承Mybatis提供的BaseMapper接口,注意这里泛型需要指定为我们操作的类

public interface UserMapper extends BaseMapper<User> {
}

image-20240409211846507

这里面继承了很多方法,方便我们使用

二、常见注解

常见注解如下

  • @TableName:用来指定表名

    • 用法:类名和表名不一致
  • @Tableld:用来指定表中的主键字段信息

    • 用法:主键id和数据库中不一致

    • type字段 设置主键的策略(这里需要指定不然默认雪花算法自动生成)

      image-20240409213618092

      image-20240409213529666

  • @TableField:用来指定表中的普通字段信息

    • 用法:普通字段和数据库中不一致

    • 注意

      • 字段为 is开头的类型必须要指定否则mybatis会自动舍去掉is
      • 关键字冲突的必须要加上``

      image-20240409214009506

      • 数据库中不存在的字段需要标记

      image-20240409214128254

image-20240409214030267

三、常见配置

MyBatisPlus的配置项继承了MyBatis原生配置和一些自己特有的配置。

mybatis-plus:
  #别名扫描包
  type-aliases-package:
  #xml文件地址,默认值
  mapper-locations:
  configuration:
    map-underscore-to-camel-case: true #是否开启下划线和驼峰的映射
    cache-enabled: false #是否开启二级缓存
  global-config:
    db-config:
      id-type: assign_id #id为雪花算法生成
      update-strategy: not_null #更新策略:只更新非空字段

四、条件构造器

MyBatisPlus支持各种复杂的where条件,可以满足日常开发的所有需求。

image-20240409215917872

QueryWrapper

继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件

例子:查出名字带o,且存款大于等于1000的id,username,info

        //构建查询条件
        QueryWrapper<User> wrapper = new QueryWrapper<User>()
                .select("id","username","info","balance")
                .like("username","o")
                .ge("balance",1000);

        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);

例子:更新用户名为jack的余额为2000

        //要更新的数据
        User user = new User();
        user.setBalance(2000);
        //构建更新条件
        QueryWrapper<User> wrapper = new QueryWrapper<User>()
                .eq("username","jack");
        userMapper.update(user,wrapper);

UpdateWrapper

继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件

例子:更新id为1,2,4的用户的余额,扣200

        List<Long> ids = List.of(1L, 2L, 4L);
        //构建更新条件
        UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
                .setSql("balance = balance - 200")
                        .in("id",ids);
        userMapper.update(null,wrapper);

LambdaWrapper

解决硬编码问题,推荐使用

        //构建查询条件
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
                .select(User::getId,User::getUsername,User::getInfo,User::getBalance)
                .like(User::getUsername,"o")
                .ge(User::getBalance,1000);

        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);

五、自定义SQL

我们可以利用MyBatisPlus的Wrapper来构建复杂的Where条件,然后自己定义SQL语句中剩下的部分。

  • 基于Wrapper构建where条件

image-20240409231737282

  • 在mapper方法参数中用Param注解声明wrapper变量名称,必须是ew

image-20240409231756086

  • 自定义SQL并使用Wrapper条件

image-20240409231809288

六、Service接口

MybatisPlus也提供了大量的service接口以便我们自己来使用

image-20240410084047901

  • 根据上图,我们可以看到,我们继承它给我们提供的Impl和我们自己实现的接口,注意红色框中需要指定泛型

userService

image-20240410084802161

userServiceImpl

image-20240410084844807

6.1、普通实现

那么我们简单的业务逻辑就可以直接在Controller中实现进行

    /**
     * 新增用户
     * @param userFormDTO
     */
    @PostMapping
    @ApiOperation("新增用户 ")
    public void addUser(@RequestBody UserFormDTO userFormDTO){
        //拷贝
        User User = new User();
        BeanUtils.copyProperties(userFormDTO,User);
        //执行新增
        userService.save(User);
    }

    @DeleteMapping("/{id}")
    @ApiOperation("删除用户 ")
    public void deleteUser(@ApiParam("用户id") @PathVariable Long id){
        //执行删除
        userService.removeById(id);
    }

当业务逻辑比较复杂的时候,我们就需要自己写。

当baseMapper提供的方法也不足以满足我们的需求,也需要自己写

6.2、lambda

查询

如果我们需要实现一个复杂的条件,例如下面这样

image-20240410102447654

按照xml中我们就应该写成下面这种

image-20240410102253322

而我们可以直接在serviceImpl中直接使用lambda快速构建

image-20240410102351779

更新

image-20240410102654775

image-20240410103507740

这里必须要加update否则语句不会执行

6.3、批处理新增

如果在使用mysql数据库的时候,遇到批处理,MySQL的客户端连接参数中有这样的一个参数:rewriteBatchedStatements

修改项目中的application.yml文件,在jdbc的url后面添加参数&rewriteBatchedStatements=true:

然后使用mybatis的批处理,让性能达到最好

@Test
void testSaveBatch() {
    // 准备10万条数据
    List<User> list = new ArrayList<>(1000);
    long b = System.currentTimeMillis();
    for (int i = 1; i <= 100000; i++) {
        list.add(buildUser(i));
        // 每1000条批量插入一次
        if (i % 1000 == 0) 
            //预编译sql,提升速度
            userService.saveBatch(list);
            list.clear();
        }
    }
    long e = System.currentTimeMillis();
    System.out.println("耗时:" + (e - b));
}

七、扩展功能

7.1、代码生成器

这里没有使用官方给我们提供的,我们直接使用插件

image-20240410105229468

在Idea顶部菜单中,找到other,选择Config Database配置数据库地址

在弹出的窗口中填写数据库连接的基本信息:

jdbc:mysql://localhost:3306/xx?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC

image-20240410111629844

然后再次点击Idea顶部菜单中的other,然后选择Code Generator:

image-20240410105354303

然后就会自动生成我们需要的代码

7.2、静态工具

有的时候Service之间也会相互调用,为了避免出现循环依赖问题,MybatisPlus提供一个静态工具类:Db,其中的一些静态方法与IService中方法签名基本一致,也可以帮助我们实现CRUD功能:

        List<Address> addresses = Db.lambdaQuery(Address.class).eq(Address::getId, id).list();

7.3、逻辑删除

逻辑删除就是基于代码逻辑模拟删除效果,但并不会真正删除数据。思路如下:

  • 在表中添加一个字段标记数据是否被删除
  • 当删除数据时把标记置为1
  • 查询时只查询标记为0的数据

image-20240410152322086

增删改查那么我们都需要加上判断是否该数据处于删除状态

那么mybatisplus给我们提供了方法

只对自动注入的 sql 起效:

  • 插入: 不作限制
  • 查找: 追加 where 条件过滤掉已删除数据,如果使用 wrapper.entity 生成的 where 条件也会自动追加该字段
  • 更新: 追加 where 条件防止更新到已删除数据,如果使用 wrapper.entity 生成的 where 条件也会自动追加该字段
  • 删除: 转变为 更新

7.3.1、使用方法

  • 例: application.yml
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
  • 实体类字段上加上@TableLogic注解
@TableLogic
private Integer deleted;

7.3.2、注意

逻辑删除本身也有自己的问题,比如:

  • 会导致数据库表垃圾数据越来越多,影响查询效率.
  • SQL中全都需要对逻辑删除字段做判断,影响查询效率

因此,我不太推荐采用逻辑删除功能,如果数据不能删除,可以采用把数据迁移到其它表的办法。

7.4、枚举处理器

解决了繁琐的配置,让 mybatis 优雅的使用枚举属性!

  • 定义枚举类

image-20240410155710303

要让MybatisPlus处理枚举与数据库类型自动转换,我们必须告诉MybatisPlus,枚举中的哪个字段的值作为数据库值。 MybatisPlus提供了@EnumValue注解来标记枚举属性:

image-20240410155739980

@JsonValue注解表示标记JSON序列化时展示的字段

image-20240410155900555

7.5、JSON处理器

数据库的user表中有一个info字段,是JSON类型:

image-20240410160843197

{"age": 20, "intro": "佛系青年", "gender": "male"}

而目前User实体类中却是String类型:

image-20240410160857442

这样一来,我们要读取info中的属性时就非常不方便。如果要方便获取,info的类型最好是一个Map或者实体类

因此MybatisPlus提供了很多特殊类型字段的类型处理器,解决特殊字段类型与数据库类型转换的问题。例如处理JSON就可以使用JacksonTypeHandler处理器。

方法

  • 首先,我们定义一个单独实体类来与info字段的属性匹配:

image-20240410160927905

package com.itheima.mp.domain.po;

import lombok.Data;

@Data
public class UserInfo {
    private Integer age;
    private String intro;
    private String gender;
}
  • 接下来,将User类的info字段修改为UserInfo类型,并声明类型处理器:

image-20240410160947131

八、插件功能

8.1、分页插件

在项目中新建一个配置类:

image-20240410161629596

@Configuration
@MapperScan("scan.your.mapper.package")
public class MybatisPlusConfig {

    /**
     * 添加分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        //初始化核心插件
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //添加分页插件
        PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        pageInterceptor.setMaxLimit(1000L);//设置分页上线
        interceptor.addInnerInterceptor(pageInterceptor);//如果配置多个插件,切记分页最后添加

        //interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 如果有多数据源可以不配具体类型 否则都建议配上具体的DbType
        return interceptor;
    }
}

编写一个分页查询的测试:

@Test
void testPageQuery() {
        // 1.分页查询,new Page()的两个参数分别是:页码、每页大小
        int pageNo =1,pageSize = 2;
        Page<User> page = Page.of(pageNo, pageSize);
        //设置排序条件
        page.addOrder(new OrderItem("balance",true));
        //2.分页查询
        Page<User> p = userService.page(page);

        // 总条数
        System.out.println("total = " + p.getTotal());
        // 总页数
        System.out.println("pages = " + p.getPages());
        // 数据
        List<User> records = p.getRecords();
        records.forEach(System.out::println);
}
  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周粥粥ya

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值