MyBatis-Plus的入门学习

简介

MyBatis-Plus是MyBatis的增强工具,在MyBatis的基础上做出增强改变,简化开发,提高效率

mybatis-plus的官网链接

特性

mybatis-plus官网的特性详解

快速开始

第一步:创建一张表

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');

第三步:初始化一个项目工程

在这里插入图片描述

第四步:引入依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0+ 版本</version>
    <relativePath/>
</parent>



<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>最新版本</version>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

第五步:配置数据库连接信息

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis-plus?
serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root

第五步:在SpringBoot项目的启动类中添加**@MapperScan注解**,用于对mapper的扫描

package com.example.mpdemo001;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.example.mpdemo001.mapper")
public class Mpdemo001Application {
    public static void main(String[] args) {
        SpringApplication.run(Mpdemo001Application.class, args);
    }

}

第六步:编码

实体类:

package com.example.mpdemo001.bean;

import lombok.Data;

/**
 * @Title: User
 * @Package com.example.mpdemo001.bean
 * @Author: CXY
 * @Copyright CXY
 * @CreateTime: 2023/3/9 12:35
 */
@Data
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

mapper接口(MyBatis-Plus不需要在接口中写很多方法,只需要继承 BaseMapper< T>即可,T表示泛型

package com.example.mpdemo001.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.mpdemo001.bean.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

**注意:**在mapper接口上面需要添加一个@Mapper的注解,用于将Mapper接口加入Spring管理

第七步:测试

package com.example.mpdemo001;

import com.example.mpdemo001.bean.User;
import com.example.mpdemo001.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class Mpdemo001ApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void contextLoads() {
        List<User> users = userMapper.selectList(null);
        for (User user:users
             ) {
            System.out.println(user);
        }


    }

}

UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper,所以不填写就是无任何条件

测试结果:

User(id=1, name=Jone, age=18, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=test2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
User(id=5, name=Billie, age=24, email=test5@baomidou.com)

可以配置MyBatis-Plus的日志文件,这样可以看到详细的SQL语句实现过程

日志文件的配置如下:

#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

MyBatis-Plus的注解详解

@Tableld

IdType:顾名思义即表示主键的类型

主键生成策略

参考资料:分布式系统唯一ID生成方案汇总:https://www.cnblogs.com/haoxinyue/p/5208136.htm

1、数据库自动增长 AUTO

这个是指在建表的时候可以直接设置id自增,这样的话idType即为AUTO;

**缺点:**这样id自增的主键,不利于数据库的分表实现,因为id是按顺序递增的,所以每一次分表的时候都需要知道前一张表的最后一个id的值,再那个基础上进行id加一的操作

2、UUID

每次生成随机唯一的值

**优点:**每次分表的时候不需要知道上一张表的最后一个元素的id值

**缺点:**id无法进行排序

3、Redis生成id

Redis是单线程的,所以也可以用来生成全局唯一的id,可以用Redis的原子操作INCRINCRBY来实现

但是使用这个来进行id生成的时候,需要指定初始值和步长

使用Redis集群可以防止单点故障的问题

单点故障:(英语:single point of failure,缩写SPOF)是指系统中一点失效,就会让整个系统无法运作的部件,换句话说,单点故障即会整体故障。

**优点:**不依赖于数据库,灵活方便,且性能优于数据库,数字id天然排序,对分页或者需要排序的结果很有帮助

**缺点:**如果系统中没有Redis,还需引入新的主键,增加系统的复杂度;需要编码和配置的工作量比较大

4、MP主键自动生成

mp可以通过雪花算法自动生成一个19位数字的id,

@TableName

  • 描述:表名注解,标识实体类对应的表
  • 使用位置:实体类

在这里插入图片描述

@TableField

  • 描述:字段注解(非主键)
自动填充

将数据自动填充进去

项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。 我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作

具体实现过程:

在实体类中需要自动进行自动填充的对象上面添加注解:**@TableField(fill=FieldFill.Xxx)**FieldFill的取值:

在这里插入图片描述

然后创建一个类用于实现MetaObjectHandler接口,实现接口中的方法,

注意:需要在这个类上面添加注解,让Spring进行管理

package com.example.mpdemo001.bean;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;

import java.util.Date;

/**
 * @Title: MyBatisMeta
 * @Package com.example.mpdemo001.bean
 * @Author: CXY
 * @Copyright CXY
 * @CreateTime: 2023/3/10 14:02
 */
@Component
//这个不能忘,忘了就无法进行了
public class MyBatisMeta implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        //插入时间,第一次创建的时候,需要插入创建时间和更新时间
        this.setFieldValByName("createTime",new Date(),metaObject);
        this.setFieldValByName("updateTime",new Date(),metaObject);
        
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        //每一次更新数据的时候,插入时间
        this.setFieldValByName("updateTime",new Date(),metaObject);
        

    }
}

测试结果:

在这里插入图片描述

测试方法:

update

