一篇博客带你学会MyBatis-Plus(后端必会)

知识点

在这里插入图片描述

在这里插入图片描述

lomback的使用

// 代替我们的set、get、无参、toString方法,注意不包含有参构造
@Data
public class User {

    private Long id;
    private String name;
    private Integer age;
    private String email;

}

依赖

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

增删改查

mapper直接继承BaseMapper,里面给我们封装好了各种常用方法。
记得指定泛型实体类。

package com.qcby.mybatis.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qcby.mybatis.model.Person;
import org.apache.ibatis.annotations.Mapper;


public interface PersonMapper extends BaseMapper<Person>{

}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.baomidou.mybatisplus.core.mapper;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;

public interface BaseMapper<T> {
    int insert(T var1);

    int deleteById(Serializable var1);

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

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

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

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

    int update(@Param("et") T var1, @Param("ew") Wrapper<T> var2);

    T selectById(Serializable var1);

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

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

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

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

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

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

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

    IPage<T> selectPage(IPage<T> var1, @Param("ew") Wrapper<T> var2);

    IPage<Map<String, Object>> selectMapsPage(IPage<T> var1, @Param("ew") Wrapper<T> var2);
}

mybaties配置日志

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

在这里插入图片描述

常用注解

转换规则
在这里插入图片描述

如果不符合规则,就需要注解来设置。

@TableName   指定表名  
@TableId     指定主键名  
@TableField  指定普通字段  

一定要加TableField的场景:

  • 布尔类型,名字is开头的一定要注意加TableField
  • 属性名与数据库关键字冲突
  • 成员遍历不是字段 @TableField(exist = false)
    @TableLogic
    private Integer isDeleted;

测试删除功能,真正执行的是修改
UPDATE t_user SET is_deleted=1 WHERE id=? AND is_deleted=0
测试查询功能,被逻辑删除的数据默认不会被查询
SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0

0表示未被删除
1表示被删除了

常用配置

在这里插入图片描述

一般都有默认值,很多不用设置。

id自增,需要在数据库也开启才能使用。

基本增删改查的使用

package com.qcby.springboot.mapper;

import com.qcby.springboot.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.*;

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void test() {
        //selectList()根据MP内置的条件构造器查询一个list集合,null表示没有条件,即查询所有
        userMapper.selectList(null).forEach(System.out::println);
    }

    @Test
    public void insert() {
        User user = new User();
        user.setAge(18);
        user.setName("小宇");
        int insert = userMapper.insert(user);
        System.out.println(insert);
        System.out.println("id = " + user.getId());
    }

    @Test
    public void delById() {
        // 根据id删除
        int insert = userMapper.deleteById(1760628979133440001L);
        System.out.println("影响行号="+insert);

    }

    @Test
    public void delByMap() {

        // 根据map中条件删除
        Map<String, Object> map = new HashMap<>();
        map.put("age", 18);
        map.put("name", "小宇");
        int result = userMapper.deleteByMap(map);
        System.out.println("受影响行数:"+result);

    }

    @Test
    public void delByIds() {
       // 根据ids集合批量删除

        List<Long> idList = Arrays.asList(6L, 7L);
        int result = userMapper.deleteBatchIds(idList);
        System.out.println("受影响行数:"+result);
    }

    @Test
    public void update() {
        User user = new User();

        user.setName("小王");
        user.setId(1L);
        userMapper.updateById(user);
    }

    @Test
    public void selectMp() {

        // 根据对象查询
        User user = userMapper.selectById(1L);
        System.out.println(user);

        // 根据ids集合批量查询
        List<Long> idList = Arrays.asList(1L, 2L);
        userMapper.selectBatchIds(idList).forEach(System.out::println);

        // 根据条件查询
        Map<String, Object> map = new HashMap<>();
        map.put("age", 18);
        map.put("name", "小王");
        userMapper.selectByMap(map).forEach(System.out::println);
    }

}

条件构造器

wrapper用于构造条件语言。
updateWrapper 一些特殊需要setsql拼接的。

    @Test
    void testUpdateWrapper() {
        List<Long> ids = new ArrayList<>();
        ids.add(1L);
        ids.add(2L);
        ids.add(3L);

        UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
                .setSql("balance = balance - 200")
                .in("id", ids);

        userMapper.update(null, wrapper);
    }
