MyBatis-Plus学习记录

目录

MyBatis-Plus快速入门

简介

快速入门

MyBatis-Plus核心功能

基于Mapper接口 CRUD

对比mybatis和mybatis-plus:

CRUD方法介绍:

基于Service接口 CRUD

对比Mapper接口CRUD区别:

为什么要加强service层:

使用方式

CRUD方法介绍:

分页查询实现

MyBatis和Mybatis-Plus分页查询

使用分页查询

自定义的mapper方法使用分页

条件构造器使用

1、条件构造器作用

2、条件构造器继承结构

3、基于QueryWrapper组装条件

4、基于UpdateWrapper组装条件

5、基于LambdaQueryWrapper组装条件

LambdaQueryWrapper对比QueryWrapper

6、基于LambdaUpdateWrapper组装条件

核心注解使用

@TableName

@TableId

@TableField

MyBatis-Plus高级拓展

逻辑删除实现

逻辑删除全局配置:

乐观锁实现

场景:

资源争抢的解决方法:

理解点:

具体技术和方案:

版本号乐观锁的实现流程:

使用mybatis-plus数据使用乐观锁:

防全表更新和删除实现

MyBatis-Plus代码生成器(MyBatisX 插件)

MyBatisX插件逆向工程

MyBatisX快速代码生成


MyBatis-PlusMyBatis-Plus 官方文档icon-default.png?t=N7T8https://baomidou.com/?spm=wolai.workspace.0.0.330e767bDepZBf

MyBatis-Plus快速入门

简介

MyBatis-Plus(MP)是一个MyBatis的增强工具,在MyBatis的基础上做增强,简化开发。

支持MySQL,Oracle等大部分数据库。

MyBatis-Plus的功能总的来说就是:

        自动生成单表的CRUD功能

        提供丰富的条件拼接方法

        全自动ORM类型持久层框架

(全自动orm思维持久层框架如hibernate,只需要把数据库数据和java实体类映射(配置),它就能提供crud方法,而且会自动生成对应的sql语句。)

 MyBatis-Plus的功能仅限于单表操作,多表操作还是自己写SQL比较好。

快速入门

1.准备数据库表:database:mybatis_plus,table:user



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

2.创建boot工程:springboot-mybatis-plus

3.导入依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.5</version>
    </parent>

    <groupId>com.qiu</groupId>
    <artifactId>springboot-mybatis-plus</artifactId>
    <version>1.0-SNAPSHOT</version>
    <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>
        </dependency>

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

        <!-- 数据库相关配置启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <!-- druid启动器的依赖  -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-3-starter</artifactId>
            <version>1.2.18</version>
        </dependency>

        <!-- 驱动类-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.28</version>
        </dependency>

    </dependencies>


    <!--    SpringBoot应用打包插件-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

(如果druid版本在1.2.20以下会报错,解决方法在之前springboot文章中)

4.创建pojo类

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

5.创建mapper接口,并继承mybatis-plus提供的Mapper接口,此接口下自带crud方法

public interface UserMapper extends BaseMapper<User> {
}

6.编写测试类进行测试(导入了spring-boot-starter-test启动器,只需要在测试类添加@SpringBootTest就可以直接注入组件)

@SpringBootTest//springboot下测试环境注解
public class BootTest {
    @Autowired
    private UserMapper userMapper;
    @Test
    public void query(){
        List<User> users = userMapper.selectList(null);
        System.out.println("users = " + users);
    }
}

注意:使用注解时,测试类要和启动类在同一目录结构下

7.测试结果

MyBatis-Plus核心功能

基于Mapper接口 CRUD

对比mybatis和mybatis-plus:

mybatis对数据库的操作:1.创建mapper接口定义crud方法 2.创建mapperxml编写crud的sql

mybatis-plus对数据库的操作:1.创建mapper接口继承BaseMapper<T>

                                                 2.原来的mybatis操作没有变,需要时依然可以使用

BaseMapper中的方法。

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

    int deleteById(Serializable id);

    int deleteById(T entity);

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

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

    int deleteBatchIds(@Param("coll") Collection<?> 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);

    default T selectOne(@Param("ew") Wrapper<T> queryWrapper) {
        List<T> list = this.selectList(queryWrapper);
        if (list.size() == 1) {
            return list.get(0);
        } else if (list.size() > 1) {
            throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
        } else {
            return null;
        }
    }

    default boolean exists(Wrapper<T> queryWrapper) {
        Long count = this.selectCount(queryWrapper);
        return null != count && count > 0L;
    }

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

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

    <P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param("ew") Wrapper<T> queryWrapper);
}
CRUD方法介绍:

