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.mzph.cn/news/743330.shtml

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

相关文章

开发指南009-从list导出excel文件

从数据库返回一般是对象的列表&#xff0c;平台底层提供了从list转为excel文件的方法。平台的设计思想就是为一些典型的场景设计对应的解决方法&#xff0c;通过模式化的方法来简化编程和提高维护性&#xff08;通过标准化来减少学习成本和维护成本&#xff0c;张三做的东西和李…

游戏数据处理

游戏行业关键数据指标 ~ 总激活码发放量、总激活量、总登录账号数 激活率、激活登录率 激活率 激活量 / 安装量 激活率 激活量 / 激活码发放量 激活且登录率 激活且登录量 / 激活码激活量 激活且登录率应用场景 激活且登录率是非常常用的转化率指标之一&#xff0c;广泛…

Ypay源支付6.9无授权聚合免签系统可运营源码

YPay是一款专为个人站长设计的聚合免签系统&#xff0c;YPay基于高性能的ThinkPHP 6.1.2 Layui PearAdmin架构&#xff0c;提供了实时监控和管理的功能&#xff0c;让您随时随地掌握系统运营情况。 说明 Ypay源支付6.9无授权聚合免签系统可运营源码 已搭建测试无加密版本…

HTML5:七天学会基础动画网页13

看完前面很多人可能还不是很明白0%-100%那到底是怎么回事&#xff0c;到底该怎么用&#xff0c;这里我们做一个普遍的练习——心跳动画 想让心❤跳起来&#xff0c;我们先分析一波&#xff0c;这个心怎么写&#xff0c;我们先写一个正方形&#xff0c;再令一个圆形前移: 再来一…

Linux中YUM仓库的配置

Linux软件包的管理 YUM仓库是什么YUM的常用命令修改YUM源其实CentOS7已经对YUM做了优化 YUM仓库是什么 之前传统RPM的管理方式 可以简单理解为写Java的时候不用Maven管理 jar包都要自己手动去导入 去下载 但是配置好YUM仓库 就放佛在用Maven管理Java项目 基于RPM包管理 能够从…

Python导入类说一说

要在Python中导入一个类&#xff0c;需要使用import关键字。 详细去看下面的代码 1、多例类 class Restaurant:餐馆类def __init__(self,restaurant_name,cuisine_type):#类的属性self.restaurant_name restaurant_nameself.cuisine_type cuisine_type# self.stregth_leve…

2024软件测试应该学什么?“我“怎么从功能转入自动化测试?

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、软件测试应该学…

TypeScript编译选项

编译单个文件&#xff1a;终端 tsc 文件名 自动编译单个文件&#xff1a;终端 tsc 文件名 -w 编译整个项目&#xff1a;tsc 前提是得有ts的配置文件tsconfig.json 自动编译整个项目&#xff1a;tsc --w tsconfig.json默认文件内容&#xff1a; tsconfig.json是ts编译器的配…

代码随想录算法训练营Day45 ||leetCode 70. 爬楼梯 (进阶)|| 322. 零钱兑换 || 279.完全平方数

70. 爬楼梯 &#xff08;进阶&#xff09; 本质上和leetcode377一样 #include <iostream> #include <vector> using namespace std; int main() {int n, m;while (cin >> n >> m) {vector<int> dp(n 1, 0);dp[0] 1;for (int i 1; i < n; i…

【MySQL 系列】MySQL 索引篇

在 MySQL 中&#xff0c;索引是一种帮助存储引擎快速获取数据的数据结构&#xff0c;形象的说就是索引是数据的目录。它一般是以包含索引键值和一个指向索引键值对应数据记录物理地址的指针的节点的集合的清单的形式存在。通过使用索引&#xff0c; MySQL 可以在不需要扫描整个…

『scrapy爬虫』03. 爬取多个页面(详细注释步骤)

目录 1. 分析网页试着拿到多个页面的url2. 抓取250个电影3. start_requests的使用4. 代码规范导库的优化关于重写最终修改后的代码 总结 欢迎关注 『scrapy爬虫』 专栏&#xff0c;持续更新中 欢迎关注 『scrapy爬虫』 专栏&#xff0c;持续更新中 1. 分析网页试着拿到多个页面…

关于tcp协议

目录 前言&#xff1a; 一、TCP协议的基本概念&#xff1a; 二、TCP协议的主要特点&#xff1a; 2.1面向连接&#xff1a; 2.2可靠传输&#xff1a; 2.3基于字节流&#xff1a; 三、TCP连接的建立与终止&#xff1a; 3.1连接建立&#xff1a; 3.1.1SYN&#xff1a; 3…

MyBatis3源码深度解析(十一)MyBatis常用工具类(四)ObjectFactoryProxyFactory

文章目录 前言3.6 ObjectFactory3.7 ProxyFactory3.8 小结 前言 本节研究ObjectFactory和ProxyFactory的基本用法&#xff0c;因为它们在MyBatis的源码中比较常见。这里不深究ObjectFactory和ProxyFactory的源码&#xff0c;而是放到后续章节再展开。 3.6 ObjectFactory Obj…

朴素贝叶斯 | 多分类问题

目录 一. 贝叶斯公式的推导二. 朴素贝叶斯1. 离散的朴素贝叶斯朴素贝叶斯导入示例 离散的朴素贝叶斯训练 2. 连续的朴素贝叶斯3. 伯努利朴素贝叶斯4. 多项式朴素贝叶斯4.1 Laplace平滑4.2 Lidstone平滑 三. 概率图模型1. 贝叶斯网络(Bayesian Network)1.1 全连接贝叶斯网络1.2 …

中国城市统计年鉴、中国县域统计年鉴、中国财政统计年鉴、中国税务统计年鉴、中国科技统计年鉴、中国卫生统计年鉴​

统计年鉴是指以统计图表和分析说明为主&#xff0c;通过高度密集的统计数据来全面、系统、连续地记录年度经济、社会等各方面发展情况的大型工具书来获取统计数据资料。 统计年鉴是进行各项经济、社会研究的必要前提。而借助于统计年鉴&#xff0c;则是研究者常用的途径。目前国…

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…

Prompt Learning:人工智能的新篇章

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

安装MySQL8.0及以上版本操作步骤

关于mysql安装过程中命令mysqld --initialize --console出错的解答 C:\mysql-8.3.0-winx64\bin>mysqld --initialize --usermysql --console 2024-03-12T11:21:23.201387Z 0 [System] [MY-015017] [Server] MySQL Server Initialization - start. 2024-03-12T11:21:23.2068…

【05】消失的数字

hellohello~这里是土土数据结构学习笔记&#x1f973;&#x1f973; &#x1f4a5;个人主页&#xff1a;大耳朵土土垚的博客 &#x1f4a5;所属专栏&#xff1a;C语言函数实现 感谢大家的观看与支持&#x1f339;&#x1f339;&#x1f339; 有问题可以写在评论区或者私信我哦…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Text)

显示一段文本的组件。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 可以包含Span和ImageSpan子组件。 接口 Text(content?: string | Resource, value?: TextOptions) 从API versi…