package com.qcby.springboot.mapper;

import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.qcby.springboot.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.internal.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserWrapperTest {

    @Autowired
    private UserMapper userMapper;


    @Test
    public void test01() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name", "a")
                .between("age", 20, 30)
                .isNotNull("email");
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }
    
    @Test
    public void test03(){
        //删除email为空的用户
        //DELETE FROM t_user WHERE (email IS NULL)
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.isNull("email");
        //条件构造器也可以构建删除语句的条件
        int result = userMapper.delete(queryWrapper);
        System.out.println("受影响的行数:" + result);
    }

    @Test
    public void test04() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
                .like("name", "a")
                .gt("age", 20)
                .or()
                .isNotNull("email");
        User user = new User();
        user.setAge(18);
        user.setEmail("user@atguigu.com");
        int result = userMapper.update(user, queryWrapper);
        System.out.println("受影响的行数:" + result);
    }

    @Test
    public void test05() {
        QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
                .like("name", "a")
                .gt("age", 20)
                .and(i -> i.gt("age", 20).or().isNull("email"));
        User user = new User();
        user.setAge(18);
        user.setEmail("user@atguigu.com");
        int result = userMapper.update(user, queryWrapper);
        System.out.println("受影响的行数:" + result);
    }

    /**
     * 指定字段查询
     */
    @Test
    public void test06() {

        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("name", "age");
            //selectMaps()返回Map集合列表,通常配合select()使用,避免User对象中没有被查询到的列值 为null
        List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
        maps.forEach(System.out::println);
    }

    /**
     * 子查询
     */
    @Test
    public void test07() {
        //查询id小于等于3的用户信息
        //SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (id IN (select id from t_user where id <= 3))
        QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
                .inSql("id", "select id from t_user where id <= 3");

        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

    /**
     * 修改的时候可以不用单独的场景user对象,直接在updatewrpper上设置
     */
    @Test
    public void test08() {

        UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
                .set("age", 18)
                .set("email", "abc@qq.com")
                .like("name", "a")
                .and(i -> i.gt("age", 20).or().isNull("email"));

        userMapper.update(null, wrapper);
    }

    /**
     * 动态判断
     */
    @Test
    public void test09() {
        //模拟用户传入的条件
        String username = null;
        Integer ageBegin = 10;
        Integer ageEnd = 24;
        QueryWrapper<User> wrapper = new QueryWrapper<User>()
                .like(StringUtils.isNotBlank(username), "name", "a")
                .ge(ageBegin != null, "age", ageBegin)
                .le(ageEnd != null, "age", ageEnd);

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

    }

    /**
     * 动态判断
     */
    @Test
    public void test10() {
        //模拟用户传入的条件
        String username = null;
        Integer ageBegin = 10;
        Integer ageEnd = 24;
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
                .like(StringUtils.isNotBlank(username), User::getName, "a")
                .ge(ageBegin != null, User::getAge, ageBegin)
                .le(ageEnd != null, User::getAge, ageEnd);
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);

    }

    @Test
    public void test11() {
        //组装set子句
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper
                .set(User::getAge, 18)
                .set(User::getEmail, "user@atguigu.com")
                .like(User::getName, "a")
                .and(i -> i.lt(User::getAge, 24).or().isNull(User::getEmail)); //lambda 表达式内的逻辑优先运算
        User user = new User();
        int result = userMapper.update(user, updateWrapper);
        System.out.println("受影响的行数:" + result);
    }
}

自定义sql

在这里插入图片描述

    @Test
    void UpdateSqlWrapper() {
        List<Long> ids = new ArrayList<>();
        ids.add(1L);
        ids.add(2L);
        ids.add(3L);

        int amount = 200;
        QueryWrapper<User> wrapper = new QueryWrapper<User>()
                .in("id", ids);

        userMapper.updateBalanceByIds(wrapper, amount);
    }
public interface UserMapper extends BaseMapper<User>{


    void updateBalanceByIds(@Param("ew") QueryWrapper<User> wrapper, @Param("amount") int amount);
}
    <update id="updateBalanceByIds">
        update user set balance = balance - #{amount} ${ew.customSqlSegment}
    </update>

IService

基本用法

service接口继承IService<实体类>
实现类实现对应service并继承ServiceImpl<对应mapper,实体类>