application.yaml

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #控制台输出日志
    #像驼峰映射的操作在底层已经设置了好了,可以不修改。

mapper接口:

public interface UserMapper extends BaseMapper<User> {
}

测试:

@SpringBootTest//springboot下测试环境注解
public class BootTest {
    @Autowired
    private UserMapper userMapper;
//            +---------------------+--------+------+--------------------+
//            | id                  | name   | age  | email              |
//            +---------------------+--------+------+--------------------+
//            |                   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 |
//            | 1767361818149871618 | 123    |   88 | 11                 |
//            +---------------------+--------+------+--------------------+
    @Test
//    INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
//    1767362650761154561(Long), 123(String), 88(Integer), 11(String)
//    mybatis-plus会自动对id进行赋值。
    public void test_insert(){
        User user = new User();
        user.setAge(88);
        user.setEmail("11");
        user.setName("123");
        userMapper.insert(user);
    }
//            +---------------------+--------+------+--------------------+
//            | id                  | name   | age  | email              |
//            +---------------------+--------+------+--------------------+
//            |                   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 |
//            | 1767361818149871618 | 123    |   88 | 11                 |
//            | 1767362650761154561 | 123    |   88 | 11                 |
//            +---------------------+--------+------+--------------------+
    @Test
    public void test_delete(){
        //根据id删除
//        DELETE FROM user WHERE id=?
//        1767362650761154561(Long)
        userMapper.deleteById(1767362650761154561L);
        //根据age=20和name=jack
//        DELETE FROM user WHERE name = ? AND age = ?
//        jack(String), 20(Integer)
        Map map=new HashMap();
        map.put("age",20);
        map.put("name","jack");
        userMapper.deleteByMap(map);
    }
//            +---------------------+--------+------+--------------------+
//            | id                  | name   | age  | email              |
//            +---------------------+--------+------+--------------------+
//            |                   1 | Jone   |   18 | test1@baomidou.com |
//            |                   3 | Tom    |   28 | test3@baomidou.com |
//            |                   4 | Sandy  |   21 | test4@baomidou.com |
//            |                   5 | Billie |   24 | test5@baomidou.com |
//            | 1767361818149871618 | 123    |   88 | 11                 |
//            +---------------------+--------+------+--------------------+
    @Test
    public void test_update(){
        //根据id修改
        //user的id必须有值
        //如果setXxx(null)不进行修改,这就是为什么要把age设置为Integer包装类,因为int类型默认为0
//        UPDATE user SET age=? WHERE id=?
//        99(Integer), 1(Long)
        User user =new User();
        user.setId(1L);
        user.setName(null);
        user.setAge(99);
        userMapper.updateById(user);

        //将所有人的年龄改为Name改为111
        //如果setXxx(null)不进行修改
//        UPDATE user SET name=?
//        111(String)
        User user1 = new User();
        user1.setName("111");
        user1.setEmail(null);
        userMapper.update(user1,null);
    }
//            +---------------------+------+------+--------------------+
//            | id                  | name | age  | email              |
//            +---------------------+------+------+--------------------+
//            |                   1 | 111  |   99 | test1@baomidou.com |
//            |                   3 | 111  |   28 | test3@baomidou.com |
//            |                   4 | 111  |   21 | test4@baomidou.com |
//            |                   5 | 111  |   24 | test5@baomidou.com |
//            | 1767361818149871618 | 111  |   88 | 11                 |
//            +---------------------+------+------+--------------------+
    @Test
    public void test_select(){
        //根据id查询
//        SELECT id,name,age,email FROM user WHERE id=?
//        1(Long)
//        user = User(id=1, name=111, age=99, email=test1@baomidou.com)
        User user = userMapper.selectById(1L);
        System.out.println("user = " + user);

        //集合查询
//        SELECT id,name,age,email FROM user WHERE id IN ( ? , ? )
//        1(Long), 3(Long)
//        users = [User(id=1, name=111, age=99, email=test1@baomidou.com),
//                 User(id=3, name=111, age=28, email=test3@baomidou.com)]
        List<Long> ids = new ArrayList<>();
        ids.add(1L);ids.add(3L);
        List<User> users = userMapper.selectBatchIds(ids);
        System.out.println("users = " + users);

    }

基于Service接口 CRUD

对比Mapper接口CRUD区别:

        1.service层的加强进一步封装了crud,采用 get 查询单行, remove 删除, list 查询集合, page 分页 前缀命名方式区分 Mapper 层避免混淆,并且支持批量操作

