[java-spring]-mybatis-plus的快速入门笔记
一、简介
参考教程:http://mp.baomidou.com/guide/
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
二、特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer 等多种数据库
- 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
- 支持 XML 热加载:Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 支持关键词自动转义:支持数据库关键词(order、key......)自动转义,还可自定义关键词
- 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
- 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
- 内置 Sql 注入剥离器:支持 Sql 注入剥离,有效预防 Sql 注入攻击
三、ORM映射关系和target作用范围
ORM映射关系:
java类 mysql数据库
java.lang.Byte byte TINYINT
java.lang.Short short SMALLINT
java.lang.Integer integer INGEGER
java.lang.Long long BIGINT
java.lang.Float float FLOAT
java.lang.Double double DOUBLE
java.lang.BigDecimal big_decimal NUMERIC
java.lang.Boolean boolean BIT
java.lang.String string VARCHAR
java.lang.Boolean yes_no CHAR(1)('Y'或'N')
java.lang.Boolean true_false CHAR(1)(‘Y’或'N')
java.uitl.Date date DATE
java.sql.Date date DATE
java.sql.Time time TIME
java.sql.Timestamp timestamp TIMESTAMP
java.uitl.Calendar celendar TIMESTAMP
java.uitl.Calendar calendar TIMESTAMP
java.io.Serializable serializable VARBINARY/BLOB
java.sql.Clob clob CLOB
java.sql.Blob blob BLOB
java.lang.Class class VARCHAR
java.uitl.Locale locale VARCHAR
java.uitl.TimeZone timezone VARCHAR
java.uitl.Currency currency VARCHAR
target作用范围:
@Target(ElementType.TYPE)——接口、类、枚举、注解
@Target(ElementType.FIELD)——字段、枚举的常量
@Target(ElementType.METHOD)——方法
@Target(ElementType.PARAMETER)——方法参数
@Target(ElementType.CONSTRUCTOR) ——构造函数
@Target(ElementType.LOCAL_VARIABLE)——局部变量
@Target(ElementType.ANNOTATION_TYPE)——注解
@Target(ElementType.PACKAGE)——包
@Retention:注解的保留位置
RetentionPolicy.SOURCE:这种类型的Annotations
只在源代码级别保留,编译时就会被忽略,在class字节码文件中不包含。
RetentionPolicy.CLASS:这种类型的Annotations编译时被保留,默认的保留策略,
在class文件中存在,但JVM将会忽略,运行时无法获得。
RetentionPolicy.RUNTIME:这种类型的Annotations将被JVM保留,
所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。
@Document:说明该注解将被包含在javadoc中
@Inherited:说明子类可以继承父类中的该注解
四、MyBatisPlus入门
快速开始参考:http://mp.baomidou.com/guide/quick-start.html
测试项目: mybatis_plus
数据库:mybatis_plus
1、创建并初始化数据库
1.1、创建数据库:
mybatis_plus
1.2、创建 User
表
其表结构如下:
id | name | age | |
---|---|---|---|
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 |
其对应的数据库 Schema 脚本如下:
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)
);
其对应的数据库 Data 脚本如下:
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、初始化工程
使用 Spring Initializr 快速初始化一个 Spring Boot 工程
Group:com.atguigu
Artifact:mybatis-plus
版本:2.3.1.RELEASE
3、添加依赖
3.1、 引入依赖
spring-boot-starter、spring-boot-starter-test
添加:mybatis-plus-boot-starter、MySQL、lombok
、
在项目中使用Lombok可以减少很多重复代码的书写。比如说getter/setter/toString等方法的编写
<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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--lombok用来简化实体类-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
注意:引入 MyBatis-Plus
之后请不要再次引入 MyBatis
以及 MyBatis-Spring
,以避免因版本差异导致的问题。
3.2、idea中安装lombok插件
(1)idea2020.1版本(默认没这个插件)
官网插件下载地址:https://plugins.jetbrains.com/plugin/6317-lombok/versions
选中lombok-plugin-0.30-2020.1这个版本,即:0.30-2020.1
(2)idea安装
依次点击:
file-->Setting-->Plugins-->切换到Installed-->点击右边的设置按钮Installed Plugin from Disk...-->选择下载的lombok-plugin-0.30-2020.1.zip
4、配置
在 application.properties
配置文件中添加 MySQL 数据库的相关配置:
mysql5:
#mysql数据库连接
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus
spring.datasource.username=root
spring.datasource.password=123456
mysql8以上(spring boot 2.1以上,这里用这个)
注意:driver和url的变化
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
注意:
1、这里的 url 使用了 ?serverTimezone=GMT%2B8 后缀,因为Spring Boot 2.1 集成了 8.0版本的jdbc驱动,这个版本的 jdbc 驱动需要添加这个后缀,否则运行测试用例报告如下错误:
java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more
2、这里的 driver-class-name 使用了 com.mysql.cj.jdbc.Driver ,在 jdbc 8 中 建议使用这个驱动,之前的 com.mysql.jdbc.Driver 已经被废弃,否则运行测试用例的时候会有 WARN 信息
5、编写代码
5.1、实体类
创建包 entity 编写实体类 User.java
(此处使用了 Lombok 简化代码)
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
Lombok使用参考:
https://blog.csdn.net/motui/article/details/79012846
5.2、mapper
创建包 mapper 编写Mapper 接口: UserMapper.java
@Repository
@Mapper //在启动类中取扫描
public interface UserMapper extends BaseMapper<User> {
//所有的crud操作都已经完成编写了
//你不需要像以前配置一大堆文件了
}
6、开始使用
Test类:
@SpringBootTest
class Mpdemo1010ApplicationTests {
@Autowired
private UserMapper userMapper;
// 查询user表的所有数据
@Test
public void findAll() {
List<User> users = userMapper.selectList(null);
System.out.println(users);
}
}
注意:
IDEA在 userMapper 处报错,因为找不到注入的对象,因为类是动态创建的,但是程序可以正确的执行。
为了避免报错,可以在 dao 层 的接口上添加 @Repository 注
控制台输出:
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)
7、配置日志
application.properties添加:
查看sql输出日志:
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
五、MyBatisPlus的CRUD 接口
1、insert
1.1、插入操作
Test类
@SpringBootTest
class Mpdemo1010ApplicationTests {
@Autowired
private UserMapper userMapper;
//添加操作
@Test
public void addUser(){
User user = new User();
user.setName("岳不群");
user.setAge(30);
user.setEmail("lucy@qq.com");
int insert = userMapper.insert(user);
System.out.println("insert:" + insert);
}
}
5.5.2、主键策略
雪花算法:
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使
用41bit作为 毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作
为毫秒内的流水号(意味 着每个节点在每毫秒可以产生 4096 个 ID),后还有一个符号位,
永远是0。可以保证几乎全球唯 一!
(1)ID_WORKER
MyBatis-Plus默认的主键策略是:ID_WORKER 全局唯一ID
参考资料:分布式系统唯一ID生成方案汇总:https://www.cnblogs.com/haoxinyue/p/5208136.html
(2)自增策略
-
要想主键自增需要配置如下主键策略
-
- 需要在创建数据表的时候设置主键自增
- 实体字段中配置 @TableId(type = IdType.AUTO)
@TableId(type = IdType.AUTO)
private Long id;
要想影响所有实体的配置,可以设置全局主键配置
#全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type=auto
其它主键策略:分析 IdType 源码可知
@Getter
public enum IdType {
/**
* 数据库ID自增
*/
AUTO(0),
/**
* 该类型为未设置主键类型
*/
NONE(1),
/**
* 用户输入ID
* 该类型可以通过自己注册自动填充插件进行填充
*/
INPUT(2),
/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
/**
* 全局唯一ID (idWorker)
*/
ID_WORKER(3),
/**
* 全局唯一ID (UUID)
*/
UUID(4),
/**
* 字符串全局唯一ID (idWorker 的字符串表示)
*/
ID_WORKER_STR(5);
private int key;
IdType(int key) {
this.key = key;
}
}
2、 update
2.1、根据Id更新操作
注意:update时生成的sql自动是动态sql:UPDATE user SET age=? WHERE id=?
Test类:
//修改操作
@Test
public void updateUser(){
User user = new User();
user.setId(1L);
user.setAge(120);
int row = userMapper.updateById(user);
System.out.println(row);
}
2.2、自动填充
项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。
我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作:
(1)数据库表中添加自动填充字段
在User表中添加datetime类型的新的字段 create_time、update_time,数据类型为datetime
(2)实体上添加注解
@Data //get set 有参 无参
public class User {
......
// create_time //自动插入填充
@TableField(fill = FieldFill.INSERT)
private Date createTime;
// update_time //自动更新填充
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
(3)实现元对象处理器接口
注意:不要忘记添加 @Component 注解
创建handler包,增加MyMetaObjectHandler.java
package com.atguigu.mpdemo1010.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component //交给spring管理
public class MyMetaObjectHandler implements MetaObjectHandler {
//使用mp实现添加操作,这个方法执行
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
//使用mp实现修改操作,这个方法执行
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
(4)测试
2.3、乐观锁
简介:
- 乐观锁 : 故名思意十分乐观,它总是认为不会出现问题,无论干什么不去上锁!如果出现了问题, 再次更新值测试
- 悲观锁:故名思意十分悲观,它总是认为总是出现问题,无论干什么都会上锁!再去操作
主要适用场景:当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
(1)数据库中添加version字段
ALTER TABLE `user` ADD COLUMN `version` INT
(2)实体类(User.java)添加version字段
并添加 @Version 注解
@Version
@TableField(fill = FieldFill.INSERT)
private Integer version; //版本号
(3)元对象处理器接口(MyMetaObjectHandler.java)添加version的insert默认值
@Component //交给spring管理
public class MyMetaObjectHandler implements MetaObjectHandler {
//使用mp实现添加操作,这个方法执行
@Override
public void insertFill(MetaObject metaObject) {
......
this.setFieldValByName("version",1,metaObject);
}
特别说明:
支持的数据类型只有 int,Integer,long,Long,Date,Timestamp,LocalDateTime整数类型下 newVersion = oldVersion + 1``newVersion
会回写到 entity
中仅支持 updateById(id)
与 update(entity, wrapper)
方法在 update(entity, wrapper)
方法下, wrapper
不能复用!!!
(4)在 MybatisPlusConfig 中注册 Bean
创建config包,配置类:MpConfig.java
//Spring Boot 使用事务非常简单,首先使用注解 @EnableTransactionManagement
// 开启事务支持后,然后在访问数据库的Service方法上添加注解 @Transactional 便可
@EnableTransactionManagement
@Configuration
@MapperScan("com.atguigu.mpdemo1010.mapper") //扫描到接口
public class MpConfig {
/**
* 乐观锁插件
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
(5)测试乐观锁可以修改成功
测试后分析打印的sql语句,将version的数值进行了加1操作
Test类
// 测试乐观锁
@Test
public void testOptimisticLocker(){
// 根据id查询数据
User user = userMapper.selectById(1L);
//进行修改
user.setAge(200);
//执行更新
userMapper.updateById(user);
}
(6)测试乐观锁修改失败
/**
* 测试乐观锁插件 失败
*/
@Test
public void testOptimisticLockerFail() {
//查询
User user = userMapper.selectById(1L);
//修改数据
user.setName("Helen Yao1");
user.setEmail("helen@qq.com1");
//模拟取出数据后,数据库中version实际数据比取出的值大,即已被其它线程修改并更新了version
user.setVersion(user.getVersion() - 1);
//执行更新
userMapper.updateById(user);
}
3、select
3.1、根据id查询记录
@Test
public void testSelectById(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
3.2、通过多个id批量查询
完成了动态sql的foreach的功能
//多个id批量查询
@Test
public void testSelectDemo1(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
System.out.println(users);
}
3.3、简单的条件查询
通过map封装查询条件
@Test
public void testSelectByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name", "Helen");
map.put("age", 18);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
3.4、分页
MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能
(1)创建配置类
此时可以删除主类中的 @MapperScan 扫描注解或者mapper接口的@Mapper扫描注解
在config.MpConfig.java编写页插件:
@EnableTransactionManagement
@Configuration
@MapperScan("com.atguigu.mpdemo1010.mapper") //扫描到接口
public class MpConfig {
......
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
(2)测试selectPage分页
测试:最终通过page对象获取相关数据
//分页查询
@Test
public void testPage(){
//1 创建page对象
//传入两个参数,当前页 和 每页显示记录数
Page<User> page = new Page<>();
//调用mp分页查询的方法
//调用mp分页查询过程中,底层封装
//把分页所有数据封装到page对象里面
userMapper.selectPage(page,null);
//通过page对象获取分页数据
System.out.println(page.getCurrent()); // 当前页
System.out.println(page.getRecords()); // 每页数据list集合
System.out.println(page.getSize()); //每页显示记录数
System.out.println(page.getTotal()); //总记录数
System.out.println(page.getPages()); //总页数
System.out.println(page.hasNext()); //下一页
System.out.println(page.hasPrevious()); //上一页
}
控制台sql语句打印:SELECT id,name,age,email,create_time,update_time FROM user LIMIT 0,5
(3)测试selectMapsPage分页:结果集是Map
@Test
public void testSelectMapsPage() {
Page<User> page = new Page<>(1, 5);
IPage<Map<String, Object>> mapIPage = userMapper.selectMapsPage(page, null);
//注意:此行必须使用 mapIPage 获取记录列表,否则会有数据类型转换错误
mapIPage.getRecords().forEach(System.out::println);
System.out.println(page.getCurrent());
System.out.println(page.getPages());
System.out.println(page.getSize());
System.out.println(page.getTotal());
System.out.println(page.hasNext());
System.out.println(page.hasPrevious());
}
4、delete
4.1、根据id删除记录
@Test
public void testDeleteById(){
int result = userMapper.deleteById(8L);
System.out.println(result);
}
4.2、批量删除
@Test
public void testDeleteBatchIds() {
int result = userMapper.deleteBatchIds(Arrays.asList(8, 9, 10));
System.out.println(result);
}
4.3、简单的条件查询删除
@Test
public void testDeleteByMap() {
HashMap<String, Object> map = new HashMap<>();
map.put("name", "Helen");
map.put("age", 18);
int result = userMapper.deleteByMap(map);
System.out.println(result);
}
4.4、逻辑删除
- 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据(以上为物理删除)
- 逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录
(1)数据库中添加 deleted字段
ALTER TABLE `user` ADD COLUMN `deleted` boolean
设置默认值为0
(2)实体类(User.java)添加deleted *字段*
并加上 @TableLogic 注解
@TableLogic
private Integer deleted;
(3)application.properties 加入配置
此为默认值,如果你的默认值和mp默认的一样,该配置可无
# 配置逻辑删除
# 逻辑已删除值(默认为 1)
mybatis-plus.global-config.db-config.logic-delete-value=1
# 逻辑未删除值(默认为 0)
mybatis-plus.global-config.db-config.logic-not-delete-value=0
(4)逻辑删除配置类
注意,3.32版本中已经不用写这个了,3.05版本还需要在配置类Config中添加
/**
* 逻辑删除插件
*/
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
(5)测试逻辑删除
- 测试后发现,数据并没有被删除,deleted字段的值由0变成了1
- 测试后分析打印的sql语句,是一条update
- 注意:被删除数据的deleted 字段的值必须是 0,才能被选取出来执行逻辑删除的操作
/**
* 测试 逻辑删除
*/
@Test
public void testLogicDelete() {
int result = userMapper.deleteById(1L);
System.out.println(result);
}
(6)测试逻辑删除后的查询
/**
* 测试 逻辑删除后的查询:
* 不包括被逻辑删除的记录
*/
@Test
public void testLogicDeleteSelect() {
User user = new User();
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
测试后分析打印的sql语句,包含 WHERE deleted=0
SELECT id,name,age,email,create_time,update_time,deleted FROM user WHERE deleted=0
5、性能分析(3.2以上版本移除这个插件)
性能分析拦截器,用于输出每条 SQL 语句及其执行时间
SQL 性能执行分析,开发环境使用,超过指定时间,停止运行。有助于发现问题
5.1、配置插件
(1)参数说明
参数:maxTime: SQL 执行最大时长,超过自动停止运行,有助于发现问题。
参数:format: SQL是否格式化,默认false。
(2)在 MybatisPlusConfig 中配置
/**
* SQL 执行性能分析插件
* 开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长
*/
@Bean
@Profile({"dev","test"})// 设置 dev test 环境开启
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(100);//ms,超过此处设置的ms则sql不执行
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
(3)Spring Boot 中设置dev环境
#环境设置:dev、test、prod
spring.profiles.active=dev
可以针对各环境新建不同的配置文件application-dev.properties
、application-test.properties
、application-prod.properties
也可以自定义环境名称:如test1、test2
5.2、测试
(1)常规测试
/**
* 测试 性能分析插件
*/
@Test
public void testPerformance() {
User user = new User();
user.setName("我是Helen");
user.setEmail("helen@sina.com");
user.setAge(18);
userMapper.insert(user);
}
输出:
(2)将maxTime 改小之后再次进行测试
performanceInterceptor.setMaxTime(5);//ms,超过此处设置的ms不执行
如果执行时间过长,则抛出异常:The SQL execution time is too large,
输出:
6、其它
如果想进行复杂条件查询,那么需要使用条件构造器 Wapper,涉及到如下方法
-
1、delete
-
2、selectOne
-
3、selectCount
-
4、selectList
-
5、selectMaps
-
6、selectObjs
-
7、update
六、MyBatisPlus条件构造器
1、wapper介绍
Wrapper : 条件构造抽象类,最顶端父类
AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
QueryWrapper : Entity 对象封装操作类,不是用lambda语法
UpdateWrapper : Update 条件封装,用于Entity对象更新操作
AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。
LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper
LambdaUpdateWrapper : Lambda 更新封装Wrapper
@SpringBootTest
public class QueryWrapperTests {
@Autowired
private UserMapper userMapper;
}
2、AbstractWrapper
注意:以下条件构造器的方法入参中的 column
均表示数据库字段
2.1、ge、gt、le、lt、isNull、isNotNull
ge
//mp实现复杂查询操作
@Test
public void testSelectQuery() {
//创建对象
QueryWrapper<User> wrapper = new QueryWrapper<>();
//通过QueryWrapper设置条件
//ge、gt、le、lt
//查询age>=30记录
//第一个参数字段名称,第二个参数设置值
wrapper.ge("age",30);
List<User> users = userMapper.selectList(wrapper);
System.out.println(users);
}
isNull:
@Test
public void testDelete() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.isNull("name")
.ge("age", 12)
.isNotNull("email");
int result = userMapper.delete(queryWrapper);
System.out.println("delete return count = " + result);
}
SQL:UPDATE user SET deleted=1 WHERE deleted=0 AND name IS NULL AND age >= ? AND email IS NOT NULL
2.2、eq、ne
注意:seletOne返回的是一条实体记录,当出现多条时会报错
//mp实现复杂查询操作
@Test
public void testSelectQuery() {
//创建对象
QueryWrapper<User> wrapper = new QueryWrapper<>();
//eq、ne
//eq 等于
//ne 不等于
wrapper.eq("name","八戒");
List<User> users1 = userMapper.selectList(wrapper);
System.out.println(users1);
}
SELECT id,name,age,email,create_time,update_time,deleted,version FROM user WHERE deleted=0 AND name = ?
2.3、between、notBetween
包含大小边界
//mp实现复杂查询操作
@Test
public void testSelectQuery() {
//创建对象
QueryWrapper<User> wrapper = new QueryWrapper<>();
//between 在....之间
//查询年龄 20-30
wrapper.between("age",30,40);
List<User> users2 = userMapper.selectList(wrapper);
System.out.println(users2);
SELECT COUNT(1) FROM user WHERE deleted=0 AND age BETWEEN ? AND ?
2.4、allEq
@Test
public void testSelectList() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
Map<String, Object> map = new HashMap<>();
map.put("id", 2);
map.put("name", "Jack");
map.put("age", 20);
queryWrapper.allEq(map);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
SELECT id,name,age,email,create_time,update_time,deleted,version
FROM user WHERE deleted=0 AND name = ? AND id = ? AND age = ?
2.5、like、notLike、likeLeft、likeRight
selectMaps返回Map集合列表
//mp实现复杂查询操作
@Test
public void testSelectQuery() {
//创建对象
QueryWrapper<User> wrapper = new QueryWrapper<>()
//like 模糊查询
wrapper.like("name","八");
List<User> users3 = userMapper.selectList(wrapper);
System.out.println(users3);
}
2.6、in、notIn、inSql、notinSql、exists、notExists
in、notIn:
- notIn("age",{1,2,3})--->age not in (1,2,3)
- notIn("age", 1, 2, 3)--->age not in (1,2,3)
inSql、notinSql:可以实现子查询
- 例:
inSql("age", "1,2,3,4,5,6")
--->age in (1,2,3,4,5,6)
- 例:
inSql("id", "select id from table where id < 3")
--->id in (select id from table where id < 3)
@Test
public void testSelectObjs() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//queryWrapper.in("id", 1, 2, 3);
queryWrapper.inSql("id", "select id from user where id < 3");
List<Object> objects = userMapper.selectObjs(queryWrapper);//返回值是Object列表
objects.forEach(System.out::println);
}
SELECT id,name,age,email,create_time,update_time,deleted,version
FROM user WHERE deleted=0 AND id IN (select id from user where id < 3)
2.7、or、and
注意:这里使用的是 UpdateWrapper
不调用or
则默认为使用 and
连
@Test
public void testUpdate1() {
//修改值
User user = new User();
user.setAge(99);
user.setName("Andy");
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "h")
.or()
.between("age", 20, 30);
int result = userMapper.update(user, userUpdateWrapper);
System.out.println(result);
}
UPDATE user SET name=?, age=?, update_time=? WHERE deleted=0 AND name LIKE ? OR age BETWEEN ? AND ?
2.8、嵌套or、嵌套and
这里使用了lambda表达式,or中的表达式最后翻译成sql时会被加上圆括号
@Test
public void testUpdate2() {
//修改值
User user = new User();
user.setAge(99);
user.setName("Andy");
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "h")
.or(i -> i.eq("name", "李白").ne("age", 20));
int result = userMapper.update(user, userUpdateWrapper);
System.out.println(result);
}
UPDATE user SET name=?, age=?, update_time=?
WHERE deleted=0 AND name LIKE ?
OR ( name = ? AND age <> ? )
2.9、orderBy、orderByDesc、orderByAsc
@Test
public void testSelectListOrderBy() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
SELECT id,name,age,email,create_time,update_time,deleted,version
FROM user WHERE deleted=0 ORDER BY id DESC
2.10、orderByDesc
//mp实现复杂查询操作
@Test
public void testSelectQuery() {
//创建对象
QueryWrapper<User> wrapper = new QueryWrapper<>();
//orderByDesc 降序
wrapper.orderByDesc("id");
2.11、last
直接拼接到 sql 的最后
注意:只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用
@Test
public void testSelectListLast() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.last("limit 1");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
SELECT id,name,age,email,create_time,update_time,deleted,version
FROM user WHERE deleted=0 limit 1
2.12、指定要查询的列
@Test
public void testSelectListColumn() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("id", "name", "age");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
SELECT id,name,age FROM user WHERE deleted=0
2.13、set、setSql
最终的sql会合并 user.setAge(),以及 userUpdateWrapper.set() 和 setSql() 中 的字段
@Test
public void testUpdateSet() {
//修改值
User user = new User();
user.setAge(99);
//修改条件
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();
userUpdateWrapper
.like("name", "h")
.set("name", "老李头")//除了可以查询还可以使用set设置修改的字段
.setSql(" email = '123@qq.com'");//可以有子查询
int result = userMapper.update(user, userUpdateWrapper);
}
UPDATE user SET age=?, update_time=?, name=?, email = '123@qq.com' WHERE deleted=0 AND name LIKE ?
七、完整代码展示
配置:
使用 Spring Initializr 快速初始化一个 Spring Boot 工程
Group:com.atguigu
Artifact:mybatis-plus
版本:2.3.1.RELEASE
1、pom.xml
<?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.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.atguigu</groupId>
<artifactId>mpdemo1010</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mpdemo1010</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--lombok用来简化实体类-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、application.properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
3、User.java
com.atguigu.mpdemo1010.entity.User.java
package com.atguigu.mpdemo1010.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;
@Data //get set 有参 无参
public class User {
//@TableId(type = IdType.ID_WORKER) //mp自带策略,生19位值,数字类型使用这种策略,比如long
//@TableId(type = IdType.ID_WORKER_STR) // mp自带策略,生19位值,字符串类型使用这种策略
//不写,默认mp会自己识别
private Long id;
private String name;
private Integer age;
private String email;
// create_time //自动插入填充
@TableField(fill = FieldFill.INSERT)
private Date createTime;
// update_time //自动更新填充
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
@Version
@TableField(fill = FieldFill.INSERT)
private Integer version; //版本号
@TableLogic
private Integer deleted;
}
4、UserMapper接口
com.atguigu.mpdemo1010.mapper.UserMapper.interface
package com.atguigu.mpdemo1010.mapper;
import com.atguigu.mpdemo1010.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface UserMapper extends BaseMapper<User> {
}
5、MpConfig.java
com.atguigu.mpdemo1010.config.MpConfig.java
package com.atguigu.mpdemo1010.config;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
//Spring Boot 使用事务非常简单,首先使用注解 @EnableTransactionManagement
// 开启事务支持后,然后在访问数据库的Service方法上添加注解 @Transactional 便可
@EnableTransactionManagement
@Configuration
@MapperScan("com.atguigu.mpdemo1010.mapper") //扫描到接口
public class MpConfig {
/**
* 乐观锁插件
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
6、MyMetaObjectHandler.java
com.atguigu.mpdemo1010.handler.MyMetaObjectHandler.java
package com.atguigu.mpdemo1010.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component //交给spring管理
public class MyMetaObjectHandler implements MetaObjectHandler {
//使用mp实现添加操作,这个方法执行
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
this.setFieldValByName("version",1,metaObject);
}
//使用mp实现修改操作,这个方法执行
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
7、启动类Mpdemo1010Application.java
package com.atguigu.mpdemo1010;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Mpdemo1010Application {
public static void main(String[] args) {
SpringApplication.run(Mpdemo1010Application.class, args);
}
}
8、测试类Mpdemo1010ApplicationTests
package com.atguigu.mpdemo1010;
import com.atguigu.mpdemo1010.entity.User;
import com.atguigu.mpdemo1010.mapper.UserMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@SpringBootTest
class Mpdemo1010ApplicationTests {
@Autowired
private UserMapper userMapper;
// 查询user表的所有数据
@Test
public void findAll() {
List<User> users = userMapper.selectList(null);
System.out.println(users);
}
//添加操作
@Test
public void addUser(){
User user = new User();
user.setName("岳不群");
user.setAge(30);
user.setEmail("lucy@qq.com");
int insert = userMapper.insert(user);
System.out.println("insert:" + insert);
}
//修改操作
@Test
public void updateUser(){
User user = new User();
user.setId(1277183173154881537L);
user.setAge(120);
int row = userMapper.updateById(user);
System.out.println(row);
}
// 测试乐观锁
@Test
public void testOptimisticLocker(){
// 根据id查询数据
User user = userMapper.selectById(1277201129763016706L);
//进行修改
user.setAge(200);
//执行更新
userMapper.updateById(user);
}
//多个id批量查询
@Test
public void testSelectDemo1(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
System.out.println(users);
}
@Test
public void testSelectByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name", "Helen");
map.put("age", 18);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
//分页查询
@Test
public void testPage(){
//1 创建page对象
//传入两个参数,当前页 和 每页显示记录数
Page<User> page = new Page<>();
//调用mp分页查询的方法
//调用mp分页查询过程中,底层封装
//把分页所有数据封装到page对象里面
userMapper.selectPage(page,null);
//通过page对象获取分页数据
System.out.println(page.getCurrent()); // 当前页
System.out.println(page.getRecords()); // 每页数据list集合
System.out.println(page.getSize()); //每页显示记录数
System.out.println(page.getTotal()); //总记录数
System.out.println(page.getPages()); //总页数
System.out.println(page.hasNext()); //下一页
System.out.println(page.hasPrevious()); //上一页
}
//删除操作 物理删除
@Test
public void testDeleteById(){
int result = userMapper.deleteById(1277218322328399874L);
System.out.println(result);
}
//批量删除
@Test
public void testDeleteBatchIds() {
int result = userMapper.deleteBatchIds(Arrays.asList(8, 9, 10));
System.out.println(result);
}
//mp实现复杂查询操作
@Test
public void testSelectQuery() {
//创建对象
QueryWrapper<User> wrapper = new QueryWrapper<>();
//通过QueryWrapper设置条件
//ge、gt、le、lt
//查询age>=30记录
//第一个参数字段名称,第二个参数设置值
//wrapper.ge("age",30);
//List<User> users = userMapper.selectList(wrapper);
//System.out.println(users);
//eq、ne
//eq 等于
//ne 不等于
//wrapper.eq("name","八戒");
//List<User> users1 = userMapper.selectList(wrapper);
//System.out.println(users1);
//between 在....之间
//查询年龄 20-30
//wrapper.between("age",30,40);
//List<User> users2 = userMapper.selectList(wrapper);
//System.out.println(users2);
//like 模糊查询
//wrapper.like("name","八");
//List<User> users3 = userMapper.selectList(wrapper);
//System.out.println(users3);
//orderByDesc 降序
//wrapper.orderByDesc("id");
//last
//wrapper.last("limit 1");
//指定要查询的列
wrapper.select("id","name");
List<User> users4 = userMapper.selectList(wrapper);
System.out.println(users4);
}
}
9、sql数据
/*
SQLyog Ultimate v12.09 (64 bit)
MySQL - 5.7.30 : Database - mybatis_plus
*********************************************************************
*/
/*!40101 SET NAMES utf8 */;
/*!40101 SET SQL_MODE=''*/;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`mybatis_plus` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `mybatis_plus`;
/*Table structure for table `user` */
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
`version` int(11) DEFAULT NULL COMMENT '版本',
`deleted` tinyint(1) unsigned DEFAULT '0' COMMENT '逻辑删除 1 true',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Data for the table `user` */
insert into `user`(`id`,`name`,`age`,`email`,`version`,`deleted`,`create_time`,`update_time`) values (2,'Jack',120,'test2@baomidou.com',NULL,NULL,NULL,NULL),(3,'Tom',28,'test3@baomidou.com',NULL,NULL,NULL,NULL),(4,'Sandy',21,'test4@baomidou.com',NULL,NULL,NULL,NULL),(5,'Billie',24,'test5@baomidou.com',NULL,NULL,NULL,NULL),(122,'猴子',35,'hou123@qq.com',1,0,'2020-06-17 21:16:53','2020-06-17 21:16:58'),(123,'八戒',39,'bajie@qq.com',1,0,'2020-06-16 21:17:38','2020-06-16 21:17:41'),(124,'唐僧',40,'tangseng@qq.com',1,0,'2020-06-01 21:19:34','2020-06-04 21:19:37'),(1277165664624279553,'lucy',30,'lucy@qq.com',NULL,NULL,NULL,NULL),(1277165679929241601,'lucy',30,'lucy@qq.com',NULL,NULL,NULL,NULL),(1277166162727149570,'lucy111',30,'lucy@qq.com',NULL,NULL,NULL,NULL),(1277166178170597378,'lucy111',30,'lucy@qq.com',NULL,NULL,NULL,NULL),(1277168996113833985,'lucy222',30,'lucy@qq.com',NULL,NULL,NULL,NULL),(1277183173154881537,'lilei',120,'lucy@qq.com',NULL,NULL,'2020-06-28 18:12:52','2020-06-28 19:19:59'),(1277199820800450562,'东方不败',30,'lucy@qq.com',NULL,NULL,'2020-06-28 19:19:01','2020-06-28 19:19:01'),(1277200061129822209,'东方不败',30,'lucy@qq.com',NULL,NULL,'2020-06-28 19:19:58','2020-06-28 19:19:58'),(1277201129763016706,'令狐冲',200,'lucy@qq.com',2,NULL,'2020-06-28 19:24:13','2020-06-28 19:29:33'),(1277218322328399874,'岳不群',30,'lucy@qq.com',1,1,'2020-06-28 20:32:32','2020-06-28 20:32:32'),(1277228495226859521,'岳不群',30,'lucy@qq.com',1,0,'2020-06-28 21:12:58','2020-06-28 21:12:58'),(1277228576806039553,'岳不群',30,'lucy@qq.com',1,0,'2020-06-28 21:13:17','2020-06-28 21:13:17');
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2020-03-09 人形词云,根据图片黑白形状绘制词云
2020-03-09 汉化的simple词云
2020-03-09 对于数据的平均值处理
2020-03-09 文字替换成函数返回数字进行排序
2020-03-09 校验
2020-03-09 Python:使用lambda对列表(list)和字典(dict)排序
2020-03-09 python中的数据排序