iUserService:

public interface IUserService extends IService<User>{
}

UserServiceImpl:

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

}

ctrl + p 查看泛型
alt + 回车 建立对应测试

IUserServiceTest:

@SpringBootTest
class IUserServiceTest {

    @Autowired
    private IUserService userService;

    @Test
    void add() {
        User user = new User();
        user.setUsername("小宇");
        user.setPassword("123");
        user.setPhone("18688990011");
        user.setBalance(200);
        user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        userService.save(user);
    }
    @Test
    void ListByIds() {
        List<Long> ids = new ArrayList<>();
        ids.add(1L);
        ids.add(2L);
        ids.add(3L);
        List<User> users = userService.listByIds(ids);
        users.forEach(System.out::println);
    }

}

插件

分页

添加插件

@Configuration
@MapperScan("com.atguigu.mybatisplus.mapper")  //可以将主类中的注解移到此处
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

}

测试:
传入页号和每页大小,自动查询。

    /**
     * 测试分页
     */
    @Test
    public void fy() {
        Page<User> page = new Page<User>(1, 5);

        userMapper.selectPage(page, null);

        //获取分页数据
        List<User> list = page.getRecords();
        list.forEach(System.out::println);
        System.out.println("当前页:"+page.getCurrent());
        System.out.println("每页显示的条数:"+page.getSize());
        System.out.println("总记录数:"+page.getTotal());
        System.out.println("总页数:"+page.getPages());
        System.out.println("是否有上一页:"+page.hasPrevious());
        System.out.println("是否有下一页:"+page.hasNext());
    }

自定义分页

比如查找年龄大于20,并分页。
自己写一个接口,并实现对应的sql。

public interface UserMapper extends BaseMapper<User>{

    public List<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);
}
    <!--SQL片段,记录基础字段-->
    <sql id="BaseColumns">id,name,age,email</sql>

    <!--IPage<User> selectPageVo(Page<User> page, Integer age);-->
    <select id="selectPageVo" resultType="com.qcby.springboot.pojo.User">
        SELECT <include refid="BaseColumns"></include> FROM t_user WHERE age > #{age}
    </select>

测试:

    /**
     * 自定义测试分页
     */
    @Test
    public void zdfy() {
        Page<User> page = new Page<User>(1, 5);

        Integer age = 20;
        userMapper.selectPageVo(page, age);

        //获取分页数据
        List<User> list = page.getRecords();
        list.forEach(System.out::println);
        System.out.println("当前页:"+page.getCurrent());
        System.out.println("每页显示的条数:"+page.getSize());
        System.out.println("总记录数:"+page.getTotal());
        System.out.println("总页数:"+page.getPages());
        System.out.println("是否有上一页:"+page.hasPrevious());
        System.out.println("是否有下一页:"+page.hasNext());
    }

注意事项

  • sql后面不要加;号,因为分页会自动在其后拼接limit。
  • 我们只需要写查询条件,分页会自动帮我们实现。

乐观锁 与 悲观锁

乐观锁

与并发编程相关,即,每次更新都先判断读取的是否是最新数据。
先添加插件:

@Configuration
public class MybatisPlusConfig {