        2.service层的方法自动添加事务

为什么要加强service层:

        1.如果接收的请求是查询用户信息,那么service层的操作仅仅只是调用了mapper的select方法。

        2.如果controller层接收的请求较简单(插入单表数据,查询单表数据等),可以直接调用service层加强的方法,不需要在从service层调用mapper层方法。

        3.加强service后,简单逻辑走service,复杂逻辑再走mapper

使用方式

接口继承IService<T>接口

public interface UserService extends IService<User> {
}

实现类继承 ServiceImpl<M extends BaseMapper<T>, T> 

public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

为什么既要继承接口又要继承实现类?

        IService<T>接口中定义了所有方法,但只默认实现了一半,而ServiceImpl<M extends BaseMapper<T>, T>中实现了另一半,如果不继承实现类,需要自己手动实现另一半方法。(ServiceImpl 实现了 IService)

public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
    ……
}
CRUD方法介绍:
package com.qiu;

import com.qiu.pojo.User;
import com.qiu.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.ArrayList;
import java.util.List;


@SpringBootTest
public class BootUserTest2 {

    @Autowired
    private UserService userService;

//    保存
    @Test
    public void test_save(){
        List<User> list= new ArrayList<>();
        User user=new User();
        user.setAge(18);
        user.setEmail("666");
        user.setName("123");
        list.add(user);

        User user1=new User();
        user1.setAge(23);
        user1.setEmail("777");
        user1.setName("231");
        list.add(user1);
//        INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
//        1767385817043038210(Long), 123(String), 18(Integer), 666(String)
//        1767385817126924289(Long), 231(String), 23(Integer), 777(String)
        userService.saveBatch(list);
    }
//    保存或修改:如果user的id有值则修改,没有则保存
    @Test
    public void test_saveOrUpdate(){
//        添加
//        INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
//        1767386132609855489(Long), 9999(String), 998(Integer), 999(String)
        User user=new User();
        user.setAge(998);
        user.setEmail("999");
        user.setName("9999");
        userService.saveOrUpdate(user);
//        修改
//        SELECT id,name,age,email FROM user WHERE id=?
//        1, 111, 99, test1@baomidou.com
//        UPDATE user SET name=?, age=?, email=? WHERE id=?
//        1999(String), 1999(Integer), 1999(String), 1(Long)
        User user1=new User();
        user1.setId(1L);
        user1.setAge(1999);
        user1.setEmail("1999");
        user1.setName("1999");
        userService.saveOrUpdate(user1);
    }


//    修改
//    UPDATE user SET name=?, age=?, email=? WHERE id=?
//    8888(String), 8888(Integer), 8888(String), 1(Long)
    @Test
    public void test_update(){
        User user=new User();
        user.setId(1L);
        user.setAge(8888);
        user.setEmail("8888");
        user.setName("8888");
        userService.updateById(user);
    }
//    移除
//    DELETE FROM user WHERE id=?
//    1767386132609855489(Long)
    @Test
    public void test_remove(){
        userService.removeById(1767386132609855489L);

    }
//    查询
    @Test
    public void test_getOrList(){
//        SELECT id,name,age,email FROM user WHERE id=?
//        1(Long)
//        user = User(id=1, name=8888, age=8888, email=8888)
        User user = userService.getById(1L);
        System.out.println("user = " + user);
//        SELECT id,name,age,email FROM user
//        list = [User(id=1, name=8888, age=8888, email=8888), User(id=3, name=111, age=28, email=test3@baomidou.com), User(id=4, name=111, age=21, email=test4@baomidou.com), 
//                User(id=5, name=111, age=24, email=test5@baomidou.com), User(id=1767361818149871618, name=111, age=88, email=11), User(id=1767385817043038210, name=123, age=18, email=666), 
//                User(id=1767385817126924289, name=231, age=23, email=777), User(id=1767386445735616513, name=9999, age=998, email=999), User(id=1767386600643825666, name=9999, age=998, email=999)]
        List<User> list = userService.list(null);
        System.out.println("list = " + list);

    }
}

分页查询实现

在mapper层增强和service层增强中都提供了分页查询方法。

MyBatis和Mybatis-Plus分页查询

MyBatis:1、设置分页参数PageHelper

                2、编写sql语句

                3、结果封装PageInfo

                4、获取分页数据

原理:后置拦截器,sql语句不要分号(;)结尾,因为分页查询底层就是在sql语句后拼接字符串(limit x,y)。