@Test
public void testUpdateById(){
    User user=new User();
    user.setId(1L);
    user.setName("战三");
    int i = userMapper.updateById(user);
    System.out.println(i);

}

结果:
在这里插入图片描述

注意:update生成的是动态sql

乐观锁

针对一种特定问题的解决方案,主要解决的是丢失更新的问题

主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数 据更新

如果不考虑事务的隔离性的话,会产生的问题有:

1、产生读问题:脏读,不可重复读,幻读

2、产生写问题:丢失更新(就是指多个人同时修改同一条数据,最后提交的把之前提交的数据覆盖了

解决丢失更新问题:

悲观锁:串行,只有一个人能在当前情况下进行操作

乐观锁:添加了一个version版本号,当一个人在进行数据操作的时候,提交数据时会比较当前数据版本号和数据库中的版本号是否一直,如果一致则可以进行修改,修改后版本号进行加一的操作。反之不能进行修改。

**乐观锁的使用例子有:**12306抢票系统,当别人把最后一张票抢走之后,另外的人就不能再进行抢票了

乐观锁的使用流程:

  1. 取出记录时,获取当前version
  2. 更新时,带上这个version
  3. 执行更新时, set version = newVersion where version = oldVersion
  4. 如果version不对,就更新失败

当有很多配置的时候,建议新建一个配置类,将所有配置写在配置类中

package com.example.mpdemo001.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Title: MyBatisConfigurations
 * @Package com.example.mpdemo001.config
 * @Author: CXY
 * @Copyright CXY
 * @CreateTime: 2023/3/10 15:12
 */
@Configuration
@MapperScan("com.example.mpdemo001.mapper")
public class MyBatisConfigurations {
    /**
     * 乐观锁插件
    *
     * 新版
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }

    }

测试成功代码:

@Test
public void testOptimisticLockerFail() {
    User user = userMapper.selectById(1l);
    user.setName("死亡法师");
    user.setAge(10000);
 
    int i = userMapper.updateById(user);
    System.out.println(i);
}

测试成功的结果

在这里插入图片描述

测试失败代码:

@Test
public void testOptimisticLockerFail() {
    User user = userMapper.selectById(2l);
    user.setName("哈利波特");
    user.setAge(300);
    //模拟取出数据后,数据库中version实际数据比取出的值大,即已被其它线程修改并更新了version
    user.setVersion(user.getVersion()-1);
    int i = userMapper.updateById(user);
    System.out.println(i);
}

结果:

在这里插入图片描述

select

查所有
@Test
public void testSelect(){
    List<User> users = userMapper.selectList(null);
    users.forEach(System.out::println);
}

结果
在这里插入图片描述

根据id查

在这里插入图片描述

多个id批量查询

在这里插入图片描述

简单条件查询(通过map封装条件)

在这里插入图片描述

注意:map中的key对应的是数据库中的列名。例如数据库user_id,实体类是userId,这时map的key需 要填写user_id

分页

MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能

添加插件:

/**
 * 分页插件(官网最新)
 */
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor1() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    return interceptor;
}

测试分页实现:

@Test
public void testPageSelect(){
    Page page=new Page<>(1,3);
    userMapper.selectPage(page, null);
    page.getRecords().forEach(System.out::println);
    System.out.println(page.getCurrent());
    System.out.println(page.getPages());
    System.out.println(page.getSize());
    System.out.println(page.getTotal());
    System.out.println(page.getRecords());


}

测试结果

在这里插入图片描述

分页的各个属性:

在这里插入图片描述

delete

根据id删除
@Test
public void testDeletedById(){
    int i = userMapper.deleteById(4l);
    System.out.println(i);
}
批量删除
@Test
public void testDeletedByIds(){
    int i = userMapper.deleteBatchIds(Arrays.asList(1, 2, 3));
    System.out.println(i);
}
简单的条件查询删除
@Test
public void testDeleteByMap(){
    HashMap<String,Object> hashMap=new HashMap<>();
    hashMap.put("name","Tom");
    hashMap.put("age",28);
    int i = userMapper.deleteByMap(hashMap);
    System.out.println(i);
}
逻辑删除

逻辑删除就是指在数据中多加一个标志属性,删除的时候只对该属性进行修改,而不是真正的删除数据,逻辑删除有利于数据的恢复

  • 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据
  • 逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍 旧能看到此条数据记录

在这里插入图片描述

在实体类上添加deleted属性

@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer deleted;

如果想修改默认属性值,则可在application.properties中进行配置

默认0表示未被删除,1表示被删除

在这里插入图片描述

在配置类中注册bean

@Bean
public ISqlInjector sqlInjector() {
    return new LogicSqlInjector();
}

高版本不用配置插件了,上述java代码无需编写。

测试删除:

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

在这里插入图片描述

被删除数据的deleted 字段的值必须是 0,才能被选取出来执行逻辑删除的操做

性能分析

在配置类中添加性能优化插件

@Profile({“dev”,“test”})表示对什么环境起作用

dev:开发环境

test:测试环境

prod:生产环境,项目部署好之后的环境 最好不要在这里使用性能优化插件

环境配置在配置文件中设置:

spring.profile.active=xxx(dev,test,prod)

**参数:**maxTime: SQL 执行最大时长,超过自动停止运行,有助于发现问题。

**参数:**format: SQL是否格式化,默认false

Wrapper

在这里插入图片描述

  1. Wrapper : 条件构造抽象类,最顶端父类
  2. AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
  3. QueryWrapper : Entity 对象封装操作类,不是用lambda语法(使用较多
  4. UpdateWrapper : Update 条件封装,用于Entity对象更新操作
  5. AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。
  6. LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper
  7. LambdaUpdateWrapper : Lambda 更新封装Wrapper

wrapper条件构造器测试

1、ge、gt、le、lt、isNull、isNotNull
@Test
public void testQueryWrapper(){
    QueryWrapper<User> queryWrapper=new QueryWrapper<>();
    queryWrapper
            .isNull("email")
            .ge("age",30)
            .isNotNull("name");
    //上面代码可以表示多个条件
    int delete = userMapper.delete(queryWrapper);
    System.out.println(delete);
}

gt:大于;ge:大于等于;le:小于;lt:小于等于

2、eq、ne

eq表示等于

ne表示不等于

@Test
public void testSelectOne() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("name", "Tom");
    User user = userMapper.selectOne(queryWrapper);
    System.out.println(user);
}

注意:selectOne方法返回的是一条实体记录,如果返回多条时,会报错

3、between、notBetween

是包含边界值的查询条件

需要注意小值在前,大值在后

@Test
public void testSelectCount() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.between("age", 20, 30);
    Long count = userMapper.selectCount(queryWrapper);
    System.out.println("==============="+count);
}
4、allEq

