0
点赞
收藏
分享

微信扫一扫

Mybatis-Plus入门

做个橙梦 2022-01-06 阅读 46

1 MP入门

1.1 创建SpringBoot项目

将springboot项目版本改为2.2.1.RELEASE

请添加图片描述

1.2 引入依赖

<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>

<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<!--lombok用来简化实体类-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

1.3 配置数据库相关信息

如果是jdbc5

#mysql数据库连接
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root

如果是jdbc8

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

1.4 构建实体类

@Data
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

1.5 构建Mapper

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

让UserMapper继承BaseMapper,并且指定实体类泛型为User,这样子就会对User进行CURD。我们可以进入BaseMapper进行查看

public interface BaseMapper<T> extends Mapper<T> {
    int insert(T entity);

    int deleteById(Serializable id);

    int deleteByMap(@Param("cm") Map<String, Object> columnMap);

    int delete(@Param("ew") Wrapper<T> wrapper);

    int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    int updateById(@Param("et") T entity);

    int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);

    T selectById(Serializable id);

    List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);

    List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);

    T selectOne(@Param("ew") Wrapper<T> queryWrapper);

    Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);

    List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);

    List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);

    List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);

    <E extends IPage<T>> E selectPage(E page, @Param("ew") Wrapper<T> queryWrapper);

    <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param("ew") Wrapper<T> queryWrapper);
}

这是BaseMapper的方法,可以看到里面有许多CURD的方法,自定义的Mapper继承它,也就继承它的方法。

1.6 SpringBootApplication修改

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

}

添加MapperScan,里面填写mapper的路径

1.7 测试(test)

@SpringBootTest
public class UserMapperTest {
    @Autowired
    private UserMapper userMapper;

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

}

结果

[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)]

我们可以在在application.properties添加配置,进行配置mybatisplus的日志输出

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

再次执行,结果为:
请添加图片描述

1.8 Mybatisplus实现添加操作(insert)

  @Test
    public void test2(){
        User user = new User();
        user.setAge(10);
        user.setEmail("974914773@qq.com");
        user.setName("zjh");
        int result = userMapper.insert(user);
        System.out.println(result);
    }

结果

请添加图片描述
我们可以看到代码中,我们没有设置id值,mp给我们自动设置了id值。

1.9 MybatisPlus的主键策略

public enum IdType {
    AUTO(0), 自动递增
    NONE(1), 空值,也需要你手动设置
    INPUT(2), 需要自己手动set值,无法自动生成
    ASSIGN_ID(3), 雪花算法,生成19位的唯一值
    ASSIGN_UUID(4), UUID
    /** @deprecated */
    @Deprecated
    ID_WORKER(3), 过时了
    /** @deprecated */
    @Deprecated
    ID_WORKER_STR(3),过时了
    /** @deprecated */
    @Deprecated
    UUID(4);过时了

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

1.9.1 雪花算法

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

雪花算法:分布式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碰撞,并且效率较高。

1.9.2 AUTO递增

@Data
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

执行结果
请添加图片描述
注意:使用自增时,需要在数据库的字段上设置自增

1.10 MybatisPlus修改操作

  @Test
    public void test3(){
        User user = new User();
        user.setAge(10);
        user.setEmail("974914773@qq.com");
        user.setName("zjhhhhhh");
        int result = userMapper.updateById(user);
        System.out.println(result);
    }

看到结果为0
请添加图片描述
因为我们没有显示的设置id值

   @Test
    public void test3(){
        User user = new User();
        user.setAge(10);
        user.setEmail("974914773@qq.com");
        user.setName("zjhhhhhh");
        user.setId(6L);
        int result = userMapper.updateById(user);
        System.out.println(result);
    }

请添加图片描述

1.11 MybatisPlus的自动填充

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

1.11.1 数据库修改

在User表中添加datetime类型的新的字段 create_time、update_time

1.11.2 实体类修改

实体上增加字段并添加自动填充注解

@TableField(fill = FieldFill.INSERT)
private Date createTime;  //create_time

@TableField(fill = FieldFill.INSERT_UPDATE) // 这是插入也执行,修改也执行
private Date updateTime; //update_time

1.11.3 实现元对象处理器接口

注意:不要忘记添加 @Component 注解

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    //mp执行添加操作,这个方法执行
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime",new Date(),metaObject);
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }

    //mp执行修改操作,这个方法执行
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}

1.11.4 执行代码

 @Test
    public void test3(){
        User user = new User();
        user.setAge(101);
        user.setEmail("974914773@qq.com");
        user.setName("zjhhhhhh");
        user.setId(6L);
        int result = userMapper.insert(user);
        System.out.println(result);
    }

结果
请添加图片描述