Mybatis-Plus:在MyBatis-Plus中一个插件集合  MybatisPlusInterceptor ,只需要把分页插件放到集合中进行了。

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //注意DbType.MYSQL导包别导错
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
使用分页查询
    @Test
    public void test_page(){
        //设置分页参数
        Page<User> page = new Page<>(1,5);
        //查询后数据会写入原来的page,可以使用它获取数据
        userService.page(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());
    }
//    SELECT COUNT(*) AS total FROM user
//    SELECT id,name,age,email FROM user LIMIT ?
//    5(Long)
//    User(id=1, name=8888, age=8888, email=8888)
//    User(id=3, name=111, age=28, email=test3@baomidou.com)
//    User(id=4, name=111, age=21, email=test4@baomidou.com)
//    User(id=5, name=111, age=24, email=test5@baomidou.com)
//    User(id=1767361818149871618, name=111, age=88, email=11)
//    获取当前页: 1
//    每页显示的条数: 5
//    总数据数: 9
//    总页数: 2
//    是否有上一页: false
//    是否有下一页: true
自定义的mapper方法使用分页

方法:UserMapper接口

public interface UserMapper extends BaseMapper<User> {
//    参数列表携带IPage接口
//    返回结果为IPage
    IPage queryPageByAge(IPage<User> page, @Param("age") Integer Age);
}

实现:MapperXml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qiu.mapper.UserMapper">

<!--    记得起别名,sql后面不要加分号-->
    <select id="queryPageByAge" resultType="user">
        select * from user where age > #{age}
    </select>
</mapper>

测试方法:

@Test
    public void test_page_age(){
        Page page =new Page<User>(1,3);
        userMapper.queryPageByAge(page,22);
        List records = page.getRecords();
        records.forEach(System.out::println);
    }

结果:

条件构造器使用

1、条件构造器作用

Mybatis-Plus的条件构造器可以让我们构建灵活、高效的查询条件。

场景:删除 name=John,age != 30,email like %@gmail.com

    @Test
    public void test2(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name","John");
        queryWrapper.ne("age",30);
        queryWrapper.like("email","@gmail.com");
//        上面操作等于delete from user where name = "John" and age !=30 and email like "%@gmail.com%"
//        int delete(@Param("ew") Wrapper<T> queryWrapper);
        userMapper.delete(queryWrapper);
    }
2、条件构造器继承结构

Wrapper:抽象类,顶端父类

        一般操作UpdateWrapper、QueryWrapper、LambdaUpdateWrapper和LambdaQueryWrapper,推荐使用后两个。

UpdateWrapper和LambdaUpdateWrapper:一般修改时使用

QueryWrapper和LambdaQueryWrapper:一般删除和查询时使用

3、基于QueryWrapper组装条件

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


    @Test
    //组装查询条件
    public void test1(){
        //查询用户名包含a,年龄在20到30之间,并且邮箱不为null的用户信息
        //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)
        QueryWrapper<User> queryWrapper=new QueryWrapper<>();
        queryWrapper.like("name",'a')
                .between("age",20,30)
                .isNotNull("email");
        List<User> list = userMapper.selectList(queryWrapper);
    }
    @Test
//    组装排序条件
    public void test2(){
        //按年龄降序查询用户,如果年龄相同则按id升序排列
        //SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 ORDER BY age DESC,id ASC
        QueryWrapper<User> queryWrapper=new QueryWrapper<>();
        queryWrapper.orderByDesc("age")
                .orderByAsc("id");
        List<User> users = userMapper.selectList(queryWrapper);
    }
    @Test
//    组装删除条件
    public void test3(){
        //删除email为空的用户
        //DELETE FROM t_user WHERE (email IS NULL)
        QueryWrapper<User> queryWrapper=new QueryWrapper<>();
        queryWrapper.isNull("email");
        int result = userMapper.delete(queryWrapper);
    }
    @Test
//    and和or关键字使用(修改):
    public void test4(){
        //将年龄大于20并且用户名中包含有a或邮箱为null的用户信息修改
//        优先级:not>and>or
        //UPDATE t_user SET age=?, email=? WHERE username LIKE ? AND age > ? OR email IS NULL
        QueryWrapper<User> queryWrapper=new QueryWrapper<>();
        queryWrapper.gt("age",20)
                .like("name","a")
                .or().isNull("email");
        User user = new User();
        user.setAge(18);
        user.setEmail("user@atguigu.com");
        int result = userMapper.update(user, queryWrapper);
    }
    @Test
