一、什么是MybatisPlus
为什么要学MybatisPlus?
MybatisPlus可以节省大量时间,所有的CRUD代码都可以自动化完成
MyBatis-Plus是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
特性:
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
二、快速入门
2.1、创建数据库mybatis_plus
2.2、创建user表
DROP TABLE IF EXISTS 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)
);
2.3、插入数据
DELETE FROM user;
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, '[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');
2.4、初始化项目
快速初始化一个空的spring boot 项目
2.5、添加依赖
引用spring boot starter 父工程
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.5</version>
<relativePath/>
</parent>
引入spring-boot-starter、spring-boot-starter-test、mybatis-plus-boot-starter、h2依赖:
<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>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
注意:尽量不要同时导入mybatis和mybatis_plus,版本差异
2.6、配置(连接数据库)
在application.yml配置文件中添加MySQL数据库的相关配置:
# DataSource Config
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql:///mybatis_plus?userUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
在spring boot启动类中添加@MapperScan注解,扫描Mapper文件夹:
@SpringBootApplication
@MapperScan("com.wen.mybatis_plus.mapper") //扫描mapper
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
2.7、编码
编写实体类User.java(此处使用Lombok简化代码)
import lombok.Data;
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
编写Mapper包下的UserMapper接口
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wen.mybatis_plus.pojo.User;
import org.apache.ibatis.annotations.Mapper;
//再对应的mapper上面实现基本的接口 BaseMapper
@Mapper
public interface UserMapper extends BaseMapper<User> {
//所有的CRUD都已经完成
//不需要像以前一样配置一大堆文件:pojo-dao(连接mybatis,配置mapper.xml文件)==>service-controller
}
注意:
要在主启动类上去扫描mapper包下的所有接口:@MapperScan("com.wen.mybatis_plus.mapper")
2.8、开始使用
添加测试类,进行功能测试:
@SpringBootTest
class MybatisPlusApplicationTests {
//继承了BaseMapper所有的方法,可以编写自己的扩展方法
@Autowired
private UserMapper userMapper;
@Test
public void testSelect(){
System.out.println("--------selectAll method test-------");
//查询全部用户,参数是一个Wrapper,条件构造器,先不使用为null
List<User> userList = userMapper.selectList(null);
userList.forEach(System.out::println);
}
提示:
UserMapper中的selectList()方法的参数为MP内置的条件封装器Wrapper,所以不填写就是无任何条件。
控制台输出:
User(id=1, name=Jone, age=18, email=test1@baomidou.com) User(id=2, name=Jack, age=20, email=test2@baomidou.com) User(id=3, name=Tom, age=28, email=test3@baomidou.com) User(id=4, name=Sandy, age=21, email=test4@baomidou.com) User(id=5, name=Billie, age=24, email=test5@baomidou.com)
2.9、小结
以上几个步骤就已经实现User表的CRUD功能,甚至连XML文件都不用编写,以上步骤可以看出集成MyBatis-Plus非常的简单,只需要引入starter工程,并配置mapper扫描路径即可。方法都是MyBatis-Plus写好的,直接引用即可。
三、配置日志
所有的SQL都是不可见的,所以在后台是希望看到SQL是怎么执行的,就必须要配置日志。
在.yml配置文件中配置日志:
#配置日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
四、CRUD
4.1、插入测试
//测试插入
@Test
public void testInsert(){
User user = new User();
user.setName("小文");
user.setAge(21);
user.setEmail("[email protected]");
int insert = userMapper.insert(user);//如果没有设置id,那么会自动生成id
System.out.println(insert);//受影响行数
System.out.println(user);//id会自动回填
}
数据库插入ID默认值为全局唯一ID
4.2、自定义ID生成器
在复杂分布式系统中,往往需要大量的数据和消息进行唯一标识。比如支付宝每一个账号在数据库分表后都需要有一个唯一ID做标识。此时一个能够生成全局唯一ID的系统是非常必要的。
所以,生成的ID需要具备一下特点:
- 全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求。
- 趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能。
- 单调递增:保证下一个ID一定大于上一个ID,例如事务版本号、IM增量消息、排序等特殊需求。
- 信息安全:如果ID是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号就更危险了,竞对可以直接知道我们一天的单量。所以在一些应用场景下,会需要ID无规则、不规则。
上述123对应三类不同的场景,3和4需求还是互斥的,无法使用同一个方案满足。
在这里只讲两种自动生成ID的方案UUID和SnowFlake
可以查看有哪些方法,查看源码:
在用户ID上添加@TableId注解,里面的值便是使用主键自动生成的方法
自 3.3.0 开始,默认使用雪花算法+UUID(不含中划线)
@Data
public class User {
//对应数据库中的主键(UUID、自增id、雪花算法、redis、zookeeper)
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private String name;
private Integer age;
private String email;
}
点击 IdType查看源码看有哪些自动生成方法
/**
* 生成ID类型枚举类
*
* @author hubin
* @since 2015-11-10
*/
@Getter
public enum IdType {
/**
* 数据库ID自增
* <p>该类型请确保数据库设置了 ID自增 否则无效</p>
*/
AUTO(0),
/**
* 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
*/
NONE(1),
/**
* 用户输入ID
* <p>该类型可以通过自己注册自动填充插件进行填充</p>
*/
INPUT(2),
/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
/**
* 分配ID (主键类型为number或string),
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
*
* @since 3.3.0
*/
ASSIGN_ID(3),
/**
* 分配UUID (主键类型为 string)
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
*/
ASSIGN_UUID(4);
private final int key;
IdType(int key) {
this.key = key;
}
}
4.2.1、UUID
UUID(Universally Unique Identifier)的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的36个字符,示例:
550e8400-e29b-41d4-a716-446655440000
,到目前为止业界一共有5种方式生成UUID,详情见IETF发布的UUID规范 A Universally Unique IDentifier (UUID) URN Namespace。
优点:
- 性能非常高:本地生成,没有网络消耗。
缺点:
- 没有排序,无法保证趋势递增。
- UUID往往使用字符串存储,查询的效率比较低。
- 不易于存储:UUID太长,16字节128位,通常以36长度的字符串表示,很多场景不适用。
- 信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露,这个漏洞曾被用于寻找梅丽莎病毒的制作者位置。
- ID作为主键时在特定的环境会存在一些问题,比如做DB主键的场景下,UUID就非常不适用:- MySQL官方有明确的建议主键要尽量越短越好[4],36个字符长度的UUID不符合要求。- 对MySQL索引不利:如果作为数据库主键,在InnoDB引擎下,UUID的无序性可能会引起数据位置频繁变动,严重影响性能。
4.2.2、SnowFlake(雪花算法)
这种方案大致来说是一种以划分命名空间(UUID也算,由于比较常见,所以单独分析)来生成ID的一种算法,这种方案把64-bit分别划分成多段,分开来标示机器、时间等,比如在snowflake中的64-bit分别表示如下图(图片来自网络)所示:
41-bit的时间可以表示(1L<<41)/(1000L360024*365)=69年的时间,10-bit机器可以分别表示1024台机器。如果我们对IDC划分有需求,还可以将10-bit分5-bit给IDC,分5-bit给工作机器。这样就可以表示32个IDC,每个IDC下可以有32台机器,可以根据自身需求定义。12个自增序列号可以表示2^12个ID,理论上snowflake方案的QPS约为409.6w/s,这种分配方式可以保证在任何一个IDC的任何一台机器在任意毫秒内生成的ID都是不同的。
核心思想:
使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个ID),最后还有一个符号位,永远是0,。
优点:
- 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。
- 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。
- 可以根据自身业务特性分配bit位,非常灵活。
缺点:
- 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。
4.3、更新操作
所有的SQL都是自动配置的
//测试更新
@Test
public void testUpdate(){
User user = new User();
//可以通过条件自动拼接动态SQL
user.setId(5L);
user.setName("id:5,修改过后");
//updateById 参数是一个对象!
int i = userMapper.updateById(user);
System.out.println(i);
}
注意:updateById 参数是一个对象!而不是ID
五、自动填充
5.1、什么是自动填充
在常用业务中有些属性需要配置一些默认值,MyBatis-Plus提供了实现此功能的插件,也就是自动填充功能。比如创建时间、修改时间这些操作一般都是自动化完成的,是不用去手动更新的。
5.2、自动填充方式
方式一:数据库级别(不建议使用)
1、在表中新增字段create_time,update_time
注意:在create_time里除了没有勾选根据当前时间戳更新外其他步骤都一样!
2、在此测试插入方法,需要将实体类同步!!
private Data createTime;
private Data updateTime;
3、在此更新查看
//测试更新
@Test
public void testUpdate(){
User user = new User();
//可以通过条件自动拼接动态SQL
user.setId(5l);
user.setName("id:5,修改过后");
user.setAge(25);
//updateById 参数是一个对象!
int i = userMapper.updateById(user);
System.out.println(i);
}
4、查看时间戳是否更新
方式二:代码级别
1、删除数据库的默认值,更新操作
2、注解填充字段
@TableField(.. fill = FieldFill.INSERT)
生成器策略部分也可以配置!
//注解填充字段 @TableField(.. fill = FieldFill.INSERT) 生成器策略部分也可以配置!
@TableField(fill = FieldFill.INSERT)
private Data createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Data updateTime;
3、自定义实现类 MyMetaObjectHandler 处理这个注解
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
//插入时的填充策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("start intsert fill ....");
//strictInsertFill(MetaObject metaObject, String fieldName, Class<T> fieldType, E fieldVal)
this.strictInsertFill(metaObject,"createTime", LocalDateTime.class,LocalDateTime.now());// 起始版本 3.3.0(推荐使用)
}
//更新时的填充策略
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐)
}
}
六、乐观锁和悲观锁
6.1、什么是乐观锁
乐观锁:十分乐观,认为不会出现问题,无论干什么都不会去上锁,如果出现问题,就再次更新测试值
乐观锁是对于数据冲突保持一种乐观态度,操作数据时不会对操作的数据进行加锁(这使得多个任务可以并行的对数据进行操作),只有到数据提交的时候才通过一种机制来验证数据是否存在冲突(一般实现方式是通过加版本号然后进行版本号的对比方式实现);
特点:乐观锁是一种并发类型的锁,其本身不对数据进行加锁而是通过业务实现锁的功能,不对数据进行加锁就意味着允许多个请求同时访问数据,同时也省掉了对数据加锁和解锁的过程,这种方式因为节省了悲观锁加锁的操作,所以可以一定程度的的提高操作的性能,不过在并发非常高的情况下,会导致大量的请求冲突,冲突导致大部分操作无功而返而浪费资源,所以在高并发的场景下,乐观锁的性能却反而不如悲观锁。
6.2、什么是悲观锁
悲观锁:十分悲观,认为总是出现问题,无论干什么都会上锁,再去操作
悲观锁是基于一种悲观的态度类来防止一切数据冲突,它是以一种预防的姿态在修改数据之前把数据锁住,然后再对数据进行读写,在它释放锁之前任何人都不能对其数据进行操作,直到前面一个人把锁释放后下一个人数据加锁才可对数据进行加锁,然后才可以对数据进行操作,一般数据库本身锁的机制都是基于悲观锁的机制实现的;
特点:可以完全保证数据的独占性和正确性,因为每次请求都会先对数据进行加锁, 然后进行数据操作,最后再解锁,而加锁释放锁的过程会造成消耗,所以性能不高;
6.3、配置乐观锁
本文主要讲解乐观锁机制
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时,set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
当要更新一条记录时,是希望这条记录没有被更新的
-- 乐观锁:1、先查询,获取版本号 version=1
-- A线程
update user name = "tian" ,version = version +1
where id = 2 and version = 1
-- 如果B线程抢先完成,这个时候version=2,就会导致A线程修改失败
-- B线程
update user name = "tian" ,version = version +1
where id = 2 and version = 1
测试MP的乐观锁插件
6.3.1、数据库中添加version字段
添加完成后查看是否更改完成
6.3.2、同步实体类
记得在实体类上加上@Version注解
@Version //乐观锁version注解
private Integer version;
6.3.3、配置插件
spring xml 方式:
<bean class="com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor" id="optimisticLockerInnerInterceptor"/>
<bean id="mybatisPlusInterceptor" class="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor">
<property name="interceptors">
<list>
<ref bean="optimisticLockerInnerInterceptor"/>
</list>
</property>
</bean>
这里讲解的是springboot的注解方式
spring boot 的注解方式
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
首先创建配置类文件config,在该文件下创建配置类MyBatisPlusConfig,该类需要添加三个注解:
@Configuration //配置类
@MapperScan("com.wen.mybatis_plus.mapper") //扫描mapper
@EnableTransactionManagement //自动管理事务,默认是开启的
@MapperScan()是将原先MybatisPlusApplication中的扫描换到这里的,所以MybatisPlusApplication中就不需要@MapperScan(),在该配置类里添加@MapperScan()即可
创建完MyBatisPlusConfig类并添加完注解后,就可以将上面的组件的注解方式填入进来
@Configuration //配置类
@MapperScan("com.wen.mybatis_plus.mapper") //扫描mapper
@EnableTransactionManagement
public class MyBatisPlusConfig {
//注册乐观锁插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
此时乐观锁就已经配置完成了!
6.3.4、测试乐观锁
在测试类中分别对成功与失败进行测试
测试成功:
//测试成功的乐观锁
@Test
void testOptimisticLocker_success() {
//1.查询用户信息
User user = userMapper.selectById(1l);
//2.修改用户信息
user.setName("tian");
user.setAge(21);
//3.执行更新操作
userMapper.updateById(user);
}
结果如下:
测试失败:
模拟多线程的方式执行插队操作
@Test
void testOptimisticLocker_failure() {
//模拟多线程实现插队效果
//线程1
User user = userMapper.selectById(1l);
user.setName("tian");
user.setAge(21);
//线程2
User user2 = userMapper.selectById(1l);
user2.setName("xiaotian");
user2.setAge(19);
userMapper.updateById(user2); //在这里插队
userMapper.updateById(user); //如果没有乐观锁就会覆盖插队线程的值
}
查看控制台输出:
从数据库看结果:
七、增删改查
7.1、查询操作
1.通过Id查询用户
//测试查询
@Test
public void testSelectById(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
结果:
2.批量查询
//批量查询
@Test
public void selectBatchIds(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out::println);
}
批量查询通过selectBatchIds方法,方法内放入的是集合,可以通过源码看
selectBatchIds方法源码:
/**
* 查询(根据ID 批量查询)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
可以看到参数是Collection也就是集合,这里使用的是Arrays集合
结果:
3.条件查询
通过自定义条件查询
//条件查询
@Test
public void selectByMap(){
HashMap<String,Object> map = new HashMap<>();
//自定义查询
map.put("name","小文");
map.put("age",20);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
可以看出,map类的参数(字段名,参数)会被MySQLPlus自动组合成查询条件
7.2、分页查询
- 原始的limit进行分页
- pageHelper第三方插件
- MyBatisPlus内置分页插件
支持数据库:
- mysql,oracle,db2,h2,hsql,sqlite,postgresql,sqlserver,Phoenix,Gauss ,clickhouse,Sybase,OceanBase,Firebird,cubrid,goldilocks,csiidb
- 达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库
属性介绍
属性名类型默认值描述overflowbooleanfalse溢出总页数后是否进行处理(默认不处理)maxLimitLong单页分页条数限制(默认无限制)dbTypeDbType数据库类型(根据类型获取应使用的分页方言)dialectIDialect方言实现类
建议单一数据库类型的均设置 dbType
如何使用MyBatisPlus内页插件?
1、配置拦截器组件即可
/**
* 注册插件
*/
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor();
// 设置请求的页面大于最大页后操作,true调回到首页,false继续请求。默认false
pageInterceptor.setOverflow(false);
// 单页分页条数限制,默认无限制
pageInterceptor.setMaxLimit(500L);
// 设置数据库类型
pageInterceptor.setDbType(DbType.MYSQL);
interceptor.addInnerInterceptor(pageInterceptor);
return interceptor;
}
2、分页组件测试
//测试MybatisPlus分页插件
@Test
public void testMybatisPlus_Page(){
// 两个参数:current的值默认是1,从1开始,不是0。size是每一页的条数。
Page<User> page = new Page<>(1, 4);
userMapper.selectPage(page,null);
page.getRecords().forEach(System.out::println);
}
结果分析:
查询第二页试试看
除此之外,Page 的方法还有很多比如:
//page的其他方法
System.out.println("当前页:" + page.getCurrent());
System.out.println("总页数:" + page.getPages());
System.out.println("记录数:" + page.getTotal());
System.out.println("是否有上一页:" + page.hasPrevious());
System.out.println("是否有下一页:" + page.hasNext());
结果:
7.3、删除操作
跟查询操作相似,就不详细讲解,直接上代码了
//测试删除
@Test
public void testDeleteById(){
userMapper.deleteById(4L);
}
//批量删除
@Test
public void testDeleteBatchId(){
userMapper.deleteBatchIds(Arrays.asList(1L,2L));
}
//通过map删除
@Test
public void testdeleteByMap(){
Map<String, Object> map = new HashMap<>();
map.put("name","xiaotian");
userMapper.deleteByMap(map);
}
7.4、逻辑删除
物理删除:从数据库中直接删除
逻辑删除:在数据库中没有被删除,而是通过一个变量来让它失效。 deleted=0 ==》deleted=1
管理员可以查看被删除的记录,防止数据丢失,相当于回收站。
测试:
1、在数据表中增加一个deleted字段
2、同步实体类,在实体类上加上@TableLogic 注解
@TableLogic //逻辑删除
private Integer deleted;
3、配置application.yml文件
#配置日志
mybatis-plus:
global-config:
db-config:
logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
4、测试
在这里直接使用之前的delete测试
//测试删除
@Test
public void testDeleteById(){
userMapper.deleteById(4L);
}
查看日志输出可以看到,delete的语句以经发生了更改
实质上就是update(修改)语句,将deleted字段从1修改为0
5、对Id为4的用户进行查询
//测试查询
@Test
public void testSelectById(){
User user = userMapper.selectById(4L);
System.out.println(user);
}
查看日志输出可以看到,seletc的语句以经发生了更改
增加了deleted的判断语句,判断deleted是否为1,为1则能搜索,0则不能
6、小结
只对自动注入的SQL有效:
- 插入: 不作限制
- 查找: 追加 where 条件过滤掉已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段
- 更新: 追加 where 条件防止更新到已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段
- 删除: 转变为 更新
比如:
删除语句转化为:update user set deleted=1 where id = 1 and deleted=0
查找语句转化为:select id,name,deleted from user where deleted=0
注意事项:
- 逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。
- 如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。
八、执行SQL分析打印
可输出 SQL 语句以及其执⾏时间,建议开发测试时启⽤该功能,能快速揪出慢查询
注意:PerformanceInterceptor在3.2.0被移除了,如果想进⾏性能分析,⽤第三⽅的,官⽅这样写的“该插件 3.2.0 以上版本移除 推荐使⽤第三⽅扩展 执⾏SQL分析打印 功能”。也就是p6spy。
使用步骤:
8.1、p6spy依赖引入
Maven:
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>最新版本</version> <!--这里用的是>3.9.1版本-->
</dependency>
8.2、application.yml配置
spring:
datasource:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:h2:mem:test
...
注意: driver-class-name 为 p6spy 提供的驱动类 url 前缀为 jdbc:p6spy 跟着冒号为对应数据库连接地址
实际配置为:
spring:
datasource:
username: root
password: 123456
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql:///mybatis_plus?userUnicode=true&characterEncoding=utf-8
8.3、spy.properties配置
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
8.4、测试
这里使用之前的分页查询来测试一下
//测试MybatisPlus分页插件
@Test
public void testMybatisPlus_Page(){
// 两个参数:current的值默认是1,从1开始,不是0。size是每一页的条数。
Page<User> page = new Page<>(2, 4);
userMapper.selectPage(page,null);
page.getRecords().forEach(System.out::println);
//page的其他方法
System.out.println("当前页:" + page.getCurrent());
System.out.println("总页数:" + page.getPages());
System.out.println("记录数:" + page.getTotal());
System.out.println("是否有上一页:" + page.hasPrevious());
System.out.println("是否有下一页:" + page.hasNext());
}
查看日志输出
因为在配置文件中设置了慢SQL的检查,为2s,所以这里的查询可以通过
但是只要超过了时长就会抛出异常
九、条件构造器
Wrapper,可以通过其构造复杂的SQL
注意:
1、耦合性高
2、传输wrapper相当于conroller用map接收值,后期维护极为困难
所以这里只是采取少量代码进行演示
9.1、代码演示
1、查询name、邮箱不为空且年龄大于等于20的用户
@Test
void WrapperTest(){
//查询name、邮箱不为空且年龄大于等于20的用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.isNotNull("name")
.isNotNull("email")
.ge("age",12);
userMapper.selectList(wrapper).forEach(System.out::println);
}
2、查询姓名为小文的用户
@Test
void WrapperTest2(){
//查询姓名为小文的用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name","小文"); //equals
User user = userMapper.selectOne(wrapper);
System.out.println(user);
}
注意:
查询一个数据出现多个结果就使用List或map
3、查询年龄在19-23之间的用户
@Test
void WrapperTest3(){
//查询年龄在19-23之间的用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age", 19, 23);
Long count = userMapper.selectCount(wrapper);//查询结果数
System.out.println(count);
}
4、查询年龄在19-23之间的用户
@Test
void WrapperTest3(){
//查询年龄在19-23之间的用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age", 19, 23);
Long count = userMapper.selectCount(wrapper);//查询结果数
System.out.println(count);
}
这里的方法取值范围是左开右闭!
5、模糊查询
@Test
void WrapperTest4(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.notLike("name","a") //查询姓名中不包含a的用户
.likeRight("email","t"); //左和右是代表%的位置 两边都要匹配则为%e%,这里是email以t开头的 t%
List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
maps.forEach(System.out::println);
}
6、联表查询
//联表查询
@Test
void WrapperTest5(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.inSql("id","select id from user where id < 4");
List<Object> objects = userMapper.selectObjs(wrapper);
objects.forEach(System.out::println);
}
7、通过ID进行排序
@Test
void WrapperTest6(){
//通过ID进行排序
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.orderByAsc("id"); //通过id升序
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
十、代码自动生成器
MybatisPlus看官方文档即可,这里讲一个Idea插件Easy Code代码生成器
10.1、EasyCode
EasyCode是基于IntelliJ IDEA Ultimate版开发的一个代码生成插件,主要通过自定义模板(基于velocity)来生成各种你想要的代码。通常用于生成Entity、Dao、Service、Controller。如果你动手能力强还可以用于生成HTML、JS、PHP等代码。理论上来说只要是与数据有关的代码都是可以生成的。
支持的数据库类型:
- MySQL
- SQL Server
- Oracle
- PostgreSQL
- Sqlite
- Sybase
- Derby
- DB2
- HSQLDB
- H2
当然支持的数据库类型也会随着Database Tool插件的更新同步更新。
10.2、功能
- 支持多表同时操作
- 支持同时生成多个模板
- 支持自定义模板
- 支持自定义类型映射(支持正则)
- 支持自定义附加列
- 支持列附加属性
- 所有配置项目支持分组模式,在不同项目(或选择不同数据库时),只需要切换对应的分组,所有配置统一变化
10.3、操作
1、安装EasyCode
2、建立数据库
这里就不再多说了,就用之前的数据库即可
3、在IDEA配置连接数据库
这里也不多说了,就是idea连接自己的数据库
4、使用EasyCode
在对应的字段上右键,就可以看到多出一个EasyCode,点击然后选择生成
5、开始生成代码
勾选需要生成的代码,点击OK即可
6、效果图
这些就是自动生成的代码了,代码你成熟了,应该自己生成了<狗头>
版权归原作者 Code_Xiaotian 所有, 如有侵权,请联系我们删除。