张家港市建设局网站/营销型网站建设设计
MyBatis-Plus
1、mybatis-plus介绍
官网:https://baomidou.com/
MyBatis-Plus (简称 MP)是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。MyBatis-Plus提供了通用的mapper和service,可以在不编写任何SQL语句的情况下,快速的实现对单表的CRUD批量、逻辑删除、分页等操作。
1.1、特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
- 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
- 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
1.2、支持数据库
任何能使用 MyBatis
进行 CRUD, 并且支持标准 SQL 的数据库,具体支持情况如下,如果不在下列表查看分页部分教程 PR 您的支持。
- MySQL,Oracle,DB2,H2,HSQL,SQLite,PostgreSQL,SQLServer,Phoenix,Gauss ,ClickHouse,Sybase,OceanBase,Firebird,Cubrid,Goldilocks,csiidb,informix,TDengine,redshift
- 达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库,优炫数据库
1.3、代码托管
- gitee:https://gitee.com/baomidou/mybatis-plus
- GitHub:https://github.com/baomidou/mybatis-plus
2、入门案例
2.1、开发环境
IDE: IDEA 2023.1
JDK:JDK8+
构建工具:maven3.8.3
MySQL版本:MySQL5.5.27
SpringBoot:2.7.6
mybatis-plus:3.5.3.1
2.2、创建数据库及表
-- 创建数据库
CREATE DATABASE mybatisplus;
-- 使用数据库
USE mybatisplus;
-- 创建表
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)
);
2.3、表中添加数据
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.4、创建SpringBoot项目
使用Spring Initializr
快速构建SpringBoot项目
项目中没有用途的几个文件可以删除
2.5、添加依赖
<?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 https://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>2.7.6</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.qbzaixian</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><name>demo</name><description>demo</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!-- mybatis-plus的启动器--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version></dependency><!-- lombok简化实体类开发,需要idea安装lombok的插件哦~~~--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.8</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.34</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
2.6、配置文件
spring:datasource:# 配置数据源类型type: com.zaxxer.hikari.HikariDataSource# 配置连接数据库的驱动driver-class-name: com.mysql.jdbc.Driver# 配置连接数据库的urlurl: jdbc:mysql://127.0.0.1:3306/mybatisplus?characterEncoding=utf-8&useSSL=false# 配置连接数据库的账号username: root# 配置连接数据库的密码password: 123
2.7、配置实体类
@Data
public class User {private Long id;private String name;private Integer age;private String email;
}
2.8、编写mapper接口
mybatis-plus提供了BaseMapper接口,其中提供大量的CRUD的方法,我们的接口只需要去继承这个接口,基本就可以实现对单表的CRUD操作。接口的泛型对应编写的实体类
public interface UserMapper extends BaseMapper<User> {
}
2.9、编写启动类
在 Spring Boot 启动类中添加
@MapperScan
注解,扫描 Mapper 文件夹:
@SpringBootApplication
@MapperScan("com.qbzaixian.mapper")
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}
2.10、编写测试类
UserMapper 中的
selectList()
方法的参数为 MP 内置的条件封装器Wrapper
,所以不填写就是无任何条件
@SpringBootTest
public class UserTest {@Autowiredprivate UserMapper userMapper;@Testpublic void testSelect(){// 接口中提供的selectList参数为条件,如果没有设置为nullList<User> users = this.userMapper.selectList(null);users.forEach(System.out::println);}
}
这里我选择降低SpringBoot的版本为2.7.6来解决这个问题。
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.6</version><relativePath/> <!-- lookup parent from repository --></parent
运行测试类,看到如下测试结果
通过以上几个简单的步骤,我们就实现了 User 表的 CRUD 功能,甚至连 XML 文件都不用编写!
从以上步骤中,我们可以看到集成
MyBatis-Plus
非常的简单,只需要引入 starter 工程,并配置 mapper 扫描路径即可。
2.11、添加日志
希望看到mybatis-plus执行的具体过程和对应的sql语句,需要简单的配置mybatis-plus的日志即可
# 配置日志
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3、BaseMapper接口
mybatis-plus提供了BaseMapper接口,其中提供大量的CRUD的方法,我们的接口只需要去继承这个接口,基本就可以实现对单表的CRUD操作。接口的泛型对应编写的实体类
3.1、insert插入
参数说明
类型 参数名 描述 T entity 实体对象 // 插入一条记录 int insert(T entity);
// BaseMapper的新增功能// INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )@Testpublic void testInset(){User user = new User();user.setAge(20);user.setName("赵四");user.setEmail("zhaosi@qq.com");int result = this.userMapper.insert(user);System.out.println(result);}
温馨提示:添加完成之后,添加的新对象也会进行ID回显的,但ID值默认采用的是雪花算法计算出来的数据,不是一个自增长的值。
3.2、delete删除
参数说明
类型 参数名 描述 Wrapper wrapper 实体对象封装操作类(可以为 null) Collection<? extends Serializable> idList 主键 ID 列表(不能为 null 以及 empty) Serializable id 主键 ID Map<String, Object> columnMap 表字段 map 对象 // 根据 entity 条件,删除记录 int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper); // 删除(根据ID 批量删除) int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根据 ID 删除 int deleteById(Serializable id); // 根据 columnMap 条件,删除记录 int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// BaseMapper的删除功能@Testpublic void testDel(){// 根据id删除 DELETE FROM user WHERE id=?int i = this.userMapper.deleteById(1L);// 根据实体对象的id删除 DELETE FROM user WHERE id=?User user= new User();user.setId(2L);int i2 = this.userMapper.deleteById(user);// 根据Collection集合删除(id列表)// DELETE FROM user WHERE id IN ( ? , ? , ? )List<Long> list = Arrays.asList(2L, 3L, 4L);this.userMapper.deleteBatchIds(list);// 根据指定的列属性删除// DELETE FROM user WHERE name = ? AND age = ?Map<String,Object> map = new HashMap<>();map.put("age",20);map.put("name","赵四");int i3 = this.userMapper.deleteByMap(map);}
3.3、update修改
在调用
updateById
方法前,需要在T entity
(对应的实体类)中的主键属性上加上@TableId
注解。参数说明
类型 参数名 描述 T entity 实体对象 (set 条件值,可为 null) Wrapper updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句) // 根据 whereWrapper 条件,更新记录 int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper); // 根据 ID 修改 int updateById(@Param(Constants.ENTITY) T entity);
@Testpublic void testUpdate(){// 根据id修改数据 UPDATE user SET name=?, email=? WHERE id=?User user = new User();user.setId(5L);user.setName("乔治");user.setEmail("qiaozhi@.qq.com");int i = this.userMapper.updateById(user);}
3.4、select查询
参数说明
类型 参数名 描述 Serializable id 主键 ID Wrapper queryWrapper 实体对象封装操作类(可以为 null) Collection<? extends Serializable> idList 主键 ID 列表(不能为 null 以及 empty) Map<String, Object> columnMap 表字段 map 对象 IPage page 分页查询条件(可以为 RowBounds.DEFAULT) // 根据 ID 查询 T selectById(Serializable id); // 根据 entity 条件,查询一条记录 T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 查询(根据ID 批量查询) List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根据 entity 条件,查询全部记录 List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询(根据 columnMap 条件) List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); // 根据 Wrapper 条件,查询全部记录 List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值 List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根据 entity 条件,查询全部记录(并翻页) IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询全部记录(并翻页) IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询总记录数 Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
-- 由于前面演示删除,修改等操作,数据库中的数据已经不多了,现在给数据库中重新插入部分数据
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');
@Testpublic void testSelectAll(){// selectList:根据条件查询,如果没有条件书写为null// SELECT id,name,age,email FROM userList<User> users = this.userMapper.selectList(null);users.forEach(System.out::println);// selectById:根据id查询// SELECT id,name,age,email FROM user WHERE id=?User user = this.userMapper.selectById(5L);System.out.println(user);// selectCount:查询满足条件的记录数,如果没有条件书写为null// SELECT COUNT( * ) AS total FROM userLong count = this.userMapper.selectCount(null);System.out.println(count);// selectBatchIds:通过多个id列表查询// SELECT id,name,age,email FROM user WHERE id IN ( ? , ? , ? )List<User> userList = this.userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));userList.forEach(System.out::println);// selectByMap:根据指定map中的属性作为查询条件// SELECT id,name,age,email FROM user WHERE name = ? AND age = ?Map<String,Object> map = new HashMap<>();map.put("name","乔治");map.put("age","20");List<User> list = this.userMapper.selectByMap(map);list.forEach(System.out::println);}
3.5、自定义接口方法
mybatis-plus主要是单表的操作,如果我们需要执行多表,或者执行自己书写的sql脚本与接口,mybatis-plus也支持指定的方式。
在mybaits-plus的
MybatisPlusProperties
类中配置相关的属性配置,其中private String[] mapperLocations = new String[]{"classpath*:/mapper/**/*.xml"};
用来加载指定路径下的xml映射文件的,当然如果你不喜欢这个路径,可以在SpringBoot的配置文件中进行修改
mybatis-plus: config-location: 书写自己的路径
在项目
resoureces
目录下,创建mapper
文件夹,在其中创建UserMapper.xml
文件<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.qbzaixian.mapper.UserMapper"><select id="selectUserById" resultType="map">select * from user where id = #{id}</select> </mapper>
在接口中编写对应的查询方法
public interface UserMapper extends BaseMapper<User> {/*** 根据指定的id,查询user数据,返回map集合* @param id* @return*/public Map<String,Object> selectUserById(Long id); }
执行查询操作:
@Testpublic void testMyQuery(){// 测试自定义的接口方法Map<String, Object> map = this.userMapper.selectUserById(1L);System.out.println(map);}
4、通用Service接口
mybatis-plus不仅提供通用的mapper接口,还提供通用的Service接口。
- 通用 Service CRUD 封装IService (opens new window)接口,进一步封装 CRUD 采用
get 查询单行
remove 删除
list 查询集合
page 分页
前缀命名方式区分Mapper
层避免混淆, - 泛型
T
为任意实体对象 - 建议如果存在自定义通用 Service 方法的可能,请创建自己的
IBaseService
继承Mybatis-Plus
提供的基类 - 对象
Wrapper
为 条件构造器
4.1、IService接口
Mybatis-Plus中提供的IService接口和实现类ServiceImpl,封装了常见业务逻辑,可以简单查阅源码
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {// 提供了大量的CRUD相关的方法
}
4.2、测试Service接口
通过前面的mapper接口的CRUD演示,针对Service接口中的大部分方法使用基本一致,这里就简单演示批量插入操作
虽然有ServiceImpl实现类和IService接口,但是大部分情况下还是需要根据对应的业务书写相关接口和实现类
// 在service包下创建UserService接口
public interface UserService extends IService<User> {
}
// 在service.impl包下创建UserServiceImpl实现类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
准备进行测试
@SpringBootTest
public class UserServiceTest {@Autowiredprivate UserServiceImpl userService;@Testpublic void testSaveBatch(){List<User> list = new ArrayList<>();for (int i = 1 ; i < 5 ; i++){User user = new User();user.setName("测试"+i);user.setAge(20+i);user.setEmail("test@163.com");list.add(user);}// 批量给数据库中插入数据,在mapper接口中是没有的boolean b = this.userService.saveBatch(list);System.out.println(b);}
}
5、注解介绍
mybatis-plus提供部分的注解,方便快速的进行表、属性、主键、主键生成策略等进行标注
5.1、@TableName注解
- 描述:表名注解,标识实体类对应的表
- 使用位置:实体类
@Data
@TableName("user")
public class User {private Long id;private String name;private Integer age;private String email;
}
一般用在实体类名与对应的表名不一致的情况下,当然如果项目中整个表名都有对应的前缀,也可以在SpringBoot的核心配置文件进行前缀的配置,省去注解的配置
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 全局配置表名的前缀 global-config:db-config:table-prefix: tb_
5.2、@TableId注解
用于标注当前的实体类的id属性对应表的主键,mybatis-plus默认采用id属性作为主键。
- 描述:主键注解
- 使用位置:实体类主键字段
@Data
@TableName("user")
public class User {@TableIdprivate Long id;private String name;private Integer age;private String email;
}
TableId注解的两个属性作用:
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | “” | 主键字段名 |
type | Enum | 否 | IdType.NONE | 指定主键类型 |
mybatis-plus在设置逐渐的时候ltype属性用来设置主键的生成策略:
@Data @TableName("user") public class User {@TableId(value='对应的表主键列名' , type=主键策略)private Long id;private String name;private Integer age;private String email; }
关于type属性(主键策略)的枚举值IdType的详细介绍:
常用的有:
@TableId(value='对应的表主键列名' , type=idType.AUTO) @TableId(value='对应的表主键列名' , type=idType.ASSIGN_ID)
值 | 描述 |
---|---|
AUTO | 数据库 ID 自增 |
NONE | 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) |
INPUT | insert 前自行 set 主键值 |
ASSIGN_ID | 分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator 的方法nextId (默认实现类为DefaultIdentifierGenerator 雪花算法) |
ASSIGN_UUID | 分配 UUID,主键类型为 String(since 3.3.0),使用接口IdentifierGenerator 的方法nextUUID (默认 default 方法) |
ID_WORKER | 分布式全局唯一 ID 长整型类型(please use ASSIGN_ID ) |
UUID | 32 位 UUID 字符串(please use ASSIGN_UUID ) |
ID_WORKER_STR | 分布式全局唯一 ID 字符串类型(please use ASSIGN_ID ) |
关于主键生成策略,可以在SpringBoot的全局配置文件中进行配置
# 配置日志 mybatis-plus: configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 全局配置 global-config:db-config:# 配置表的名的前缀table-prefix: tb_# 配置主键生成策略id-type: auto
5.3、雪花算法
SnowFlake 中文意思为雪花,故称为雪花算法。最早是 Twitter 公司在其内部用于分布式环境下生成唯一 ID。在2014年开源 scala 语言版本。
雪花算法的原理就是生成一个的 64 位比特位的 long 类型的唯一 id。
- 最高 1 位固定值 0,因为生成的 id 是正整数,如果是 1 就是负数了。
- 接下来 41 位存储毫秒级时间戳,2^41/(1000606024365)=69,大概可以使用 69 年。
- 再接下 10 位存储机器码,包括 5 位 datacenterId 和 5 位 workerId。最多可以部署 2^10=1024 台机器。
- 最后 12 位存储序列号。同一毫秒时间戳时,通过这个递增的序列号来区分。即对于同一台机器而言,同一毫秒时间戳下,可以生成 2^12=4096 个不重复 id。
- 可以将雪花算法作为一个单独的服务进行部署,然后需要全局唯一 id 的系统,请求雪花算法服务获取 id 即可。
对于每一个雪花算法服务,需要先指定 10 位的机器码,这个根据自身业务进行设定即可。例如机房号+机器号,机器号+服务号,或者是其他可区别标识的 10 位比特位的整数值都行。
简单说雪花算法解决的问题:
- 需要选择合适的方案应对数据规模化的增长,以应对逐渐增长的访问压力与数据量。
- 数据库扩展方式主要包括:业务分库、主从复制、数据库分表等。
不同的编程语言,都有雪花算法的实现,可以直接调用对应的程序即可得到一个雪花算法值。
5.4、@TableField注解
作用:字段注解(非主键),解决表的属性名与实体中的属性名不一致问题。
@Data
@TableName("user")
public class User {@TableIdprivate Long id;@TableField("nickname")private String name;private Integer age;private String email;
}
@TableField注解的属性比较多,常用的就是标注列名(即value属性,默认可以不写)
其他的属性如果需要,可以参考官方文档
5.5、@TableLogic注解
作用:表字段逻辑处理注解(逻辑删除)
物理删除:真实删除,将对应的数据从数据库中删除,之后查询不到此条被删除的数据
逻辑删除:假删除,将对应的数据中代表是否删除的字段状态修改
被删除
,在数据库可以查到这条数据记录,只是标记为被删除
使用场景:可以进行数据恢复
@Data
@TableName("user")
public class User {@TableIdprivate Long id;@TableField("nickname")private String name;private Integer age;private String email;@TableLogicprivate Integer isDeleted;
}
注意:
- 上面使用
isDeleted
属性来标准逻辑删除,就需要在表中添加这么一列,可以将未删除的状态设置为0,删除就对应的为1, - 在进行查询时候,默认会添加
where is_deleted = 0
表示只查询未被删除的, - 在逻辑删除的时候,默认会将
is_deleted
的值设置为1
6、条件构造器
6.1、条件构造器介绍
在使用mybatis-plus的时候,发现修改、删除、查询的都对应有Wrapper,他就是用于构建各种条件。是构造条件的顶级父类。
- Wrapper:条件构造器
- AbstractWrapper:用于条件封装,生成sql的where条件
- QueryWrapper:查询条件封装
- UpdateWrapper:修改条件封装
- AbstractLambdaWrapper:使用Lambda语法
- LambdaQueryWrapper:用于Lambda语法使用的查询Wrapper
- LambdaUpdateWrapper:Lambda更新封装Wrapper
- AbstractWrapper:用于条件封装,生成sql的where条件
6.2、QueryWrapper查询条件
@Testpublic void testQueryWrapper(){// 创建QueryWrapper对象,用于构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<>();// 查询姓名中包含字母o,邮箱不为null,年龄大于等于20的wrapper.like("name","o").isNotNull("email").ge("age",20);List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}
最终生成的sql:SELECT id,name,age,email FROM user WHERE (name LIKE ? AND email IS NOT NULL AND age >= ?)
6.3、QueryWrapper排序条件
@Testpublic void testQueryWrapper2(){// 创建QueryWrapper对象,用于构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<>();// 查询年龄在20到30之间的,先按照年龄降序,年龄相同在按照姓名升序wrapper.between("age",20,30).orderByDesc("age").orderByAsc("name");List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}
最终生成的sql:SELECT id,name,age,email FROM user WHERE (age BETWEEN ? AND ?) ORDER BY age DESC,name ASC
6.4、QueryWrapper删除条件
@Testpublic void testQueryWrapper3(){// 创建QueryWrapper对象,用于构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<>();// 删除邮箱为空的数据wrapper.isNull("email");int i = this.userMapper.delete(wrapper);System.out.println(i);}
最终生成的sql:DELETE FROM user WHERE (email IS NULL)
6.5、QueryWrapper实现修改
@Testpublic void testQueryWrapper4(){// 创建QueryWrapper对象,用于构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<>();// 修改name中包含字母a的数据wrapper.like("name","a");User user = new User();user.setAge(26);user.setEmail("bbb@qq.com");int i = this.userMapper.update(user, wrapper);System.out.println(i);}
最终生成的sql:UPDATE user SET age=?, email=? WHERE (name LIKE ?)
6.6、QueryWrapper查询指定的列
@Testpublic void testQueryWrapper5(){// 创建QueryWrapper对象,用于构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<>();// 查询name、age两列数据wrapper.select("name","age");List<Map<String, Object>> maps = this.userMapper.selectMaps(wrapper);maps.forEach(System.out::println);}
最终生成的sql:SELECT name,age FROM user
6.7、QueryWrapper更改查询的条件优先级
默认情况下,使用QueryWrapper进行条件组装的时候,多个条件之间使用的and进行连接,QueryWrapper中提供and和or方法,可以提供指定的查询条件的优先级。
@Testpublic void testQueryWrapper6(){// 创建QueryWrapper对象,用于构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<>();// 查询年龄在20到30之间并且(姓名包含字母a或者邮箱不为null的数据)wrapper.between("age",20,30).and(i->i.like("name","a").or().isNotNull("email"));List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}
最终生成的sql:
SELECT id,name,age,email FROM user WHERE (age BETWEEN ? AND ? AND (name LIKE ? OR email IS NOT NULL))
6.8、QueryWrapper实现子查询
@Testpublic void testQueryWrapper7(){// 创建QueryWrapper对象,用于构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<>();// 查询年龄在20到30之间的所有数据,这里故意采用子查询的方式完成wrapper.inSql("age","select age from user where age >= 20 and age <= 30");List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}
最终生成的sql:
SELECT id,name,age,email FROM user WHERE (age IN (select age from user where age >= 20 and age <= 30))
6.9、使用UpdateWrapper实现修改
虽然QueryWrapper可以完成修改操作的,但是需要传递实体对象,还是有点小麻烦。提供的UpdateWrapper是专门用于完成修改条件和数据封装
@Testpublic void testUpdateWrapper(){// 创建UpdateWrapper对象,用于构建查询条件UpdateWrapper<User> wrapper = new UpdateWrapper<>();// 修改name中包含字母a的数据// 设置修改的的条件wrapper.like("name","a");// 设置需要修改的数据wrapper.set("age",22).set("email","abc@qq.com");int i = this.userMapper.update(null, wrapper);System.out.println(i);}
最终生成的sql:
UPDATE user SET age=?,email=? WHERE (name LIKE ?)
6.10、使用condition组装条件
condition用于在进行条件封装的时候,判断某个值,当这个值不为null的时候,会自动添加对应的条件,如果为null,就不会添加条件
简单说:condition可以动态根据条件组装条件
@Testpublic void testQueryWrapper9(){// 创建QueryWrapper对象,用于构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<>();// 根据指定的数据,添加查询条件,模拟多条件查询的场景String name = "a";Integer age = 20;String email = null;wrapper.like(StringUtils.isNotBlank(name),"name",name).gt(age!=null , "age",age).eq(StringUtils.isNotBlank(email),"email",email);List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}
最终生成的sql:生成的sql中并不包含email,因为email为null
SELECT id,name,age,email FROM user WHERE (name LIKE ? AND age > ?)
6.11、LambdaQueryWrapper
Lambda相关的查询与修改构造器,针对QueryWrapper和UpdateWrapper在构造添加的时候,需要书写表的列名进行优化,通过Lambda相关的条件构造器,可以将对应的条件采用实体类的实型进行编写。
@Testpublic void testQueryWrapper10(){// 创建QueryWrapper对象,用于构建查询条件LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();// 查询姓名中包含字母o,邮箱不为null,年龄大于等于20的wrapper.like(User::getName,"o").isNotNull(User::getEmail).ge(User::getAge,20);List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}
最终生成的sql:生成的sql中并不包含email,因为email为null
SELECT id,name,age,email FROM user WHERE (name LIKE ? AND email IS NOT NULL AND age >= ?)
6.12、LambdaUpdateWrapper
@Testpublic void testUpdateWrapper(){// 创建UpdateWrapper对象,用于构建查询条件LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();// 修改name中包含字母a的数据// 设置修改的的条件wrapper.like(User::getName,"a");// 设置需要修改的数据wrapper.set(User::getAge,22).set(User::getEmail,"abc@qq.com");int i = this.userMapper.update(null, wrapper);System.out.println(i);}
最终生成的sql:生成的sql中并不包含email,因为email为null
UPDATE user SET age=?,email=? WHERE (name LIKE ?)
7、MyBatis-Plus分页
7.1、分页插件的配置和使用
MyBatis-Plus的分页非常简单,只需要添加一个配置类即可,而MyBatis-Plus提供的配置是以插件的方式提供。
@Configuration
public class MyBatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){// 创建拦截器对象MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 设置分页的拦截器,并设置数据库类型为mysqlinterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));// 返回拦截器对象return interceptor;}
}
通过上面代码的配置,就可以使用分页功能
@Testpublic void testPagination(){LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();wrapper.ge(User::getAge,20);Page<User> page = new Page<>(1,3);// 如果没有条件,第二个参数可以设置为nullthis.userMapper.selectPage(page , wrapper);// 查询的结果就放在Page对象中// 获取当前分页总页数System.out.println(page.getPages());// 每页显示条数,默认 10System.out.println(page.getSize());// 当前页System.out.println(page.getCurrent());// 当前满足条件的总数System.out.println(page.getTotal());// 分页查询的数据System.out.println(page.getRecords());}
7.2、自定义分页功能
在实际使用中,难免会出现需要自己书写sql语句,同时还需要使用分页功能,这时就需要自定义分页功能
需要注意:
- 自定义Mapper接口中的方法的第一个参数必须是MybatisPlus提供的分页Page对象。
- 自定义Mapper接口中的方法返回值必须是Page对象
public interface UserMapper extends BaseMapper<User> {@Select("select * from user where age > #{age}")public Page<User> selectUserAndPage(@Param("page") Page<User> page , @Param("age") Integer age);
}
@Testpublic void testPagination2(){Page<User> page = new Page<>(1,3);// 如果没有条件,第二个参数可以设置为nullthis.userMapper.selectUserAndPage(page , 20);// 查询的结果就放在Page对象中// 获取当前分页总页数System.out.println(page.getPages());// 每页显示条数,默认 10System.out.println(page.getSize());// 当前页System.out.println(page.getCurrent());// 当前满足条件的总数System.out.println(page.getTotal());// 分页查询的数据System.out.println(page.getRecords());}
7.3、乐观锁与悲观锁
MySQL中的乐观锁和悲观锁主要区别如下:
- 悲观锁:
- 悲观锁会在更新数据时对数据加锁,避免其他事务对此数据进行更新。
- 通常使用锁定的SELECT…FOR UPDATE语句来实现。
- 在整个更新操作过程中,数据被锁定,无法进行其他更新操作,保证数据consistency。
- 效率低,锁定时间长,发生锁争用的概率大。
- 乐观锁:
- 乐观锁不会对数据加锁,只在更新时检查版本号,如果版本号不同,则说明数据已经被更新。
- 通常使用数据版本号version来实现。
- 在更新时,通过where 条件检查version号是否与最新的相同。如果不同就代表已经更新过了。
- 效率高,不会发生死锁。
InnoDB存储引擎从MySQL 5.5开始支持行锁,可以实现行级别的悲观锁。
总体来说,乐观锁适用于写比较少的场景,可以提高吞吐量。悲观锁适用于写比较多的场景,可以保证数据的完整性。
7.4、mybatis-plus的乐观锁插件
为了测试,重新创建一张表
CREATE TABLE goods(id INT PRIMARY KEY AUTO_INCREMENT,NAME VARCHAR(100),price DOUBLE ,num INT,VERSION INT DEFAULT 0
);
INSERT INTO goods(id,NAME,price,num,VERSION) VALUES(NULL,"华为手机",100,3,0);
需要在数据库表中添加一列version,用于乐观锁的版本号确认,需要在实体类中使用@Version注解。
@Data
@TableName("goods")
public class Goods {@TableIdprivate Long id;private String name;private Integer price;private Integer num;@Versionprivate Integer version;
}
需要在拦截器中添加乐观锁的插件
@Configuration
public class MyBatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){// 创建拦截器对象MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 设置分页的拦截器,并设置数据库类型为mysqlinterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));// 添加乐观锁的拦截器interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());// 返回拦截器对象return interceptor;}
}
@SpringBootTest
public class GoodsTest {@Autowiredprivate GoodsMapper goodsMapper;@Testpublic void test(){// 架设一个操作:需要对商品的价格先增加50%,然后在优惠20%// 架设另一个操作:需要对商品的价格在前一个修改后的基础上在优惠10%// 但是可能会出现两个操作同时进行的情况,就需要通过乐观锁进行控制// 架设两个操作同时进行,操作之前需要先查询到当前商品的数据信息Goods goods = this.goodsMapper.selectById(1);Goods goods2 = this.goodsMapper.selectById(1);// 开始执行 价格先增加50%goods.setPrice(goods.getPrice()*1.5);// 然后在优惠20%goods.setPrice(goods.getPrice()*0.8);// 将数据跟新到数据库int i = this.goodsMapper.updateById(goods);System.out.println(i);// goods2进行更新goods2.setPrice(goods2.getPrice()*0.9);// goods2更新不会成功this.goodsMapper.updateById(goods2);// 若要更新成功,就必须在更新失败后,重新获取数据,对最新的数据进行更新}
}
通过执行的过程中产生的sql语句,会发现,version作为where的条件存在
UPDATE goods SET name=?, price=?, num=?, version=? WHERE id=? AND version=?
8、代码生成器
MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。
安装:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx
搜索并安装。安装成功会提示重启idea,我们重庆idea之后,就可以使用MybatisX插件了
使用前提:需要配置数据库源
选择 +
按钮,然后选择Data Source
菜单,找到 MySql
选项
在弹出窗口中,填写对应的信息,最后点击OK
,即可完成数据源的配置
打开数据源,找到对应的表,右击选择MyBatisX-Generator
需要根据提示,填写对应的内容
继续完成对应的配置
最后选择Finish
,即可生成最基础的模版,然后就可以开始愉快的编程了。
在mybatis-plus默认的功能不够的时候,就可以借助MyBaitsX快速给Mapper接口添加对应的方法,同时也会在mapper文件中生成对应的sql 语句。
相关文章:

MyBatis-Plus的使用
MyBatis-Plus 1、mybatis-plus介绍 官网:https://baomidou.com/ MyBatis-Plus (简称 MP)是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。MyBatis-Plus提供了通用的mapper…...

板卡设计+硬件每日学习十个知识点(44)23.8.24 (检测单元设计,接口部分设计,板卡电源输入设计,电源检测电路)
文章目录 1.检测单元介绍(使用GD32单片机)2.GD32的最小系统板3.GD32的温度监测4.GD32的电压监测和电流监测5.GD32的布线6.接口部分设计7.板卡电源输入设计8.电源检测电路 1.检测单元介绍(使用GD32单片机) 答: 首先要为…...

jmeter HTTP信息头管理器
首先,打开JMeter并创建一个新的测试计划。右键单击测试计划,选择"添加" > “线程组”,然后在线程组上右键单击,选择"添加" > “Sampler” > “HTTP请求”。 在HTTP请求中填写服务器的URL和其他必要…...

各种中间件的默认端口
面试时会忘记个别中间件端口 docker:2375 nacos:8848 redis:6379 rabbitMq: 5672(后台配置的端口)15672(web管理界面)账号:guest15674(web STOMP插件):通过WebSocket…...

leetcode303. 区域和检索 - 数组不可变(java)
前缀和数组的应用 区域和检索 - 数组不可变题目描述前缀和数组代码演示 区域和检索 - 数组不可变 难度 - 简单 原题链接 - 区域和检索 - 数组不可变 题目描述 给定一个整数数组 nums,处理以下类型的多个查询: 计算索引 left 和 right (包含 left 和 righ…...

PHP 安装Composer,vue前端依赖包
电脑安装Composer 官网下载:https://getcomposer.org/Composer-Setup.exe 后端安装: 检查是否安装依赖: 安装Composer install 或 Composer i 前端安装: yarn install 安装依赖...

OpenCV项目开发实战--基于Python/C++实现鼠标注释图像和轨迹栏来控制图像大小
鼠标指针是图形用户界面 (GUI) 中的关键组件。没有它,您就无法真正考虑与 GUI 进行交互。那么,让我们深入了解 OpenCV 中鼠标和轨迹栏的内置函数。我们将演示如何使用鼠标来注释图像,以及如何使用轨迹栏来控制图像的大小 我们将使用下图来演示 OpenCV 中鼠标指针和轨迹栏功能…...

❤ Vue使用Eslint检测报错问题和解决
❤ Vue使用Eslint检测报错问题和解决 1、 关闭Eslint检测 关闭ESLint语法检测即可:具体步骤如下: 第一步我们打开setting设置: 打开左上角的file,然后点击setting 接下来进入setting以后我们来看下面操作: eol-l…...

解决运行在微信小程序中报[ app.json 文件内容错误] app.json: app.json 未找到(env: Windows,mp,1.05.2204
找到project.config.json文件夹 添加 "miniprogramRoot": "unpackage/dist/dev/mp-weixin/", 即可...

python 基础 -- 安装Python模块
作为一个流行的开源开发项目,Python拥有一个由贡献者和用户组成的活跃支持社区,他们还根据开源许可条款向其他Python开发人员提供他们的软件。 这允许Python用户有效地共享和协作,从其他人已经创建的常见(有时甚至是罕见的!)问题的解决方案中…...

C语言实现状态机
关于状态机,基础的知识点可以自行理解,讲解的很多,这里主要是想写一个有限状态机FSM通用的写法,目的在于更好理解,移植,节省代码阅读与调试时间,体现出编程之美。 传统的实现方案 if...else : …...

交叉编译工具链arm-linux-gnueabihf的安装-ubuntu 20.04
前面下载安装步骤参考该博主的文章 http://t.csdn.cn/ZbjFX 另:本人对所遇到的环境变量的配置问题作补充 1.修改环境变量 建议直接在.bashrc文件作修改 ,修改方式相同 ( vi :视自己的编辑器而定) sudo vi ~/.bashrc 2.修改环境变量后,可…...

Java的类加载器
类加载 1、ClassLoader 用来加载 Class 文件 2、 系统内置的ClassLoader 通过双亲委托加载指定目录下的class和资源 3、 可以自定义ClassLoader 一般覆盖findClass() 4、ContextClassLoader 与线程相关,可以获取和设置,可以绕过双亲委托的机制。 三个类…...

Stable Diffusion web UI 部署详细教程
前言 本文使用 AutoDL 平台进行 Stable Diffusion web UI 云端部署 AutoDL 官网:AutoDL算力云 | 弹性、好用、省钱。租GPU就上AutoDL Stable Diffusion web UI 官网:AUTOMATIC1111/stable-diffusion-webui: Stable Diffusion web UI (github.com) 步…...

《深度学习计算机视觉 》书籍分享(包邮送书三本)
深度学习计算机视觉介绍 随着计算机技术的发展和进步,计算机视觉领域得到了广泛的关注和研究。而深度学习作为一种强大的机器学习方法,已经成为计算机视觉领域的重要工具之一。本文将介绍深度学习在计算机视觉中的应用和取得的成果。 深度学习是一种模…...

【使用 k 折叠交叉验证的卷积神经网络(CNN)】基于卷积神经网络的无特征EMG模式识别研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

微服务 Nacos配置热部署
在nacos中添加配置文件 在配置列表中添加配置, 注意:项目的核心配置,需要热更新的配置才有放到nacos管理的必要。基本不会变更的一些配置还是保存在微服务本地比较好。 从微服务拉取配置 微服务要拉取nacos中管理的配置,并且与…...

国产调度器之光——Fsched到底有多能打?
这是一篇推荐我们速石自研调度器——Fsched的文章。 看起来在专门写调度器,但又不完全在写。 往下看,你就懂了。 本篇一共五个章节: 一、介绍一下主角——速石自研调度器Fsched 二、只要有个调度器,就够了吗? 三…...

LeetCode:53. 最大子数组和 - Python
53. 最大子数组和 问题描述: 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 子数组 是数组中的一个连续部分。 示例 1: 输入:nums [-…...

网站建设 之 react usestate
react着重在于“不可变动” 如果变动了怎么办呢?那就整个新的 局部变量/函数/jsx-》state/props-〉ref,依次越来越难变 每次state/props,局部变量/函数/jsx都是新的 既然函数是新的,那么就会有一个问题,回调函数用…...

第一讲使用IDEA创建Java工程——HelloWorld
一、前言导读 为了能够让初学者更快上手Java,不会像其他书籍或者视频一样,介绍一大堆历史背景,默认大家已经知道Java这么编程语言了。本专栏只会讲解干货,直接从HelloWord入手,慢慢由浅入深,讲个各个知识点,这些知识点也是目前工作中项目使用的,而不是讲一些老的知识点…...

BootstrapBlazor组件使用:数据注解
文章目录 前言BB数据注解数据注解源码数据注解简介注解简单实例[BB 编辑弹窗](https://www.blazor.zone/edit-dialog)[ValidateForm 表单组件](https://www.blazor.zone/validate-form)使用简介 前言 BootstrapBlazor(一下简称BB)是个特别好用的组件,基本上满足了大…...

MySQL 触发器
文章目录 1.简介2.行级与语句级触发器3.触发时机4.触发器优缺点5.创建触发器语法示例 6.查看触发器7.删除触发器参考文献 1.简介 触发器(Trigger)是与表关联的命名数据库对象,当表发生特定事件时激活。 触发器的一些用途是对要插入表中的值执…...

DPDK主从进程模式 rte_mempool_put失败
版本:19.11.6 情景:主进程应用rte_mempool_create创建mempool,rte_mempool_get获取数据;从进程应用rte_mempool_put归还数据 问题:从进程rte_mempool_put无法归还数据 原因:DPDK通过rte_mempool_ops_tab…...

ZooKeeper 的工作原理
ZooKeeper 的工作原理可以概括为以下几个方面: 1. 数据模型 ZooKeeper 使用树形目录节点(znode)来建模关键的数据,每个 znode 可以存储数据内容,也可以作为目录包括子节点。客户端可以在节点上设置监听器。 2. 一致性算法 ZooKeeper 使用 ZAB(ZooKeeper Atomic Broadcast)协议…...

【业务功能篇73】分布式ID解决方案
业界实现方案 1. 基于UUID2. 基于DB数据库多种模式(自增主键、segment)3. 基于Redis4. 基于ZK、ETCD5. 基于SnowFlake6. 美团Leaf(DB-Segment、zkSnowFlake)7. 百度uid-generator() 1.基于UUID生成唯一ID UUID:UUID长度128bit,32个16进制字符,占用存储空…...

Qt安卓开发经验技巧总结V202308
01:01-05 pro中引入安卓拓展模块 QT androidextras 。pro中指定安卓打包目录 ANDROID_PACKAGE_SOURCE_DIR $$PWD/android 指定引入安卓特定目录比如程序图标、变量、颜色、java代码文件、jar库文件等。 AndroidManifest.xml 每个程序唯一的一个全局配置文件&…...

【vue2】前端实现下载后端返回的application/octet-stream文件流
1、下载csv/txt时 此时无须修改接口的响应格式 let filenameRegex /filename[^;\n]*((["]).*?\2|[^;\n]*)/; let matches filenameRegex.exec(data.headers[content-disposition]); let blob new Blob([\uFEFF data.data], {//目前只有csv格式type: text/csv;charse…...

【Java】SM2Utils(国密 SM2 工具类)
基于 bouncycastle 实现 国密 SM2 <!-- 引入 bouncycastle --> <dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.70</version> </dependency>import lombok.Sneak…...

『C语言入门』初识C语言
文章目录 前言C语言简介一、Hello World!1.1 编写代码1.2 代码解释1.3 编译和运行1.4 结果 二、数据类型2.1 基本数据类型2.2 复合数据类型2.3 指针类型2.4 枚举类型 三、C语言基础3.1 变量和常量3.2 运算符3.3 控制流语句3.4 注释单行注释多行注释注释的作用 四、 …...