//    指定列映射查询:返回指定的列数据
    public void test5(){
        //查询用户信息的username和age字段
        //SELECT username,age FROM t_user
        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
//    condition判断组织条件
    public void test6(){
        String name = "John";
        int    age = 18;
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //判断条件拼接
        //当name不为null拼接等于, age > 1 拼接等于判断
        //方案1: 手动判断
//      import org.junit.platform.commons.util.StringUtils;
        if (StringUtils.isNotBlank(name)){
            queryWrapper.eq("name",name);
        }
        if (age > 1){
            queryWrapper.eq("age",age);
        }
        //方案2: 拼接condition判断
        //每个条件拼接方法都condition参数,这是一个比较运算,为true追加当前条件!
        //eq(condition,列名,值)
        queryWrapper.eq(StringUtils.isNotBlank(name),"name",name)
                .eq(age>1,"age",age);
    }
}
4、基于UpdateWrapper组装条件
    @Test
    public void test(){
//        如果使用QueryWrapper进行修改,需要准备要修改的实体类,而且数据不能改为null值。
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("age",18);
        User user = new User();
        user.setAge(99);
        user.setName(null);//此时不会修改name
        userMapper.update(user,queryWrapper);

//        使用UpdateWrapper修改
//        可以直接携带数据 updateWrapper.set("age",12);
//        也可以设置为空 updateWrapper.set("email",null);
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.gt("age",35)
                .set("email",null)
                .set("age",19);
        userMapper.update(null,updateWrapper);

    }
5、基于LambdaQueryWrapper组装条件
LambdaQueryWrapper对比QueryWrapper

QueryWrapper表示字段名时使用字符串:queryWrapper.eq("name","qiu");

LambdaQueryWrapper表示字段名时使用实体类的属性引用:lambdaQueryWrapper.eq(User::getName,"qiu");

提高了代码的可读性和可维护性。

@Test
    //组装查询条件
    public void test1(){
        //查询用户名包含a,年龄在20到30之间,并且邮箱不为null的用户信息
        LambdaQueryWrapper<User> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.like(User::getName,'a')
                .between(User::getAge,20,30)
                .isNotNull(User::getEmail);
        List<User> list = userMapper.selectList(lambdaQueryWrapper);
    }
6、基于LambdaUpdateWrapper组装条件
    @Test
    public void test2(){
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.gt(User::getAge,35)
                .set(User::getEmail,null)
                .set(User::getAge,19);
        userMapper.update(null,updateWrapper);
    }

核心注解使用

@TableName

@TableName("value"):加到与数据库表对应的pojo类上,value对应的数据库的表名,可以不加,不加默认使用实体类的名字作为表名,忽略大小写。

IService<User>,BaseMapper<User>等会根据User类对应的表名在数据库中操作相应的表。

 一般数据库表会有 “  t_  ”之类的前缀,可以在application.yaml中添加

        mybatis-plus.global-config.db-config.table-prefix: t_

来统一配置,如果类上有@TableName注解则该配置对此类无效。

@TableId

@TableId(value="主键名", type=主键策略)

使用场景:1.主键的列名与属性名不一致(驼峰映射后能对应上的不需要使用)

                  2.指定插入数据时如何生成主键,常用枚举类型如下:

  • ASSIGN_ID:分配ID,在mybati-plus3.3.0以后会使用雪花算法分配id(Long类型)
  • AUTO:数据库ID自增(需要mysql数据库表的主键列设置自增长)

主键策略也可以统一设置:application.yaml中的mybatis-plus.global-config.db-config.id-type: auto

@TableField

当普通字段与属性不一致时,可以使用该注解手动设置。

@Data
public class User {
    @TableId("id")
    private Long userId;
    @TableField("name")
    private String userName;
    private Integer age;
    private String email;
    @TableField(exist = false)
    private String py;
}

pojo类属性应该与数据库表的字段一一对应,属性缺少不报错但不会返回该字段,属性比字段多会报错,要么删除,要么该属性上使用@TableField(exist=false)。

MyBatis-Plus高级拓展

逻辑删除实现

逻辑删除是指在表中添加一个字段模拟删除,在删除操作时使用假删除,方便之后进行数据分析和恢复。

在数据库表中添加删除字段 

alter table user add deleted int default 0 ; int类型,1为逻辑删除,0为未逻辑删除。

在pojo类中添加逻辑删除属性,并在属性上添加@TableLogic注解

使用该注解后,默认执行mybatis-plus提供的删除方法时,会自动换成修改方法,如果要删除的数据中逻辑删除字段为0,就将该字段改为1,不为0则不修改。

