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 {@Autowiredprivate UserMapper userMapper;@Testpublic 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 {@Autowiredprivate 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                 |
//            +---------------------+--------+------+--------------------+@Testpublic 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                 |
//            +---------------------+--------+------+--------------------+@Testpublic 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                 |
//            +---------------------+------+------+--------------------+@Testpublic 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 {@Autowiredprivate UserService userService;//    保存@Testpublic 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有值则修改,没有则保存@Testpublic 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)@Testpublic 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)@Testpublic void test_remove(){userService.removeById(1767386132609855489L);}
//    查询@Testpublic 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 ,只需要把分页插件放到集合中进行了。

    @Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//注意DbType.MYSQL导包别导错interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}
使用分页查询
    @Testpublic 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接口
//    返回结果为IPageIPage queryPageByAge(IPage<User> page, @Param("age") Integer Age);
}

实现:MapperXml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//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>

测试方法:

@Testpublic 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

    @Testpublic 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 {@Autowiredprivate 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 ASCQueryWrapper<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 NULLQueryWrapper<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_userQueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.select("name", "age");//selectMaps()返回Map集合列表,通常配合select()使用,避免User对象中没有被查询到的列值为nullList<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组装条件
    @Testpublic void test(){
//        如果使用QueryWrapper进行修改,需要准备要修改的实体类,而且数据不能改为null值。QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.gt("age",18);User user = new User();user.setAge(99);user.setName(null);//此时不会修改nameuserMapper.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组装条件
    @Testpublic 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. 添加版本号更新插件

    @Beanpublic 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,未逻辑删除为0private Integer deleted;@Versionprivate 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 |
+---------------------+--------+------+--------------------+---------+---------+

防全表更新和删除实现

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

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

测试:

    @Testpublic 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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

cms垃圾回收

cms垃圾回收 CMS概述CMS收集器整体流程初始标记并发标记重新标记并发清除 CMS卡表什么是卡表(card table)什么是mod-union table CMS概述 CMS(Concurrent Mark Sweep)收集器是Java虚拟机中的一种老年代(old Generation)垃圾收集器&#xff0c;他主要目标是减少垃圾收集时的应用…

Git之版本回退

文章转载于&#xff1a;https://www.jianshu.com/p/3020740561a8 以前&#xff0c;如果是要去除某一块功能&#xff0c;我都是选择性删除&#xff0c;选择性注释&#xff0c;然后前后逻辑各种查看&#xff0c;各种比较。每一次&#xff0c;改完这些我总感觉心好累啊&#xff01…

【C#】【SAP2000】读取SAP2000中所有Frame对象的应力比到Grasshopper中

if (build true) {// 连接到正在运行的 SAP2000// 使用 System.Runtime.InteropServices.Marshal.GetActiveObject 方法获取正在运行的 SAP2000 实例cOAPI mySapObject (cOAPI)System.Runtime.InteropServices.Marshal.GetActiveObject("CSI.SAP2000.API.SapObject"…

基于STM32的感应开关垃圾桶

1.定时器介绍 1.1 工作原理 使用精准的时基&#xff0c;通过硬件的方式&#xff0c;实现定时功能。 1.2 定时器分类 基本定时器&#xff08;TIM6~TIM7&#xff09;通用定时器&#xff08;TIM2~TIM5&#xff09;高级定时器&#xff08;TIM1和TIM8&#xff09; 1.3 通用定时器…

0基础使用dockerfile构建容器镜像

目录 一、使用dockerfile构建镜像 1.1、dockerfile指令 1.FROM 2.RUN 3.CMD 4.ENTRYPOINT 5.EXPOSR ​编辑 6.ADD和COPY ​编辑7.volume 8.USER 二、案例1&#xff1a;dockerfile构建httpd镜像 构建一个指定挂载点的httpd镜像 三、案例2&#xff1a;构建tomcat镜…

使用公式在Excel中指定列值的变化实现自动间隔着色(不是按照固定的行数)

如果你的文件很小&#xff0c;可以手工着色&#xff1b;但如果很大&#xff0c;就要借助公式来着色&#xff1b; 目的是什么&#xff0c;其中之一是&#xff1a;提升可读性。 一起往下看吧&#xff01;&#xff01; 如果你想要根据Excel某列中值的变化来间隔着色&#xff0c;…

Prompt Learning:人工智能的新篇章

开篇&#xff1a;AI的进化之旅 想象一下&#xff0c;你正在和一位智能助手对话&#xff0c;它不仅理解你的问题&#xff0c;还能提出引导性的问题帮助你更深入地思考。这正是prompt learning的魔力所在——它让机器学习模型变得更加智能和互动。在这篇博客中&#xff0c;我们将…

腾讯云轻量服务器配置nginx资源服务器

文章目录 序言&#xff1a;2、安装WindowsServer系统3、修改密码&#xff0c;启动系统4、进入系统&#xff0c;这里踩了一些坑5、安装Nginx6、配置nginx7、启动nginx,8、外网配置9、修改安全策略 序言&#xff1a; 今天项配置一个游戏资源服务器&#xff0c;做更新资源用&#…

uniapp 对video视频组件嵌套倍速按钮

这次接了需求是要求有倍速功能&#xff0c;去看了文档发现并没有倍速按钮的属性&#xff0c;想着手写一个吧 可最后发现原生层级太高&#xff0c;无论怎么样都迭不上去&#xff0c;就只能去找插件看看咯 找了好多插件发现都不可用&#xff0c;因为我这是app端&#xff0c;有些视…

学生时期学习资源同步-1 第一学期结业考试题2

原创作者&#xff1a;田超凡&#xff08;程序员田宝宝&#xff09; 版权所有&#xff0c;引用请注明原作者&#xff0c;严禁复制转载

【小沐学C#】C#文件读写方式汇总

文章目录 1、简介2、相关类介绍3、代码示例3.1 FileStream&#xff08;流文件&#xff09;3.2 StreamReader / StreamWriter &#xff08;文本文件&#xff09;3.2.1 StreamReader3.2.2 StreamWriter 3.3 BinaryReader / BinaryWriter &#xff08;二进制文件&#xff09;3.3.1…

FastWiki v0.1.0发布!新增超多功能

FastWiki 发布 v0.1.0 https://github.com/239573049/fast-wiki/releases/tag/v0.1.0 更新日志 兼容OpenAI接口格式删除Blazor版本UI删除useEffect,解决可能存在问题的bug修复对话可以看到所有对话Merge branch ‘master’ of https://gitee.com/hejiale010426/fast-wiki更新…