@Test
    public void test3(){
        User user = new User();
        user.setAge(101);
        user.setEmail("974914773@qq.com");
        user.setName("zjhhhhhh");
        user.setId(6L);
        int result = userMapper.updateById(user);
        System.out.println(result);
    }

请添加图片描述

2. MyabtisPlus实现乐观锁

2.1 场景

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

2.2 乐观锁实现方式

  1. 取出记录时,获取当前version
  2. 更新时,带上这个version
  3. 执行更新时, set version = newVersion where version = oldVersion
  4. 如果version不对,就更新失败
  5. 接下来介绍如何在Mybatis-Plus项目中,使用乐观锁:

2.3 乐观锁实现流程

2.3.1 修改实体类

添加 @Version 注解

@Version
private Integer version;

2.3.2 创建配置文件

创建包config,创建文件MybatisPlusConfig.java
此时可以删除主类中的 @MapperScan 扫描注解

@Configuration
@MapperScan("com.zjh.mybaitsplus.mapper")
public class MpConfig {
    /**
     * 乐观锁插件
     */
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
}

2.3.3 注册乐观锁插件

在 MybatisPlusConfig 中注册 Bean

/**
* 乐观锁插件
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}

2.3.4 让version进行变化

在刚刚插入的时候给version默认赋值为1

 	@Version
    @TableField(fill = FieldFill.INSERT)
    private Integer version; // 乐观锁版本号

执行插入

  @Test
    public void test2(){
        User user = new User();
        user.setAge(10);
        user.setEmail("974914773@qq.com");
        user.setName("zjh");
        int result = userMapper.insert(user);
        System.out.println(result);
    }

结果
请添加图片描述
我们进行修改id为11的数据

   @Test
    public void test3(){
        User user = new User();
        user.setAge(000);
        user.setEmail("974914773@qq.com");
        user.setName("mmm");
        user.setId(11L);
        int result = userMapper.updateById(user);
        System.out.println(result);
    

结果
请添加图片描述
可以看到,其余值都变了,就是version的值还是1,斌没有自动自增

因为mp中如果需要version自增,需要先查出来,获取了version后,才能进行递增

@Test
    public void test3(){
        User user = userMapper.selectById(11L);
        user.setName("zjh");
        int result = userMapper.updateById(user);
        System.out.println(result);
    }

结果
请添加图片描述
可以看到,上述代码的修改名字成功以及version也超过自增

2.3.5 测试乐观锁

@Test
    public void test3() throws InterruptedException {
        User user = userMapper.selectById(11L);
        user.setName("测试乐观锁");
        System.out.println("拿到version");
        Thread.sleep(10000);
        System.out.println("开始执行修改");
        int result = userMapper.updateById(user);
        System.out.println(result);
    }

我们在控制台输出拿到version的时候,用户线程sleep10s,我们在此期间修改数据库的version来模拟多线程的情况下,代码是否会成功修改name为测试乐观锁

  1. 开始测试前的数据
    请添加图片描述
  2. 执行代码后修改version
    请添加图片描述
  3. 代码执行结束
    请添加图片描述
    请添加图片描述
  4. 由此可判断乐观锁成功执行

3. Mybatis的查询操作

3.1 批量查询

ArrayList list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);

        List users = userMapper.selectBatchIds(list);
        System.out.println(users);

请添加图片描述

3.2 简单的条件查询

例如现在进行查询名称为zjh,年龄为0的条件进行查询

HashMap map = new HashMap();
        map.put("name","zjh");
        map.put("age", 0);

        List users = userMapper.selectByMap(map);
        System.out.println(users);

请添加图片描述

这种方式不常用,下面会进行wapper的方式进行查询

3.3 分页查询

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

3.3.1 添加分页插件

配置类中添加@Bean配置

/**
 * 分页插件
 */
@Bean
public PaginationInterceptor paginationInterceptor() {
    return new PaginationInterceptor();
}

3.3.2 测试selectPage分页

测试:最终通过page对象获取相关数据

  1. 创建page对象(里面有两个参数,当前页,每页记录数)
  2. 调用mp的方法实现分页
@Test
    public void testSelectPage() {
        Page<User> page = new Page(1,3);
        Page<User> userPage = userMapper.selectPage(page, null); // 第二个参数为wapper查询条件
        //返回对象得到分页所有数据
        long pages = userPage.getPages(); //总页数
        long current = userPage.getCurrent(); //当前页
        List<User> records = userPage.getRecords(); //查询数据集合
        long total = userPage.getTotal(); //总记录数
        boolean hasNext = userPage.hasNext();  //下一页
        boolean hasPrevious = userPage.hasPrevious(); //上一页

        System.out.println(pages);
        System.out.println(current);
        System.out.println(records);
        System.out.println(total);
        System.out.println(hasNext);
        System.out.println(hasPrevious);
    }