逻辑删除全局配置:
  • 在application.yaml中设置mybatis-plus.global-config.db-config.logic-delete-field: deleted 设置所有逻辑这段的实体字段名为deleted。
  • mybatis-plus.global-config.db-config.logic-delete-value: 1 设置逻辑删除值为1
  • mybatis-plus.global-config.db-config.logic-not-delete-value: 0 设置未逻辑删除值为0

乐观锁实现

场景:

可能为500,也可能为0。

两个动作都是先取值在减值,如果两个动作同时取到1000,减值后为500。

资源争抢的解决方法:

乐观锁和悲观锁是在并发编程中用于处理并发访问和资源竞争的两种不同的锁机制!!

悲观锁:获取资源时把资源上锁,确保在减值操作前只有自己获得资源,但效率低。

乐观锁:获取资源前检查是否资源已被使用,没有就操作资源,有则等待一段时间后继续尝试获取资源。效率较高,但是在并发冲突较为频繁的情况下,乐观锁会导致较多的冲突处理和重试操作。

理解点:

         悲观锁和乐观锁是两种解决并发数据问题的思路,不是具体技术!!!

具体技术和方案:

1.乐观锁实现方案和技术:

  • 版本号/时间戳:为数据添加一个版本号或时间戳字段,每次更新数据时,比较当前版本号或时间戳与期望值是否一致,一致则更新成功,否则表示数据已被修改,需要冲突处理。
  • CAS(Compare-and-Swap):使用原子操作比较当前值与旧值是否一致,若一致则进行更新操作,否则重新尝试。
  • 无锁数据结构:采用无锁数据结构,如无锁队列,无锁哈希表等,通过使用原子操作实现并发安全。

2.悲观锁实现方案和技术:

  • 锁机制:使用传统的锁机制,如互斥锁或读写锁来保证对共享资源的独占访问。
  • 数据库锁:在数据库层面使用行级锁或表级锁来控制并发访问。
  • 信号量:使用信号量来限制对资源的并发访问。
版本号乐观锁的实现流程:

1.数据库表添加一个字段version

2.取出数据时,获取当前version

3.更新时,检查获取版本号是不是数据库当前最新版本号

4.如果是,说明没人修改数据,执行更新操作,set 数据更新,version = version + 1

5.如果不是,说明数据被修改了,当前数据为无效数据,更新失败。

使用mybatis-plus数据使用乐观锁:

1. 添加版本号更新插件

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//        分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
//        版本号更新插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }

2.乐观锁字段添加@Version注解

  数据库添加乐观锁字段

ALTER TABLE t_user ADD VERSION INT DEFAULT 1 ;  # int 类型 乐观锁字段
表结构
+---------+-------------+------+-----+---------+-------+
| Field   | Type        | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id      | bigint      | NO   | PRI | NULL    |       |
| name    | varchar(20) | YES  |     | NULL    |       |
| age     | int         | YES  |     | NULL    |       |
| email   | varchar(20) | YES  |     | NULL    |       |
| deleted | int         | YES  |     | 0       |       |
| VERSION | int         | YES  |     | 1       |       |
+---------+-------------+------+-----+---------+-------+

乐观锁字段支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime

仅支持updateById(id)与update(entry, wrapper)方法

@Data
public class User {
    @TableId("id")
    private Long userId;
    @TableField("name")
    private String userName;
    private Integer age;
    private String email;
//    @TableLogic
  逻辑删除字段 int mybatis-plus下,默认逻辑删除为1,未逻辑删除为0
    private Integer deleted;
    @Version
    private Integer version;
}

测试:

//演示乐观锁生效场景
@Test
public void testQuick7(){
    //步骤1: 先查询,在更新 获取version数据
    //同时查询两条,但是version唯一,最后更新的失败
    User user  = userMapper.selectById(4);
    User user1  = userMapper.selectById(4);

    user.setAge(20);
    user1.setAge(30);

    userMapper.updateById(user);
    //乐观锁生效,失败!
    userMapper.updateById(user1);
}

结果:

+---------------------+--------+------+--------------------+---------+---------+
| id                  | name   | age  | email              | deleted | VERSION |
+---------------------+--------+------+--------------------+---------+---------+
|                   1 | Jone   |   18 | test1@baomidou.com |       0 |       1 |
|                   2 | Jack   |   20 | test2@baomidou.com |       0 |       1 |
|                   3 | Tom    |   28 | test3@baomidou.com |       0 |       1 |
|                   4 | Sandy  |   20 | test4@baomidou.com |       0 |       2 |
|                   5 | Billie |   24 | test5@baomidou.com |       1 |       1 |
| 1767361818149871618 | 111    |   88 | 11                 |       0 |       1 |
| 1767385817043038210 | 123    |   18 | 666                |       0 |       1 |
| 1767385817126924289 | 231    |   23 | 777                |       0 |       1 |
| 1767386445735616513 | 9999   |  998 | 999                |       0 |       1 |
| 1767386600643825666 | 9999   |  998 | 999                |       0 |       1 |
+---------------------+--------+------+--------------------+---------+---------+