多条件均相等

@Test
public void testSelectList() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    Map<String, Object> map = new HashMap<>();
    map.put("id", 2);
    map.put("name", "Jack");
    map.put("age", 20);
    queryWrapper.allEq(map);
    List<User> users = userMapper.selectList(queryWrapper);
    users.forEach(System.out::println);
}
5、like、notLike、likeLeft、likeRight

like:模糊查询,表示like %内容%

notLike:不包含

likeLeft:表示like %内容

likeRight:表示 like 内容%

@Test
public void testQuerrySelectMaps() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper
            .notLike("name","san")
            .likeRight("email","test1");
    List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
    maps.forEach(System.out::println);


}
6、in、notIn、inSql、notInSql、exists、notExists

in notIn用法如下:

notIn(“age”,{1,2,3})—>age not in (1,2,3)

notIn(“age”, 1, 2, 3)—>age not in (1,2,3)

inSql、notInSql可以实现子查询

@Test
    public void testSelectObjs() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//queryWrapper.in("id", 1, 2, 3);
        queryWrapper.inSql("id", "select id from user where id < 3");
        List<Object> objects = userMapper.selectObjs(queryWrapper);//返回值是Object列表
        objects.forEach(System.out::println);
    }
7、or、and

不调用or则默认为使用 and 连

8、嵌套or、嵌套and

这里使用了lambda表达式,or中的表达式最后翻译成sql时会被加上圆括号

 @Test
    public void testUpdate2() {
//修改值
        User user = new User();
        user.setAge(99);
        user.setName("Andy");
//修改条件
        UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
        userUpdateWrapper
                .like("name", "h")
                .or(i -> i.eq("name", "李白").ne("age", 20));
//        i -> i.eq("name", "李白").ne("age", 20)这一段是lambda表达式
        int result = userMapper.update(user, userUpdateWrapper);
        System.out.println(result);
    }

最后的sql语句为:

UPDATE user SET name=?, age=?, update_time=? WHERE deleted=0 AND name LIKE ?

OR ( name = ? AND age <> ?)

9、orderBy、orderByDesc、orderByAsc

**orderBy:**默认是升序

**orderByDesc:**表示升序排列,该方法可以有多个条件参与排列

**orderByAsc:**表示降序排列,该方法可以有多个条件参与排列

10、last

直接拼接到 sql 的最后

注意:只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用

@Test
public void testSelectListLast() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.last("limit 1");
    List<User> users = userMapper.selectList(queryWrapper);
    users.forEach(System.out::println);
}

sql语句:SELECT id,name,age,email,create_time,update_time,deleted,version FROM user WHERE deleted=0 limit 1

11、set、setSql

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

@Test
    public void testUpdateSet() {
//修改值
        User user = new User();
        user.setAge(99);
//修改条件
        UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
        userUpdateWrapper
                .like("name", "h")
                .set("name", "老李头")//除了可以查询还可以使用set设置修改的字段
                .setSql(" email = '123@qq.com'");//可以有子查询
        int result = userMapper.update(user, userUpdateWrapper);
    }

sql语句:

UPDATE user SET age=?, update_time=?, name=?, email = ‘123@qq.com’ WHERE deleted=0 AND name LIKE ?

b站教学视频:
尚硅谷

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值