请添加图片描述

3.3.3 测试selectMapsPage分页

当指定了特定的查询列时,希望分页结果列表只返回被查询的列,而不是很多null值
测试selectMapsPage分页:结果集是Map

@Test
public void testSelectMapsPage() {
//Page不需要泛型
Page<Map<String, Object>> page = newPage<>(1, 5);
Page<Map<String, Object>> pageParam = userMapper.selectMapsPage(page, null);
List<Map<String, Object>> records = pageParam.getRecords();
records.forEach(System.out::println);
System.out.println(pageParam.getCurrent());
System.out.println(pageParam.getPages());
System.out.println(pageParam.getSize());
System.out.println(pageParam.getTotal());
System.out.println(pageParam.hasNext());
System.out.println(pageParam.hasPrevious());
}

请添加图片描述

4. 删除和逻辑删除

4.1 根据id进行删除

@Test
public void testDeleteById(){
    int result = userMapper.deleteById(5L);
system.out.println(result);
}

请添加图片描述

4.2 批量删除

@Test
public void testDeleteBatchIds() {
    int result = userMapper.deleteBatchIds(Arrays.asList(8, 9, 10));
system.out.println(result);
}

请添加图片描述

4.3 简单条件删除

@Test
public void testDeleteByMap() {
HashMap<String, Object> map = new HashMap<>();
map.put("name", "zjhhhhhh");
map.put("age", 10);
    int result = userMapper.deleteByMap(map);
system.out.println(result);
}

4.4 逻辑删除

4.4.1 物理删除和逻辑删除

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

逻辑删除的使用场景:

  1. 可以进行数据恢复
  2. 有关联数据,不便删除

4.4.2 逻辑删除实现流程

4.4.2.1 数据库修改

添加 deleted字段

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

4.4.2.2 实体类修改

添加deleted 字段,并加上 @TableLogic 注解 \

4.4.2.3 配置(可选)

application.properties 加入以下配置,此为默认值,如果你的默认值和mp默认的一样,该配置可无

mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0

4.4.2.4 测试

测试后发现,数据并没有被删除,deleted字段的值由0变成了1

测试后分析打印的sql语句,是一条update

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

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

请添加图片描述

4.4.2.5 测试逻辑删除后的查询

MyBatis Plus中查询操作也会自动添加逻辑删除字段的判断

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

请添加图片描述

5. 条件构造器和常用接口(wapper)

5.1 wapper介绍

在这里插入图片描述
Wrapper : 条件构造抽象类,最顶端父类
AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
QueryWrapper : 查询条件封装
UpdateWrapper : Update 条件封装
AbstractLambdaWrapper : 使用Lambda 语法
LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
LambdaUpdateWrapper : Lambda 更新封装Wrapper

5.2 测试用例

5.2.1 ge(大于)、gt(大于等于)、le(小于)、lt(小于等于)、isNull、isNotNull

@Test
public void testQuery() {
QueryWrapper<User>queryWrapper = newQueryWrapper<>();
queryWrapper
        .isNull("name")
        .ge("age", 12)
        .isNotNull("email");
    int result = userMapper.delete(queryWrapper);
System.out.println("delete return count = " + result);
}

5.2.2 eq(等于)、ne(不等于)

注意:seletOne()返回的是一条实体记录,当出现多条时会报错

@Test
public void testSelectOne() {
QueryWrapper<User>queryWrapper = newQueryWrapper<>();
queryWrapper.eq("name", "Tom");
Useruser = userMapper.selectOne(queryWrapper);//只能返回一条记录,多余一条则抛出异常
System.out.println(user);
}

5.2.3 between、notBetween

包含大小边界

@Test
public void testSelectCount() {
QueryWrapper<User>queryWrapper = newQueryWrapper<>();
queryWrapper.between("age", 20, 30);
    Integer count = userMapper.selectCount(queryWrapper); //返回数据数量
System.out.println(count);
}

5.2.4 like、notLike、likeLeft、likeRight

selectMaps()返回Map集合列表,通常配合select()使用

@Test
public void testSelectMaps() {
QueryWrapper<User>queryWrapper = newQueryWrapper<>();
queryWrapper
        .select("name", "age")
        .like("name", "e")
        .likeRight("email", "5");
List<Map<String, Object>>maps = userMapper.selectMaps(queryWrapper);//返回值是Map列表
maps.forEach(System.out::println);
}

5.2.5 orderBy、orderByDesc、orderByAsc

@Test
public void testSelectListOrderBy() {
QueryWrapper<User>queryWrapper = newQueryWrapper<>();
queryWrapper.orderByDesc("age", "id");
List<User>users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

5.2.6 查询方式 说明

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”)

举报

相关推荐

0 条评论