MyBatis Plus详细教程
时间:2024-04-07 08:45:31 来源:网络cs 作者:焦糖 栏目:卖家故事 阅读:
目录
一、什么是MybatisPlus
二、快速入门
2.1、创建数据库mybatis_plus
2.2、创建user表
2.3、插入数据
2.4、初始化项目
2.5、添加依赖
2.6、配置(连接数据库)
2.7、编码
2.8、开始使用
2.9、小结
三、配置日志
四、CRUD
4.1、插入测试
4.2、自定义ID生成器
4.2.1、UUID
4.2.2、SnowFlake(雪花算法)
4.3、更新操作
五、自动填充
5.1、什么是自动填充
5.2、自动填充方式
六、乐观锁和悲观锁
6.1、什么是乐观锁
6.2、什么是悲观锁
6.3、配置乐观锁
6.3.1、数据库中添加version字段
6.3.2、同步实体类
6.3.3、配置插件
6.3.4、测试乐观锁
七、增删改查
7.1、查询操作
7.2、分页查询
7.3、删除操作
7.4、逻辑删除
八、执行SQL分析打印
8.1、p6spy依赖引入
8.2、application.yml配置
8.3、spy.properties配置
8.4、测试
九、条件构造器
9.1、代码演示
十、代码自动生成器
10.1、EasyCode
10.2、功能
10.3、操作
一、什么是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, '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、初始化项目
快速初始化一个空的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 Configspring: 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") //扫描mapperpublic class MybatisPlusApplication { public static void main(String[] args) { SpringApplication.run(MybatisPlusApplication.class, args); }}
2.7、编码
编写实体类User.java(此处使用Lombok简化代码)
import lombok.Data;@Datapublic 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@Mapperpublic interface UserMapper extends BaseMapper<User> { //所有的CRUD都已经完成 //不需要像以前一样配置一大堆文件:pojo-dao(连接mybatis,配置mapper.xml文件)==>service-controller}
注意:
要在主启动类上去扫描mapper包下的所有接口:@MapperScan("com.wen.mybatis_plus.mapper")
2.8、开始使用
添加测试类,进行功能测试:
@SpringBootTestclass 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、插入测试
//测试插入@Testpublic void testInsert(){ User user = new User(); user.setName("小文"); user.setAge(21); user.setEmail("2312103645@qq.com"); 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(不含中划线)
@Datapublic 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 */@Getterpublic 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都是自动配置的
//测试更新@Testpublic 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、在此更新查看
//测试更新@Testpublic 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@Componentpublic 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 +1where id = 2 and version = 1-- 如果B线程抢先完成,这个时候version=2,就会导致A线程修改失败-- B线程update user name = "tian" ,version = version +1where 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 的注解方式
@Beanpublic 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@EnableTransactionManagementpublic class MyBatisPlusConfig { //注册乐观锁插件 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; }}
此时乐观锁就已经配置完成了!
6.3.4、测试乐观锁
在测试类中分别对成功与失败进行测试
测试成功:
//测试成功的乐观锁@Testvoid testOptimisticLocker_success() { //1.查询用户信息 User user = userMapper.selectById(1l); //2.修改用户信息 user.setName("tian"); user.setAge(21); //3.执行更新操作 userMapper.updateById(user);}
结果如下:
测试失败:
模拟多线程的方式执行插队操作
@Testvoid 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查询用户
//测试查询@Testpublic void testSelectById(){ User user = userMapper.selectById(1L); System.out.println(user);}
结果:
2.批量查询
//批量查询@Testpublic 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.条件查询
通过自定义条件查询
//条件查询@Testpublic 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
达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库
属性介绍
属性名 | 类型 | 默认值 | 描述 |
---|---|---|---|
overflow | boolean | false | 溢出总页数后是否进行处理(默认不处理) |
maxLimit | Long | 单页分页条数限制(默认无限制) | |
dbType | DbType | 数据库类型(根据类型获取应使用的分页方言) | |
dialect | IDialect | 方言实现类 |
建议单一数据库类型的均设置 dbType
如何使用MyBatisPlus内页插件?
1、配置拦截器组件即可
/** * 注册插件 */@Beanpublic 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分页插件@Testpublic 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、删除操作
跟查询操作相似,就不详细讲解,直接上代码了
//测试删除@Testpublic void testDeleteById(){ userMapper.deleteById(4L);}//批量删除@Testpublic void testDeleteBatchId(){ userMapper.deleteBatchIds(Arrays.asList(1L,2L));}//通过map删除@Testpublic 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测试
//测试删除@Testpublic void testDeleteById(){ userMapper.deleteById(4L);}
查看日志输出可以看到,delete的语句以经发生了更改
实质上就是update(修改)语句,将deleted字段从1修改为0
5、对Id为4的用户进行查询
//测试查询@Testpublic 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分页插件@Testpublic 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的用户
@Testvoid WrapperTest(){ //查询name、邮箱不为空且年龄大于等于20的用户 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper .isNotNull("name") .isNotNull("email") .ge("age",12); userMapper.selectList(wrapper).forEach(System.out::println);}
2、查询姓名为小文的用户
@Testvoid WrapperTest2(){ //查询姓名为小文的用户 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("name","小文"); //equals User user = userMapper.selectOne(wrapper); System.out.println(user);}
注意:
查询一个数据出现多个结果就使用List或map
3、查询年龄在19-23之间的用户
@Testvoid 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之间的用户
@Testvoid WrapperTest3(){ //查询年龄在19-23之间的用户 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.between("age", 19, 23); Long count = userMapper.selectCount(wrapper);//查询结果数 System.out.println(count);}
这里的方法取值范围是左开右闭!
5、模糊查询
@Testvoid 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、联表查询
//联表查询@Testvoid 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进行排序
@Testvoid 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、效果图
阅读本书更多章节>>>>这些就是自动生成的代码了,代码你成熟了,应该自己生成了<狗头>
本文链接:https://www.kjpai.cn/gushi/2024-04-07/155023.html,文章来源:网络cs,作者:焦糖,版权归作者所有,如需转载请注明来源和作者,否则将追究法律责任!