防全表更新和删除实现

添加防止全表更新或删除插件

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//        分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
//        版本号更新插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
//        防止全表更新或删除
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        return interceptor;
    }

测试:

    @Test
    public void test(){
//        全表删除
        userMapper.delete(null);
    }

结果:报错

MyBatis-Plus代码生成器(MyBatisX 插件)

MyBatisX插件逆向工程

MyBatisX快速代码生成

使用mybatisX插件,自动生成sql语句实现

MybatisX快速开发插件 | MyBatis-PlusMyBatis-Plus 官方文档icon-default.png?t=N7T8https://baomidou.com/pages/ba5b24/?spm=wolai.workspace.0.0.330e767bDepZBf#%E5%8A%9F%E8%83%BD

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/455227.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LEETCODE3

法一:记忆化递归 int climbStairsRecursive(int n, int* memo) {if (n < 2) {return n;}if (memo[n] > 0) {return memo[n];}memo[n] climbStairsRecursive(n - 1, memo) climbStairsRecursive(n - 2, memo);return memo[n]; }int climbStairs(int n) {int* memo (in…

2061:【例1.2】梯形面积

时间限制: 1000 ms 内存限制: 65536 KB 提交数:201243 通过数: 79671 【题目描述】 在梯形中阴影部分面积是150平方厘米&#xff0c;求梯形面积。 【输入】 (无&#xff09; 【输出】 输出梯形面积&#xff08;保留两位小数&#xff09;。 【输入样例】 &#xff…

redis在微服务领域的贡献,字节跳动只面试两轮

dubbo.registry.addressredis://127.0.0.1:6379 注册上来的数据是这样&#xff0c;类型是hash /dubbo/ s e r v i c e / {service}/ service/{category} 如 /dubbo/com.newboo.sample.api.DemoService/consumers /dubbo/com.newboo.sample.api.DemoService/providers has…

JOSEF约瑟 TQ-100同期继电器 额定直流电压220V 交流电压100V±10V

TQ-100型同期继电器 TQ-100同期继电器 ​ l 应用 本继电器用于双端供电线路的自动重合闸和备用电源自投装置中&#xff0c;以检查线路电压与母线电压的 相位差和幅值差。 2 主要性能 2 1采用进口集成电路和元器件构成&#xff0c;具有原理先进、性能稳定、可靠性高、动作值精…

mysql生成连续的日期

1.代码 例如&#xff1a;生成"2023-03-01"至"2023-03-10"之间的日期 WITH RECURSIVE date_range AS (SELECT "2023-03-01" AS date FROM dualUNION ALLSELECT DATE_ADD(date, INTERVAL 1 DAY) dateFROM date_rangeWHERE DATE_ADD(date, INTER…

windows系统提示msvcp120.dll丢失如何解决,如何找回dll文件?

如果你的电脑出现了关于msvcp120.dll丢失的情况那么大家一定要及时去解决msvcp140.dll丢失的问题&#xff0c;msvcp120.dll丢失可能会导致电脑出现各类的问题&#xff0c;今天就教大家四种关于msvcp120.dll丢失的解决办法&#xff0c;有效的解决msvcp120.dll丢失。 一、msvcp1…

php 对接Bigo海外广告平台收益接口Reporting API

今天对接的是Bigo广告reporting api接口&#xff0c;拉取广告收益回来自己做统计。记录分享给大家 首先是文档地址,进入到BIGO后台就能看到文档地址以及参数&#xff1a; 文档地址&#xff1a;https://www.bigossp.com/guide/sdk/reportingApi/doc?type1 接入这些第三方广告…

kangle一键安装脚本

Kangle一键脚本&#xff0c;是一款可以一键安装KangleEasypanelMySQLPHP集合的Linux脚本。 脚本本身集成&#xff1a;PHP5.38.2、MYSQL5.68.0&#xff0c;支持极速安装和编译安装2种模式&#xff0c;支持CDN专属安装模式。同时也对Easypanel面板进行了大量优化。 脚本特点 ◎…

十六、接口隔离原则、反射、依赖注入

接口隔离原则、反射、特性、依赖注入 接口隔离原则 客户端不应该依赖它不需要的接口&#xff1b;一个类对另一个类的依赖应该建立在最小的接口上。 五种原则当中的i 上一章中的接口&#xff0c;即契约。 契约就是在说两件事&#xff0c;甲方说自己不会多要&#xff0c;乙方会在…

Linux下安装Android Studio及创建桌面快捷方式

下载 官网地址&#xff1a;https://developer.android.com/studio?hlzh-cn点击下载最新版本即可 安装 将下载完成后文件&#xff0c;进行解压&#xff0c;然后进入android-studio-2023.2.1.23-linux/android-studio/bin目录下&#xff0c;启动studio.sh即可为了更加方便的使…

医药大数据案例分析

二、功能 &#xff08;1&#xff09;流量分析 &#xff08;2&#xff09;经营状态分析 &#xff08;3&#xff09;大数据可视化系统 配置tomcat vim /root/.bash_profile添加以下内容&#xff1a; export CATALINA_HOME/opt/tomcat export PATH P A T H : PATH: PATH:CATALIN…

程序员的三重境界:码农,高级码农、程序员!

见字如面&#xff0c;我是军哥&#xff01; 掐指一算&#xff0c;我在 IT 行业摸爬滚打 19 年了&#xff0c;见过的程序员至少大好几千&#xff0c;然后真正能称上程序员不到 10% &#xff0c;绝大部分都是高级码农而已。 今天和你聊聊程序员的三个境界的差异&#xff0c;文章不…

LDA主题模型学习笔记

&#xff08;1&#xff09;LDA的基本介绍&#xff08;wiki&#xff09; LDA是一种典型的词袋模型&#xff0c;即它认为一篇文档是由一组词构成的一个集合&#xff0c;词与词之间没有顺序以及先后的关系。一篇文档可以包含多个主题&#xff0c;文档中每一个词都由其中的一个主题…

鸿蒙Socket通信示例(TCP通信)

前言 DevEco Studio版本&#xff1a;4.0.0.600 参考链接&#xff1a;OpenHarmony Socket 效果 TCPSocket 1、bind绑定本地IP地址 private bindTcpSocket() {let localAddress resolveIP(wifi.getIpInfo().ipAddress)console.info("111111111 localAddress: " …

特殊类设计以及C++中的类型转换

1. 请设计一个类&#xff0c;不能被拷贝 拷贝只会放生在两个场景中&#xff1a;拷贝构造函数以及赋值运算符重载&#xff0c;因此想要让一个类禁止拷贝&#xff0c;只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。 C98&#xff1a; 将拷贝构造函数与赋值运算符重载只…

ruoyi-vue插件集成websocket

链接&#xff1a;插件集成 | RuoYi WebSocketServer.java&#xff1a;补充代码 /*** 此为广播消息* param message 消息内容*/public void sendAllMessage(String message) {LOGGER.info("【websocket.sendAllMessage】广播消息:"message);try {for(String sessionI…

观测云在 .NET 业务中分析性能问题的最佳实践

背景 某药业集团是一家以创新技术驱动的线下医疗数据 SaaS 平台建设和运营公司&#xff0c;其主营的某智慧医疗平台产品&#xff0c;围绕线下医疗场景痛点提供一体化服务解决方案。近期集团对其生物检材在线递检系统进行功能升级开发及 IaaS 平台迁移。在针对新系统和新基础设…

云仓酒庄北京朝阳区旗舰店发布活动盛况:红酒品鉴沙龙共筑美好

原标题&#xff1a;云仓酒庄北京朝阳区旗舰店活动盛况&#xff1a;红酒品鉴沙龙与招商交流共筑美好未来 在繁忙的都市中&#xff0c;有一片静谧的天地&#xff0c;那便是云仓酒庄北京朝阳区旗舰店。这里不仅是红酒爱好者的聚集地&#xff0c;更是商业交流的新平台。近日&#…

网络编程-套接字相关基础知识

1.1. Socket简介 套接字&#xff08;socket&#xff09;是一种通信机制&#xff0c;凭借这种机制&#xff0c; 客户端<->服务器 模型的通信方式既可以在本地设备上进行&#xff0c;也可以跨网络进行。 Socket英文原意是“孔”或者“插座”的意思&#xff0c;在网络编程…

2023 年安徽省职业院校技能大赛(高职组)

#需要资源或有问题的&#xff0c;可私博主&#xff01;&#xff01;&#xff01; #需要资源或有问题的&#xff0c;可私博主&#xff01;&#xff01;&#xff01; #需要资源或有问题的&#xff0c;可私博主&#xff01;&#xff01;&#xff01; 某企业根据自身业务需求&#…