摄影工作室网站模板,南宁制作网站公司,wordpress文章评论数,wordpress 简介
MyBatis-Plus 简称 MP是一个 MyBatis 的增强工具在 MyBatis 的基础上只做增强不做改变为简化开发、提高效率而生。 他们的愿景是成为 MyBatis 最好的搭档就像魂斗罗中的 1P、2P基友搭配效率翻倍。
特性 无侵入只做增强不做改变引入它不会对现有工程产生影响如丝般顺滑 损耗小启动即会自动注入基本 CURD性能基本无损耗直接面向对象操作 强大的 CRUD 操作内置通用 Mapper、通用 Service仅仅通过少量配置即可实现单表大部分 CRUD 操作更有强大的条件构造器满足各类使用需求 支持 Lambda 形式调用通过 Lambda 表达式方便的编写各类查询条件无需再担心字段写错 支持主键自动生成支持多达 4 种主键策略内含分布式唯一 ID 生成器 - Sequence可自由配置完美解决主键问题 内置代码生成器采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码支持模板引擎更有超多自定义配置等您来使用 内置分页插件基于 MyBatis 物理分页开发者无需关心具体操作配置好插件之后写分页等同于普通 List 查询 分页插件支持多种数据库支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
支持数据库 任何能使用 MyBatis 进行 CRUD, 并且支持标准 SQL 的数据库具体支持情况如下如果不在下列表查看分页部分教程 PR 您的支持。 MySQLOracleDB2H2HSQLSQLitePostgreSQLSQLServerPhoenixGauss ClickHouseSybaseOceanBaseFirebirdCubridGoldilockscsiidbinformixTDengineredshift 达梦数据库虚谷数据库人大金仓数据库南大通用(华库)数据库南大通用数据库神通数据库瀚高数据库优炫数据库。
框架结构 SSM传统编程模式 快速开始
现有一张User表
-- 用户表user
DROP TABLE IF EXISTS user;
CREATE TABLE user (id BIGINT(20) PRIMARY KEY AUTO_INCREMENT COMMENT 主键,name VARCHAR(30) DEFAULT NULL COMMENT 姓名,age INT(11) DEFAULT NULL COMMENT 年龄,email VARCHAR(50) DEFAULT NULL COMMENT 邮箱,manager_id BIGINT(20) DEFAULT NULL COMMENT 直属上级id,create_time DATETIME(0) DEFAULT NULL COMMENT 创建时间,update_time DATETIME DEFAULT NULL COMMENT 修改时间,version INT(11) DEFAULT 1 COMMENT 版本,deleted INT(1) DEFAULT 0 COMMENT 逻辑删除标识(0.未删除,1.已删除)
) ENGINEINNODB CHARSETUTF8;
INSERT INTO user(id,name,age,email,manager_id,create_time) VALUES
(1087982257332887553, 徐源, 28, bossbaomidou.com, NULL, 2023-06-28 09:20:20),
(1088248166370832385, Tony, 25, wtfbaomidou.com, 1087982257332887553, 2023-06-28 11:12:22),
(1088250446457389058, Jack, 28, lywbaomidou.com, 1088248166370832385, 2023-06-28 08:31:16),
(1094590409767661570, Rose, 31, zjqbaomidou.com, 1088248166370832385, 2023-06-27 09:15:15),
(1094592041087729666, Lemon, 32, lhmbaomidou.com, 1088248166370832385, 2023-06-28 09:48:16);项目添加依赖
propertiesjava.version1.8/java.versionmybatis-plus.version3.3.2/mybatis-plus.version
/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdscoperuntime/scope/dependencydependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion${mybatis-plus.version}/version/dependency
/dependencies配置
在 application.yml 配置文件中添加数据库的相关配置
# 配置数据源
spring:datasource:url: jdbc:mysql://121.89.226.84:3306/mybatis-plus?useUnicodetruecharacterEncodingutf8serverTimezoneUTCdriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: root# 配置日志
logging:level:root: warncom.xkw.mp.crud.mapper: tracepattern:console: %p%m%nmybatis-plus:mapper-locations: [classpath:/mapper/*Mapper.xml]在 Spring Boot 启动类中添加 MapperScan 注解扫描 Mapper 文件夹
SpringBootApplication
MapperScan(com.xkw.mp.crud.mapper)
public class CrudApp {public static void main(String[] args) {SpringApplication.run(ServiceApp.class, args);}
}编码
编写实体类 User.java此处使用了 Lombok简化代码
Data
public class User {private Long id;private String name;private Integer age;private String email;private Long managerId;private LocalDateTime createTime;private LocalDateTime updateTime;// 乐观锁注解Versionprivate Integer version 1;// 逻辑删除标识(0.未删除,1.已删除)TableLogicTableField(select false)private Integer deleted 0;
}编写 Mapper 包下的 UserMapper接口
public interface UserMapper extends BaseMapperUser {}编写 Service 包下的 IUserService接口非必须
public interface IUserService extends IServiceUser {}Service
public class UserService extends ServiceImplUserMapper, User implements IUserService {}测试
RunWith(SpringRunner.class)
SpringBootTest(classes CrudApp.class)
Slf4j
public class SampleTest {AutowiredUserMapper userMapper;AutowiredIUserService userService;Testpublic void testSelect() {ListUser users01 userMapper.selectList(null);Assert.assertEquals(8, users01.size());users01.forEach(user - log.info(user.getName()));ListUser users02 userService.list();Assert.assertEquals(8, users02.size());users02.forEach(user - log.info(user.getName()));}
}以上代码可以通过代码生成器快速生成并且可以定制化生成 AutoGenerator 是 MyBatis-Plus 的代码生成器通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码极大的提升了开发效率。 CRUD
通用Mapper
通用 CRUD 封装BaseMapper 接口为 Mybatis-Plus 启动时自动解析实体表关系映射转换为 Mybatis 内部对象注入容器
泛型 T 为任意实体对象
参数 Serializable 为任意类型主键 Mybatis-Plus 不推荐使用复合主键约定每一张表都有自己的唯一 id 主键
对象 Wrapper 为 条件构造器。
/*** Mapper 继承该接口后无需编写 mapper.xml 文件即可获得CRUD功能* p这个 Mapper 支持 id 泛型/p** author hubin* since 2016-01-23*/
public interface BaseMapperT extends MapperT {/*** 插入一条记录** param entity 实体对象*/int insert(T entity);/*** 根据 ID 删除** param id 主键ID*/int deleteById(Serializable id);/*** 根据 columnMap 条件删除记录** param columnMap 表字段 map 对象*/int deleteByMap(Param(Constants.COLUMN_MAP) MapString, Object columnMap);/*** 根据 entity 条件删除记录** param wrapper 实体对象封装操作类可以为 null*/int delete(Param(Constants.WRAPPER) WrapperT wrapper);/*** 删除根据ID 批量删除** param idList 主键ID列表(不能为 null 以及 empty)*/int deleteBatchIds(Param(Constants.COLLECTION) Collection? extends Serializable idList);/*** 根据 ID 修改** param entity 实体对象*/int updateById(Param(Constants.ENTITY) T entity);/*** 根据 whereEntity 条件更新记录** param entity 实体对象 (set 条件值,可以为 null)* param updateWrapper 实体对象封装操作类可以为 null,里面的 entity 用于生成 where 语句*/int update(Param(Constants.ENTITY) T entity, Param(Constants.WRAPPER) WrapperT updateWrapper);/*** 根据 ID 查询** param id 主键ID*/T selectById(Serializable id);/*** 查询根据ID 批量查询** param idList 主键ID列表(不能为 null 以及 empty)*/ListT selectBatchIds(Param(Constants.COLLECTION) Collection? extends Serializable idList);/*** 查询根据 columnMap 条件** param columnMap 表字段 map 对象*/ListT selectByMap(Param(Constants.COLUMN_MAP) MapString, Object columnMap);/*** 根据 entity 条件查询一条记录** param queryWrapper 实体对象封装操作类可以为 null*/T selectOne(Param(Constants.WRAPPER) WrapperT queryWrapper);/*** 根据 Wrapper 条件查询总记录数** param queryWrapper 实体对象封装操作类可以为 null*/Integer selectCount(Param(Constants.WRAPPER) WrapperT queryWrapper);/*** 根据 entity 条件查询全部记录** param queryWrapper 实体对象封装操作类可以为 null*/ListT selectList(Param(Constants.WRAPPER) WrapperT queryWrapper);/*** 根据 Wrapper 条件查询全部记录** param queryWrapper 实体对象封装操作类可以为 null*/ListMapString, Object selectMaps(Param(Constants.WRAPPER) WrapperT queryWrapper);/*** 根据 Wrapper 条件查询全部记录* p注意 只返回第一个字段的值/p** param queryWrapper 实体对象封装操作类可以为 null*/ListObject selectObjs(Param(Constants.WRAPPER) WrapperT queryWrapper);/*** 根据 entity 条件查询全部记录并翻页** param page 分页查询条件可以为 RowBounds.DEFAULT* param queryWrapper 实体对象封装操作类可以为 null*/E extends IPageT E selectPage(E page, Param(Constants.WRAPPER) WrapperT queryWrapper);/*** 根据 Wrapper 条件查询全部记录并翻页** param page 分页查询条件* param queryWrapper 实体对象封装操作类*/E extends IPageMapString, Object E selectMapsPage(E page, Param(Constants.WRAPPER) WrapperT queryWrapper);
}通用Service
通用 Service CRUD 封装IService接口进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆
泛型 T 为任意实体对象
建议如果存在自定义通用 Service 方法的可能请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类
对象 Wrapper 为 条件构造器。
Save
// 插入一条记录选择字段策略插入
boolean save(T entity);
// 插入批量
boolean saveBatch(CollectionT entityList);
// 插入批量
boolean saveBatch(CollectionT entityList, int batchSize);通用 insertBatch 为什么放在 service 层处理 SQL 长度有限制海量数据量单条 SQL 无法执行就算可执行也容易引起内存泄露 JDBC 连接超时等 不同数据库对于单条 SQL 批量语法不一样不利于通用 目前的解决方案循环预处理批量提交虽然性能比单 SQL 慢但是可以解决以上问题。
SaveOrUpdate
// TableId 注解存在更新记录否插入一条记录
boolean saveOrUpdate(T entity);
// 根据updateWrapper尝试更新否继续执行saveOrUpdate(T)方法
boolean saveOrUpdate(T entity, WrapperT updateWrapper);
// 批量修改插入
boolean saveOrUpdateBatch(CollectionT entityList);
// 批量修改插入
boolean saveOrUpdateBatch(CollectionT entityList, int batchSize);Remove
// 根据 queryWrapper 设置的条件删除记录
boolean remove(WrapperT queryWrapper);
// 根据 ID 删除
boolean removeById(Serializable id);
// 根据 columnMap 条件删除记录
boolean removeByMap(MapString, Object columnMap);
// 删除根据ID 批量删除
boolean removeByIds(Collection? extends Serializable idList);Update
// 根据 UpdateWrapper 条件更新记录 需要设置sqlset
boolean update(WrapperT updateWrapper);
// 根据 whereWrapper 条件更新记录
boolean update(T updateEntity, WrapperT whereWrapper);
// 根据 ID 选择修改
boolean updateById(T entity);
// 根据ID 批量更新
boolean updateBatchById(CollectionT entityList);
// 根据ID 批量更新
boolean updateBatchById(CollectionT entityList, int batchSize);Get
// 根据 ID 查询
T getById(Serializable id);
// 根据 Wrapper查询一条记录。结果集如果是多个会抛出异常随机取一条加上限制条件 wrapper.last(LIMIT 1)
T getOne(WrapperT queryWrapper);
// 根据 Wrapper查询一条记录
T getOne(WrapperT queryWrapper, boolean throwEx);
// 根据 Wrapper查询一条记录
MapString, Object getMap(WrapperT queryWrapper);
// 根据 Wrapper查询一条记录
V V getObj(WrapperT queryWrapper, Function? super Object, V mapper);List
// 查询所有
ListT list();
// 查询列表
ListT list(WrapperT queryWrapper);
// 查询根据ID 批量查询
CollectionT listByIds(Collection? extends Serializable idList);
// 查询根据 columnMap 条件
CollectionT listByMap(MapString, Object columnMap);
// 查询所有列表
ListMapString, Object listMaps();
// 查询列表
ListMapString, Object listMaps(WrapperT queryWrapper);
// 查询全部记录
ListObject listObjs();
// 查询全部记录
V ListV listObjs(Function? super Object, V mapper);
// 根据 Wrapper 条件查询全部记录
ListObject listObjs(WrapperT queryWrapper);
// 根据 Wrapper 条件查询全部记录
V ListV listObjs(WrapperT queryWrapper, Function? super Object, V mapper);Page
// 无条件分页查询
IPageT page(IPageT page);
// 条件分页查询
IPageT page(IPageT page, WrapperT queryWrapper);
// 无条件分页查询
IPageMapString, Object pageMaps(IPageT page);
// 条件分页查询
IPageMapString, Object pageMaps(IPageT page, WrapperT queryWrapper);Chain
query
// 链式查询 普通
QueryChainWrapperT query();
// 链式查询 lambda 式。注意不支持 Kotlin
LambdaQueryChainWrapperT lambdaQuery();// 示例
query().eq(column, value).one();
lambdaQuery().eq(Entity::getId, value).list();update
/ 链式更改 普通
UpdateChainWrapperT update();
// 链式更改 lambda 式。注意不支持 Kotlin
LambdaUpdateChainWrapperT lambdaUpdate();// 示例
update().eq(column, value).remove();
lambdaUpdate().eq(Entity::getId, value).update(entity);条件构造器
MyBatis-Plus条件构造器MyBatis-Plus Condition Wrapper是 MyBatis-Plus 框架提供的一个强大的查询条件构建工具用于在 SQL 查询中添加多个动态条件
通过使用条件构造器您可以方便地根据不同的情况动态构建查询条件并且避免了手动编写复杂的 SQL 语句。它提供了一套简单直观的 API使得构建和管理查询条件变得更加灵活和高效。
使用条件构造器您可以轻松实现以下常见操作
等值判断 eq(column, value) 示例
QueryWrapperUser query new QueryWrapper();
query.eq(name, Tony);不等值判断 ne(column, value) 示例
QueryWrapperUser query new QueryWrapper();
query.ne(age, 28);大于判断 gt(column, value)示例gt(“salary”, 5000)
小于判断 lt(column, value)示例lt(“create_time”, LocalDateTime.now())
范围判断 between(column, value1, value2)示例between(“price”, 100, 200)
模糊查询 like(column, value)示例like(“title”, “%keyword%”)
除此之外条件构造器还支持排序、分页、子查询等功能以及各种逻辑运算符与、或、非、函数计算等高级操作。
Lambda条件构造器
在 MyBatis-Plus 中除了常规的条件构造器外还提供了一种更为简洁、方便的 Lambda 条件构造器Lambda Query Wrapper
使用 Lambda 条件构造器您可以通过 Java 8 中引入的 Lambda 表达式来编写查询条件
使用 Lambda 条件构造器能够让代码更加精炼并且具有语法高亮和类型安全性防误写
下面是几个示例
等值判断 eq(column, value) 示例
LambdaQueryWrapperUser lambdaQuery Wrappers.lambdaQuery();
lambdaQuery.eq(User::getName, Tony);不等值判断 ne(column, value) 示例
LambdaQueryWrapperUser lambdaQuery Wrappers.lambdaQuery();
lambdaQuery.ne(User::getAge, 28);大于判断wrapper.gt(User::getSalary, 5000)
小于判断wrapper.lt(User::getCreateTime, LocalDateTime.now())
范围判断wrapper.between(User::getPrice, 100, 200)
模糊查询wrapper.like(User::getTitle, %keyword%)
Lambda 条件构造器还支持排序、分页、子查询等功能以及各种逻辑运算符与、或、非、函数计算等高级操作使得条件拼接变得更加灵活和简单。
通过使用 MyBatis-Plus 的 Lambda 条件构造器您可以轻松实现复杂查询减少手动编写 SQL 的工作量并且更容易维护和理解代码。它大大简化了查询条件的构建过程提升了开发效率和代码质量。
Case
查询徐姓或者年龄大于等于25按照年龄降序排列年龄相同则按照id升序排列
SQLname like ‘徐%’ or age25 order by age desc,id asc
QueryWrapperUser query new QueryWrapper();
query.likeRight(name, 徐).or().ge(age, 25).orderByDesc(age).orderByAsc(id);
ListUser list userMapper.selectList(query);
list.forEach(System.out::println);LambdaQueryWrapperUser lambdaQuery Wrappers.lambdaQuery();
lambdaQuery.likeRight(User::getName, 徐).or().ge(User::getAge, 25).orderByDesc(User::getAge).orderByAsc(User::getId);
ListUser list02 userMapper.selectList(lambdaQuery);
list02.forEach(System.out::println);ListUser list03 userService.lambdaQuery().likeRight(User::getName, 涂).or().ge(User::getAge, 25).orderByDesc(User::getAge).orderByAsc(User::getId).list();
list03.forEach(System.out::println);/**
* DEBUG Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE (name LIKE ? OR age ?) ORDER BY age DESC,id ASC
* DEBUG Parameters: 徐%(String), 20(Integer)
*/查询徐姓或者龄小于40并且年龄大于28并且邮箱不为空
SQLname like ‘徐%’ or (age 40 and age 28 and email is not null)
QueryWrapperUser query new QueryWrapper();
query.likeRight(name, 徐).or(q - q.between(age, 28, 40).isNotNull(email));
ListUser list01 userMapper.selectList(query);
list01.forEach(System.out::println);LambdaQueryWrapperUser lambdaQuery Wrappers.lambdaQuery();
lambdaQuery.likeRight(User::getName, 徐).or(q - q.between(User::getAge, 28, 40).isNotNull(User::getName));
ListUser list02 userMapper.selectList(lambdaQuery);
list02.forEach(System.out::println);/**
* DEBUG Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE (name LIKE ? OR (age BETWEEN ? AND ? AND name IS NOT NULL))
* DEBUG Parameters: 徐%(String), 28(Integer), 40(Integer)
*/查询指定字段名字中包含雨并且年龄小于40
QueryWrapperUser query new QueryWrapper();
query.select(name, age).like(name, 雨).lt(age, 40);
ListUser list01 userMapper.selectList(query);
list01.forEach(System.out::println);LambdaQueryWrapperUser lambdaQuery Wrappers.lambdaQuery();
lambdaQuery.select(User::getName, User::getAge).like(User::getName, 雨).lt(User::getAge, 40);
ListUser list02 userMapper.selectList(lambdaQuery);
list02.forEach(System.out::println);/**
* DEBUG Preparing: SELECT name,age FROM user WHERE (name LIKE ? AND age ?)
* DEBUG Parameters: %雨%(String), 40(Integer)
*/查询创建日期为2023年06月28日并且直属上级为徐姓
SQLdate_format(create_time, ‘%Y-%m-%d’)‘2023-06-28’ and manager_id in (select id from user where name like ‘徐%’)
QueryWrapperUser query new QueryWrapper();
query.apply(DATE_FORMAT(create_time,%Y-%m-%d){0}, 2023-06-28).inSql(manager_id, SELECT id FROM user WHERE name LIKE 徐%);
ListUser list01 userMapper.selectList(query);
list01.forEach(System.out::println);LambdaQueryWrapperUser lambdaQuery Wrappers.lambdaQuery();
lambdaQuery.apply(DATE_FORMAT(create_time,%Y-%m-%d){0}, 2023-06-28).inSql(User::getManagerId, SELECT id FROM user WHERE name LIKE 徐%);
ListUser list02 userMapper.selectList(lambdaQuery);
list02.forEach(System.out::println);/**
* DEBUG Preparing: SELECT id,name,age,email,manager_id,create_time FROM user WHERE (DATE_FORMAT(create_time,%Y-%m-%d)? AND manager_id IN (SELECT id FROM user WHERE name LIKE 徐%))
* DEBUG Parameters: 2023-06-28(String)
*/主键策略
MyBatis-Plus 提供了一种灵活且开发友好的主键策略可以方便地生成和管理实体类的主键。以下是 MyBatis-Plus 主要支持的几种主键策略 自增主键AUTO_INCREMENT这是最常见的主键策略之一数据库会自动为每条记录分配一个唯一的递增值作为主键 UUID 主键使用 Universally Unique Identifier (UUID) 作为唯一标识符在插入数据时会自动生成一个长度为 32 的唯一字符串并将其作为主键 分布式 ID提供了多种分布式 ID 策略如雪花算法 Snowflake、Leaf、Ulid 等。这些策略通过在不同节点之间进行协调来产生全局唯一的 ID 自定义主键生成器允许开发者根据具体业务需求自定义主键生成逻辑只需要实现 KeyGenerator 接口并配置到相应的实体上即可。
对于自增主键MyBatis-Plus 在默认情况下会假设数据库会返回自动生成的主键并自动将其设置回实体对象中。
若您使用的数据库无法自动生成主键或想手动控制主键生成过程可以设置 TableId注解的 type 属性为 IdType.INPUT然后通过编码方式设置主键值。
对于 UUID 主键或分布式 ID只需使用 TableId注解指定主键生成策略为IdType.UUID 或 IdType.ID_WORKER 等即可。MyBatis-Plus 将会在插入数据时自动生成相应的主键。
若想自定义主键生成逻辑可以实现 KeyGenerator 接口并将该自定义主键生成器注入到 IOC 容器中。然后在实体类的 TableId 注解中设置 generator 属性为自定义主键生成器的名字。
总而言之MyBatis-Plus 提供了多种灵活且易用的主键策略能够满足不同项目和业务场景下的需求。开发者可以根据具体情况选择合适的主键生成方式并通过简单的配置即可轻松管理实体对象的主键。
插件
分页插件
分页插件PaginationInterceptor分页是在大部分应用中都需要处理的需求之一MyBatis-Plus 的分页插件可以帮助我们方便地进行数据库查询结果的分页操作。通过使用该插件我们可以很容易地实现分页查询并获取分页结果。
Bean
ConditionalOnMissingBean
public PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor new PaginationInterceptor();// 单页限制 500 条小于 0 如 -1 不受限制paginationInterceptor.setLimit(-1);// 溢出总页数后是否进行处理(默认不处理)paginationInterceptor.setOverflow(true);return paginationInterceptor;
}LambdaQueryWrapperUser query new LambdaQueryWrapper();
query.ge(User::getAge, 26).orderByDesc(User::getCreateTime);
PageUser page new Page(1, 2);
IPageMapString, Object iPage userMapper.selectMapsPage(page, query);
System.out.println(总页数 page.getPages());
System.out.println(总记录数 iPage.getTotal());
ListMapString, Object list iPage.getRecords();
list.forEach(System.out::println);乐观锁插件
数据库乐观锁插件OptimisticLockerInterceptor乐观锁是一种并发控制机制通过在数据表中添加一个版本号字段实现。MyBatis-Plus 的乐观锁插件可以自动处理带有乐观锁的更新操作确保并发情况下不会出现数据被覆盖或丢失的问题。
/*** 引入乐观锁插件*/
Bean
ConditionalOnMissingBean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){return new OptimisticLockerInterceptor();
}Data
public class User {/*** 乐观锁注解*/Versionprivate Integer version;
}User user userMapper.selectById(1673973063479255042L);
user.setAge(30);System.out.println(开始休息在修改的过程中有其他用户对数据进行了更新...);
Thread.sleep(10000);
System.out.println(去数据库修改该条数据并将version增加一个版本);int rows userMapper.updateById(user);
System.out.println(影响行数rows);/**
* DEBUG Preparing: SELECT id,name,age,email,manager_id,create_time,update_time,version,deleted FROM user WHERE id?
* DEBUG Parameters: 1673973063479255042(Long)
* TRACE Columns: id, name, age, email, manager_id, create_time, update_time, version, deleted
* TRACE Row: 1673973063479255042, 王二狗, 30, null, 1088248166370832385, 2023-06-28 00:34:14, null, 5, 0
* DEBUG Total: 1
* 开始休息在修改的过程中有其他用户对数据进行了更新...
* 去数据库修改该条数据并将version增加一个版本
* DEBUG Preparing: UPDATE user SET name?, age?, manager_id?, create_time?, version?, deleted? WHERE id? AND version?
* DEBUG Parameters: 王二狗(String), 30(Integer), 1088248166370832385(Long), 2023-06-28T00:34:14(LocalDateTime), 6(Integer), 0(Integer), 1673973063479255042(Long), 5(Integer)
* DEBUG Updates: 0
* 影响行数0
*/