    /**
     * mp分页插件
     * @return
     */
    @Bean
    public MybatisPlusInterceptor  mybatisPlusInterceptor() {

        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        // 添加乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

加上@Version注解。相当于并发编程中的版本号。

import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;

@Data
public class Product {
    private Long id;
    private String name;
    private Integer price;

    @Version
    private Integer version;
}

测试:

@SpringBootTest
public class lockTest {

    @Autowired
    private ProductMapper productMapper;
    /**
     * 演示应用场景
     */
    @Test
    public void productUpdate() {

        // 读取100
        Product productli = productMapper.selectById(1);

        // 读取100
        Product productwang = productMapper.selectById(1);

        //3、小李将价格加了50元,存入了数据库
        productli.setPrice(productli.getPrice() + 50);
        int result1 = productMapper.updateById(productli);
        System.out.println("小李修改结果:" + result1);

        //4、小王将商品减了30元,存入了数据库,会发现更新失败,返回了0,因为版本号对不上
        productwang.setPrice(productwang.getPrice() - 30);
        int result2 = productMapper.updateById(productwang);
        System.out.println("小王修改结果:" + result2);

        //最后的结果
        Product p3 = productMapper.selectById(1L); // 150

        // 若更新失败,重新读取最新数据,再更新
        if(result2 == 0) {
            //失败重试,重新获取version并更新
            productwang = productMapper.selectById(1L);
            productwang.setPrice( productwang.getPrice() - 30);
            result2 = productMapper.updateById( productwang);
        }
        System.out.println("小王修改结果:" + result2);
        System.out.println("最后的结果:" + p3.getPrice()); // 120
    }
}

悲观锁

相当于并发编程中上锁,其他人更新时,不允许操作被上锁的数据。
不再过多赘述,重点是乐观锁。

枚举

在这里插入图片描述

如:用1代表男,2代表女。

配置扫描枚举类型包:
在这里插入图片描述

  • 创建枚举类型
  • 添加@EnumValue注解标识。
import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;

@Getter
public enum SexEnum  {

    MALE(1, "男"),
    FEMALE(2, "女");

    @EnumValue // 这里
    private Integer sex;
    private String sexName;

    SexEnum(Integer sex, String sexName) {
        this.sex = sex;
        this.sexName = sexName;
    }
}

测试:

    @Autowired
    private UserMapper userMapper;
    /**
     * 测试枚举,设置性别
     */
    @Test
    public void testEnums() {
        User user = new User();
        user.setName("Enum");
        user.setAge(20);
        user.setSex(SexEnum.FEMALE); // 直接利用枚举  

        int insert = userMapper.insert(user);

    }

代码生成器

导入依赖:

        <!--逆向工程依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.1</version>
        </dependency>
        <!--模板引擎依赖-->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>

写一个代码生成器的工具类。
配置参数运行,即可生成我们用到的基本的各种代码。
这里我们生成user表的service,mapper,impl等代码。

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.Collections;

public class FastAutoGeneratorTest {
    public static void main(String[] args) {
        FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/mybatis_plus?characterEncoding=utf-8&userSSL=false",
                "root",
                "root")
                //全局配置
                .globalConfig(builder -> {
                    builder.author("qcby") // 设置作者
                            //.enableSwagger() // 开启 swagger 模式
                            .fileOverride() // 覆盖已生成文件
                            .outputDir("E://mybatis_plus"); // 指定输出目录
                })
                //设置包的
                .packageConfig(builder -> {
                    builder.parent("com.qcby") // 设置父包名
                            .moduleName("springboot") // 设置父包模块名
                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "E://mybatis_plus"));
                    // 设置mapperXml生成路径
                })
                //策略的配置
                .strategyConfig(builder -> {
                    builder.addInclude("user"); // 设置需要生成的表名
                            // .addTablePrefix("t_", "c_"); // 设置过滤表前缀 可以设置多个值
                })
                .templateEngine(new FreemarkerTemplateEngine())
                // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                //.execute()执行的意思
                .execute();
    }
}

在这里插入图片描述

多数据源

就是配置多个数据库,一个项目会用到多个数据库。
导入依赖。

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.5.0</version>
</dependency>

当然需要
配置数据库:

spring:
  #配置数据源信息
  datasource:
    #配置数据源的类型
    #type: com.zaxxer.hikari.HikariDataSource
    #配置连接数据库的各个信息
    #驱动类的版本  8.0以上用这个包   5.0以上用com.mysql.jdbc.Driver这个包
    #driver-class-name: com.mysql.cj.jdbc.Driver
    #url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&userSSL=false
    #username: root
    #password: root
    dynamic:
      # 设置默认的数据源或者数据源组,默认值即为master
      primary: master
      # 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源
      strict: true
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf8&useSSL=false
          driver-class-name: com.mysql.jdbc.Driver
          username: root
          password: root
        slave_1:
          url: jdbc:mysql://localhost:3306/mybatis_plus_1?characterEncoding=utf8&useSSL=false
          driver-class-name: com.mysql.jdbc.Driver
          username: root
          password: root
  • 在写接口是,要选择其对应的数据库。
  • 利用@Ds注解

如User接口使用的master配置的数据库。
在这里插入图片描述

在Product接口使用的slave_1配置的数据库。
在这里插入图片描述

就可以正常使用了。

  • 35
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Cosmoshhhyyy

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

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

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

打赏作者

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

抵扣说明:

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

余额充值