目录
总结,以上是对MP的一些基础做了大致的讲解,文章若出现不足,或者错误,欢迎大家指点。
一、Mybatis-Plus的简介
1、介绍
Mybatis-plus 简称(MP) 是一个Mybatis的中增加工具,在Mybatis的基础上只做增强不做该变,为简化开发、提高效率而生。
2、特性
-
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
-
损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
3、支持的数据库
-
MySQL,Oracle,DB2,H2,HSQL,SQLite,PostgreSQL,SQLServer,Phoenix,Gauss ,ClickHouse,Sybase,OceanBase,Firebird,Cubrid,Goldilocks,csiidb,informix,TDengine,redshift
-
达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库,优炫数据库
4、框架结构
二、Mybatis-Plus的环境配置
1、我这里是以整合Spring Boot为主,springboot的初始化工程就演示了,那么我就在Spring Boot 环境的中导入Mybatis-Plus的相关依赖。
<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>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
这里只需要引入mybatis-plus依赖即可,因为mybatis-plus依赖中包含了mybatis依赖。
2、在Spring Boot的全局配置文件中配置mybatis-plus的相关配置以及数据库连接参数(我这里是以application.yaml配置为主)。
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatisplus?serverTimezone=GMT%2B8
password: root
username: root
driver-class-name: com.mysql.cj.jdbc.Driver
# 去除控制台spring默然图标输出
main:
banner-mode: off
mybatis-plus:
global-config:
# 去除mybatis控制台输出图标
banner: false
# 查看mybatis-plus底层怎么拼接SQL的(执行的过程)
configuration:
# 控制台mybatis-Plus底层操作数据库日志输出
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 开启包别名扫描
type-aliases-package: com.demo.mybatispulso2.domain
# 开启mapper配置文件扫描
mapper-locations: classpath:mapper/*.xml
三、Mybatis-Plus测试数据导入
1、创建数据库
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、数据插入
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');
四、Mybatis-Plus基本使用
1、编写实体类
因为我们的数据库是user数据库,所以我们需要创建User实体类,用于对数据的持久化,我在的实体类中引入一个lombok依赖,这个依赖可以快速的生成set、get等方法,这里我就不多做解释。
package com.demo.mybatispulso2.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @BelongsProject: mybatisplusdemo
* @BelongsPackage: com.demo.mybatispulso2.domain
* @Author: 云边小屋(My.Tears)
* @CreateTime: 2023-05-30 17:26
* @Description: TODO
* @Version: 1.0
*/
// 自动创建set、get等方法
@Data
// 无参构造器
@NoArgsConstructor
// 有参构造器
@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
2、编写Dao层
创建一个UserMapper接口并继承BaseMapper<T>类
package com.demo.mybatispulso2.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.demo.mybatispulso2.domain.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
// 该注解表示可被springboot扫描到,并被springboot识别为mapper接口
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
我这里定义的mapper接口里面的为何没有方法,因为我们继承了一个BaseMapper<T>类,我们可以查看这个类的底层,可以看到这个类中包含了一些对于数据库增删查改方法,这里就体现了mybatis-plus强大的CRUD操作,因为你只要继承这个类就可以获得一些基本的增删查改方法,就不要再重复的编写那些代码了。
3、测试
直接在SpringBoot的test文件夹下编写测试代码,你也可以选择不再这里测试,去基于controller测试,我这里主要是以test文件夹测试,因为我们引入了springboot的相关测试依赖,在这里测试可以更方便快捷,这里我只测试增删查改的方法,其他的方法你可以自行的去测试。
测试添加方法
package com.demo.mybatispulso2;
import com.demo.mybatispulso2.domain.User;
import com.demo.mybatispulso2.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class Mybatispulso2ApplicationTests {
// 注入UserMapper接口
private UserMapper userMapper;
// 自动注入
@Autowired
Mybatispulso2ApplicationTests(UserMapper userMapper) {
this.userMapper = userMapper;
}
// 新增数据测试
@Test
void insert() {
User user = new User();
user.setId(6L);
user.setName("那边一条云");
user.setEmail("test@mybatisplus.com");
user.setAge(18);
// 因为我们的UserMapper继承了BaseMapper<User> 所以可以直接使用该类中的方法
userMapper.insert(user);
}
}
运行结果,由于我们在全局配置中配置了mybatis-Plus底层操作数据库日志输出,所以我们可以看到,当我们运行这个方法的时候,mybatis-plus会在底层自动的拼接这个insert sql语句,数据是否真正的添加到数据库中去了,查看数据库即可。
接下来,继续测试查询单条方法。
// 查询单条数据
@Test
void selectOne(){
// 查询id为1的数据
User user = userMapper.selectById(1L);
System.out.println(user);
}
测试结果,还是同样的自动拼接SQL语句,并且控制台也成功的输出了查询结果。
接下来,继续测试查询多条方法。
// 查询多条数据
@Test
void selectList(){
/**
* 这里为什么要给selectList()方法null
* List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);
* 因为这个方法里面的参数是一个条件对象 我们现在还没有接触这个条件对象 所以为null
* 为null 就说明没有条件 返回所有的数据
*/
List<User> users = userMapper.selectList(null);
for (User user : users) {
System.out.println(user);
}
}
测试结果,我们可以看到能够通过测试,并且返回所以的参数。
接下来,继续测试修改方法。
// 修改操作
@Test
void updateById(){
User user = new User();
user.setId(6L);
user.setAge(58);
user.setName("憨憨");
userMapper.updateById(user);
}
测试结果,成功通过测试,并且可以看到拼接的SQL语句,数据是否真正的修改到数据库中去了,查看数据库即可。
接下来,继续测试删除方法。
// 删除操作
@Test
void deleteById(){
// 根据id删除数据
userMapper.deleteById(6L);
}
测试结果,成功通过测试。
如果想通过service层来调用dao层的方法,而不是直接使用dao层的方法,那么下来我就来大致演示一下。
4、编写service层
除了Mapper接口外,mybatis-plus还提供了service层的接口,而service层的接口我们也不需要编写那些增删查改的方法,只需要继承一个叫IService<T>的接口和对应的实现类ServiceImpl,该实现类已经提供好了一些对应的方法,我们可以直接使用,那么我们来实现一个UserService接口并继承IService<User>。
package com.demo.mybatispulso2.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.demo.mybatispulso2.domain.User;
import com.demo.mybatispulso2.mapper.UserMapper;
import java.util.List;
/**
* @BelongsProject: mybatisplusdemo
* @BelongsPackage: com.demo.mybatispulso2.service
* @Author: 云边小屋(My.Tears)
* @CreateTime: 2023-05-30 17:31
* @Description: TODO
* @Version: 1.0
*/
/**
* 继承IService接口 让其具备mybatisPlus的Service接口的一些增删查改方法
*/
public interface UserService extends IService<User> {
}
为什么可以直接使用呢,我们可以看看IService的底层代码,我们可以看到这个接口里面有着许多的方法,我就说一个我们认识的方法,这个红框里面的方法是一个新增方法,我们可以看到这个方法调用了BaseMapper接口里面的insert方法,来进行新增操作。
我们既然创建了UserService接口,那么这个接口必须得有一个实现类才可以,接下来,我们创建一个这个接口的实现类,并继承这个ServiceImpl类和实现UserService接口
package com.demo.mybatispulso2.service.Impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.demo.mybatispulso2.domain.User;
import com.demo.mybatispulso2.mapper.UserMapper;
import com.demo.mybatispulso2.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @BelongsProject: mybatisplusdemo
* @BelongsPackage: com.demo.mybatispulso2.service.Impl
* @Author: 云边小屋(My.Tears)
* @CreateTime: 2023-05-30 17:32
* @Description: TODO
* @Version: 1.0v
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {
}
为什么要继承ServiceImpl类和实现UserService接口,因为这样不仅继承了mybatisplus的那些基础通用的 CRUD 功能方法,而且还可以编写自己的接口,实现自己的接口,在实际开发中,解决那些复杂的业务.我们可以通过ServiceImpl这个类的底层可以看到这个类实现了IService接口,里面的代码我就不多做解释了,有兴趣的可以去看看,并且这个ServiceImpl类携带2个参数,第一个是BaseMapper,第二个是一个泛型,说明需要2个参数。
基于service层的测试代码,具体的查询结果我就不演示了。
package com.demo.mybatispulso2;
import com.demo.mybatispulso2.domain.User;
import com.demo.mybatispulso2.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* @BelongsProject: mybatisplusdemo
* @BelongsPackage: com.demo.mybatispulso2
* @Author: 云边小屋(My.Tears)
* @CreateTime: 2023-06-04 00:31
* @Description: TODO
* @Version: 1.0
*/
@SpringBootTest
public class ServiceTest {
private UserService userService;
@Autowired
public ServiceTest(UserService userService) {
this.userService = userService;
}
// 新增操作
@Test
void save(){
User user = new User();
user.setId(6L);
user.setName("我是添加的数据");
user.setEmail("save@qq.com");
user.setAge(45);
userService.save(user);
}
// 删除操作
@Test
void removeById(){
userService.removeById(6L);
}
// 修改操作
@Test
void updateById(){
User user = new User();
user.setId(6L);
user.setName("修改的数据");
user.setAge(45);
userService.updateById(user);
}
// 查询单个操作
@Test
void getById(){
User user = userService.getById(1L);
System.out.println(user);
}
// 查询所有操作
@Test
void list(){
// 无查询条件 返回所有数据
List<User> users = userService.list(null);
for (User user : users) {
System.out.println(user);
}
}
}
mybatis-plus不仅仅内置了通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求,并且它还支持自定义Mapper文件对方法进行注入,如果你不喜欢mybati-plus自带的Mapper、Service,可以自行的编写Mapper、Service接口以及sql语句。
五、自定义Mapper接口方法
1、编写dao层
创建一个UserMapeer接口
package com.demo.mybatispulso2.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.demo.mybatispulso2.domain.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface UserMapper extends BaseMapper<User> {
/**
* 根据id查询数据
* @param id id
* @return {@link User}
*/
User findById(Long id);
/**
* 查询所有
* @return {@link List}<{@link User}>
*/
List<User> findAll();
/**
* 修改用户信息
* @param user 用户
* @return {@link Integer}
*/
Integer UserUpdate(User user);
/**
* 根据id删除用户
* @param id id
*/
void UserDelete(Long id);
}
2、编写service层
创建UserService
package com.demo.mybatispulso2.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.demo.mybatispulso2.domain.User;
import com.demo.mybatispulso2.mapper.UserMapper;
import java.util.List;
/**
* @BelongsProject: mybatisplusdemo
* @BelongsPackage: com.demo.mybatispulso2.service
* @Author: 云边小屋(My.Tears)
* @CreateTime: 2023-05-30 17:31
* @Description: TODO
* @Version: 1.0
*/
/**
* 继承IService接口 让其具备mybatisPlus的Service接口的一些增删查改方法
*/
public interface UserService extends IService<User> {
/**
* 根据id查询数据
* @param id id
* @return {@link User}
*/
User findById(Long id);
/**
* 查询所有
* @return {@link List}<{@link User}>
*/
List<User> findAll();
/**
* 修改用户信息
* @param user 用户
* @return {@link Integer}
*/
Integer UserUpdate(User user);
/**
* 根据id删除用户
* @param id id
*/
void UserDelete(Long id);
}
3、实现UserService
创建UserServiceImpl类,实现UserService接口
package com.demo.mybatispulso2.service.Impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.demo.mybatispulso2.domain.User;
import com.demo.mybatispulso2.mapper.UserMapper;
import com.demo.mybatispulso2.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @BelongsProject: mybatisplusdemo
* @BelongsPackage: com.demo.mybatispulso2.service.Impl
* @Author: 云边小屋(My.Tears)
* @CreateTime: 2023-05-30 17:32
* @Description: TODO
* @Version: 1.0v
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {
private UserMapper userMapper;
@Autowired
public UserServiceImpl(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
public User findById(Long id) {
return userMapper.findById(id);
}
@Override
public List<User> findAll() {
return userMapper.findAll();
}
@Override
public Integer UserUpdate(User user) {
return userMapper.UserUpdate(user);
}
@Override
public void UserDelete(Long id) {
userMapper.UserDelete(id);
}
}
4、编写UserMapper.xml文件
在resource文件下创建mapper文件夹,并在该文件下创建一个与UserMapper同名的UserMapper.xml文件,由于我在全局配置文件中配置了该文件的包扫描,所以不用与文件夹同名。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.demo.mybatispulso2.mapper.UserMapper">
<!--查询单个-->
<select id="findById" resultType="user">
select *
from user
where id = #{id}
</select>
<!--查询所有-->
<select id="findAll" resultType="user">
select *
from user
</select>
<!--删除数据-->
<delete id="UserDelete">
delete
from user
where id = #{id}
</delete>
<!--新增数据-->
<insert id="UserAdd">
insert into user (id, `name`, age, email)
VALUES (#{id}, #{name}, #{age}, #{email})
</insert>
<!--修改数据-->
<update id="UserUpdate" parameterType="user">
update user
<trim prefix="set" suffixOverrides=",">
<if test="name!=null">
name = #{name},
</if>
<if test="age!=null">
age = #{age},
</if>
<if test="email!=null">
email = #{email},
</if>
</trim>
where id = #{id}
</update>
</mapper>
5、测试
测试结果,就不展示了,只展示测试代码。
package com.demo.mybatispulso2;
import com.demo.mybatispulso2.domain.User;
import com.demo.mybatispulso2.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* @BelongsProject: mybatisplusdemo
* @BelongsPackage: com.demo.mybatispulso2
* @Author: 云边小屋(My.Tears)
* @CreateTime: 2023-06-04 18:16
* @Description: TODO
* @Version: 1.0
*/
@SpringBootTest
public class MyMapperTest {
private UserService userService;
@Autowired
public MyMapperTest(UserService userService) {
this.userService = userService;
}
// 新增
@Test
void UserAdd(){
User user = new User();
user.setId(6L);
user.setName("自定义Mapper");
user.setAge(45);
user.setEmail("MyMapper@qq.com");
userService.UserAdd(user);
}
// 删除
@Test
void UserDelete(){
userService.UserDelete(6L);
}
// 修改
@Test
void UserUpdate(){
User user = new User();
user.setId(6L);
user.setName("自定义Mapper");
user.setAge(20);
userService.UserUpdate(user);
}
// 查询
@Test
void findById(){
userService.findById(6L);
}
// 查询
@Test
void findAll(){
List<User> users = userService.findAll();
for (User user : users) {
System.out.println(user);
}
}
}
从这里就可以看出来,Mybatis-Plus只做增强不做改变,即保留了Mybatis的特性,又新增了许多特性,大大的提高了开发效率。
六、Mybatis-Plus映射规则
1、表名映射
1.1、@TableName()
该注解可用于指定表名与实体类的映射,为什么需要指定呢,因为在一个比较大的项目的中可能有着一些相同的数据表,那么我们就需要对这些表做一些区分,比如加一些前缀,例如,数据表为mybatisplus_user,这样我们就可以看出来这个user表是用于mybatisplus_情景下的。
但是在我们改变表名的时候,而我们创建的User实体类会无法找到对应的表映射,从而报错。
那么这个时候该怎么解决呢?我们可以在这个实体类的上面添加一个注解,并指定数据库表名,该注解就是@TableName()。
此时,我们在运行一次就可以通过测试了,并且可以看到这个查询的数据表和我们更改的数据表一致。
1.2、全局配置
上面哪种方法,在有大量的表名的时候并且前缀都一样的情况下,每次都向实体类中添加@TableName()注解会很麻烦,那么我们可以在全局配置中配置数据库表名的前缀,就不需要在存在大量相同前缀表名的情况下逐个添加了。
mybatis-plus:
global-config:
db-config:
# 配置数据库表名的默认前缀
table-prefix: mybatisplus_
在此运行测试,看看是否能通过测试并且查询到数据。
测试通过,并且数据成功查询出来。
2、字段名映射
2.1、@TableField()
该注解可用于指定字段名,因为我们实际开发当中可能存在表名的字段与实体类中的属性不匹配,那么在不匹配的情况下,是肯定会报错的,那么我们就可以通过该注解来指定实体类与字段名映射。
在没有指定字段名的时候,查询数据库中的数据,我们可以看到,报了错误,该错误指明说存在着一个未知列也就是一个不存在的列。
那么当我们指定了字段名,是否能成功通过测试呢。
我们可以看到通过了测试,并且可以在控制台看到,在添加了@TableField()后mybatisplus自动的帮我们给这个与表名字段不一致的属性起了别名。
3.字段和列名的驼峰映射
3.1、全局配置
这个字段和列名的驼峰映射就不多做解释了,学过mybatis应该都知道。
若要开启该功能只需要在全局配置中添加以下即可
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
map-underscore-to-camel-case: true 表示支持以下划线到驼峰的映射规则
map-underscore-to-camel-case: false 表示不支持以下划线到驼峰的映射规则
4、主键生成策略
4.1、什么是主键?
主键的作用就是唯一标识,我们可以通过这个唯一标识来找到这个数据。
4.2、mybatis-plus提供的主键生成策略
在mybatis-plus中提供了一个注解,这个注解就是@TableId,该注解提供了各种的主键生成策略,既然mybatis-plus为我们提供了主键生成策略,那么我们就可以使用该注解来对一些新增的数据指定主键生成策略,那么在以后每新增一条,数据就都会按照我们指定的主键策略来生成对应的主键,在实际开发环境中自己去生成主键是比较麻烦的,并且还不安全,那么我们就可以使用MP为我们提供的主键生成策略来进行主键生成,比较方便快捷,可以直接使用。
4.3、AUTO策略
该生成策略表示跟随数据库表的主键的生成策略递增,也就是数据库是什么主键策略那么我就是什么主键策略,前提是必须在数据库表中设置主键自增,这个才生效。
那么接下来就来使用该策略,在使用该策略的时候必须把数据表设为主键递增,不然会报错,以下就是报错信息。
既然使用该策略需要将数据表设置自增,那么我们接下来去设置并且再去测试。
我们可以看到 ,通过了测试,并成功添加一条数据。
4.4、INPUT策略
数据库的自动递增需关闭,不然看不到测试效果。 该策略表示,必须由我们手动的插入id,否则无法添加数据。 该策略主要使用在,对项目是否需要手动设置id的时候使用。
更改AUTO策略,将User实体类的id设置为INPUT主键策略,在使用须由我们手动的插入id,否则无法添加数据,以下是报错信息。
那么给User设置id之后,能否通过测试呢。
成功的通过测试,并且添加了一条数据。
4.5、ASSIGN_ID策略(雪花算法)
在使用该策略的时候可以先了解一下雪花算法。
接下来,我来演示一下该策略的使用。
测试通过,查看数据库是否添加成功。
我们可以看到我的表在未设置主键自增的情况下,并且在执行添加的数据的时候也并未设置id属性,但是在我添加该策略之后,ASSIGN_ID策略自动的帮我添加了一个id,并且这个id不是单独的id,为我的表id设置了一个不直接暴露的id。
4.6、雪花算法
该算法是由一个64位的二进制组成的,最终是一个Long类型的数值。 主要分为四部分存储(每一部分都有含义)
1. 1位的符号位,固定值为0 (在二进制中为0就是正数,为1就是负数); 2. 41位的时间戳 (根据创建时间生产的二进制码); 3. 10位的机器码,包含5位机器id和5位服务id (防止同一时间创建多个相同的时间戳 从而根据同一时间该主键是由哪个机器生成的主键来生成机器码); 4. 12位序列号 (防止同一时间同一台机器在一毫秒的时候连续生产几个相同的机器码);
使用雪花算法可以实现有序、唯一、且不直接暴露排序的数字。
4.7、NONE策略
该策略表示不指定主键生成策略,当我们实体类没有指定策略或者策略为NONE的时候,那么该实体类跟随的策略都是全局策略。 而全局策略(也就是全局配置,在全局配置中使用了什么配置,那么就是什么策略,默认是ASSIGN_ID)在全局配置中 id-type是用于配置主键生成策略的。
我来演示一下用法,并且测试,看结果。
测试通过,并且生成了一个通过雪花算法得来的id,为什么会这样呢,因为该策略表示不指定主键生成策略,不指定主键策略那么就会使用默认的主键生成策略,而默认的全局主键策略是ASSIGN_ID策略。
再次测试,将主键策略注释,看看是否是这样。
测试通过,并且可以看到在未设置策略,还是生成了一个通过雪花算法得来的id。
4.8、ASSIGN_UUID策略
UUID(Universally Unique Identifier)全局唯一标识符,定义一个字符串主键,采用32位数字组成,编码采用16进制,定义了在时间和空间都完全唯一的系统信息。 UUID编码规则:
1. 1-8 位采用系统时间,在系统时间上精确到毫秒级保证时间上的唯一性; 2. 9-16 位采用底展的 IP 地址,在服务器集群中的唯一性; 3. 17-24位采用当前对象的HashCode值,在一个内部对象上的唯一性; 4. 25-32 位采用调用方法的一个随机数,在一个对象内的毫秒级的唯一性;
通过以上4种策略可以保证id唯一性。在系统中需要用到随机数的地方都可以考虑采用UUID算法。
在使用UUID的时候,需要把数据库中的id类型改为varchar类型,并且长度还必须是32位,不然会报错,因为生成的是字符串。 以上算法不需要我们去研究,只需要知道这些算法能让我们的主键生成不同的数值。
我来演示该策略的用法,在测试该策略之前需要把数据表中的id设置为varchar类型,至于为什么要设置为字符串类型,因为UUID生成的是一个字符串。
测试通过,并且生成了一个通过UUID算法得来的字符串id,这种策略需要根据需求来添加该主键,因为该策略生成的id为varchar类型。
七、Mybatis-Plus条件查询
我大致的演示一些用法,而我会通过以下2个类进行条件查询,基本上大部分的查询都是以LambdaQueryWrapper类为基本,作为查询对象,因为该类支持Lambda表达式,这些方法都比较简单,如果不理解也可以测试看运行,我就不一一讲解了,我一次性测试完。
LambdaQueryWrapper条件对象
// 支持Lambda表达式的方式查询
LambdaQueryWrapper<T> lambdaQueryWrapper = new LambdaQueryWrapper<T>();
LambdaQueryWrapper条件对象的底层
QueryWrapper条件对象
// 支持字符串的形式查询
QueryWrapper<T> wrapper = new QueryWrapper<T>();
QueryWrapper条件对象的底层
在学习条件查询之前,我们可以看看BaseMapper接口的底层代码,很多方法的第二个参数都是一个Wrapper<T> queryWrapper对象,而这个对象就是我们的条件对象,我们可以将所需的条件找出来,然后在添加进来,就可以进行条件查询了。
上面那些方法中带有Wrapper<T> queryWrapper的都是表示可进行条件查询,这些查询条件可以用以上2个类进行条件编写。
代码如下 :
package com.demo.mybatispulso2;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.demo.mybatispulso2.domain.User;
import com.demo.mybatispulso2.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.*;
import java.util.function.Consumer;
/**
* @BelongsProject: mybatisplusdemo
* @BelongsPackage: com.demo.mybatispulso2
* @Author: 云边小屋(My.Tears)
* @CreateTime: 2023-05-31 10:26
* @Description: TODO
* @Version: 1.0
*/
@SpringBootTest
public class QueryWarMapper {
private UserMapper userMapper;
@Autowired
public QueryWarMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
/**
* 方式1 :使用QueryWrapper查询
* 等值查询,顾名思义就是判断需要查询的值是否存在,比如我的字段名为name
* 而我需要查询一个名为张三的姓名
* 那么就需要判断这个字段这个值是否存在,存在就返回结果,不存在就不返回。
*/
@Test
void eq1() {
// 创建条件查询对象
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 设置查询条件 指定查询的字段值和匹配的值 查询相同的值
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (age = ?)
wrapper.eq("age", "18");
// 进行条件查询
User user = userMapper.selectOne(wrapper);
System.out.println(user);
}
/**
* 方式2 :使用LambdaQueryWrapper查询
*/
@Test
void eq2() {
LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (age = ?)
userLambdaQueryWrapper.eq(User::getAge, 18);
User user = userMapper.selectOne(userLambdaQueryWrapper);
System.out.println(user);
}
/**
* 如果查询的数据条件为空,那么就返回所有的数据
* 不为空就返回该结果
*/
@Test
void isnull() {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 定义需要查询的条件
String name = null;
// 判断查询的数据是否为空
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user
queryWrapper.eq(name != null, User::getName, name);
// 不为空返回该数据
if (name != null) {
User user = userMapper.selectOne(queryWrapper);
System.out.println(user);
} else {
// 为空返回所有数据 这里是返回全部数据 因为我设置的条件为null
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
}
/**
* 多条件查询,例如我下面添加了2个eq,一个eq查询name,另一个查询age
* 那么在查询的时候,会以这俩个字段为查询条件,相当于and查询
*/
@Test
void allEq1() {
LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (name = ? AND age = ?)
userLambdaQueryWrapper.eq(User::getName, "Jone");
userLambdaQueryWrapper.eq(User::getAge, "18");
User user = userMapper.selectOne(userLambdaQueryWrapper);
System.out.println(user);
}
/**
* 以Map形式的多条件查询
*/
@Test
void allEq2() {
// 创建Map对象
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("name", "Tom");
hashMap.put("age", null);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
/**
* false 当查询条件为空的时候 不把为空的子弹进行查询
*
* true 当查询条件为空 把为空的查条件作为查询条件
*/
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (name = ?)
queryWrapper.allEq(hashMap, false);
User user = userMapper.selectOne(queryWrapper);
System.out.println(user);
}
/**
* 查询条件以外的值,也就是除这个条件以外的所有值
*/
@Test
void ne() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 查询不等于name字段 不包含Tom的所有值
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (name <> ?)
lambdaQueryWrapper.ne(User::getName, "Tom");
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 范围查询
* 大于查询条件
*/
@Test
void gt() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 查询年龄大于18的数据
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (age > ?)
lambdaQueryWrapper.gt(User::getAge, 18);
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 范围查询
* 大于等于查询条件
*/
@Test
void ge() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 查询年龄大于等于18的数据
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (age >= ?)
lambdaQueryWrapper.ge(User::getAge, 18);
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 范围查询
* 小于查询条件
*/
@Test
void lt() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 查询年龄小于20的数据
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (age < ?)
lambdaQueryWrapper.lt(User::getAge, 20);
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 范围查询
* 小于等于查询条件
*/
@Test
void le() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 查询年龄小于等于20的数据
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (age <= ?)
lambdaQueryWrapper.le(User::getAge, 20);
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 范围查询
* between(区间)查询
*/
@Test
void between() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 查询18到20之间的数据
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (age BETWEEN ? AND ?)
lambdaQueryWrapper.between(User::getAge, 18, 20);
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 范围查询
* notBetween 查询不在该区间的数据
*/
@Test
void notBetween() {
LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
// 查询不在18与20之间的数据
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (age NOT BETWEEN ? AND ?)
userLambdaQueryWrapper.notBetween(User::getAge, 18, 20);
List<User> users = userMapper.selectList(userLambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 模糊查询
* like
*/
@Test
void like() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 查询姓名只要包含a字符串的数据 全部返回
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (name LIKE ?)
lambdaQueryWrapper.like(User::getName, "a");
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* notLike
*/
@Test
void notLike() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 查询不包含a的数据
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (name NOT LIKE ?)
lambdaQueryWrapper.notLike(User::getName, "a");
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* likeLeft
* 左模糊查询
*/
@Test
void likeLeft() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 底层拼接的语句是 %J 那么这样就可以看出来 模糊查询只包含了左方 没有包含右方
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (name LIKE ?)
lambdaQueryWrapper.likeLeft(User::getName, "J");
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* likeRight
* 右模糊查询
*/
@Test
void likeEight() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 底层拼接的语句是 J% 那么这样就可以看出来 模糊查询只包含了右方 没有包含左方
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (name LIKE ?)
lambdaQueryWrapper.likeRight(User::getName, "J");
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 判断该字段是否存在数据 不为空(存在)就不查询数据
*/
@Test
void isNull2() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 不为空(存在)就不查询数据 为空就查询数据 不返回要查询的字段
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (name IS NULL)
lambdaQueryWrapper.isNull(User::getName);
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 判断该字段是否存在数据 不为空(存在)就查询数据
*/
@Test
void isNotNull() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 数据为空就不查询 数据不为空(存在)就查询 注意是要查询的字段
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (name IS NOT NULL)
lambdaQueryWrapper.isNotNull(User::getName);
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 包含查询 只要包含某一条就就返回
*/
@Test
void in() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
ArrayList<Integer> arrayList = new ArrayList<>();
// 查询包含这三个年龄数的数据 三条里面有一条都返回
Collections.addAll(arrayList, 18, 28, 22);
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (age IN (?,?,?))
lambdaQueryWrapper.in(User::getAge, arrayList);
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 不包含查询 只要包含就不查询
*/
@Test
void notIn() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
ArrayList<Integer> arrayList = new ArrayList<>();
// 查询不包含这三个年龄数的数据 都不返回
Collections.addAll(arrayList, 18, 28, 22);
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (age NOT IN (?,?,?))
lambdaQueryWrapper.notIn(User::getAge, arrayList);
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 自定义带in关键字的查询
*/
@Test
void inSql() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
/**
* 自定义sql查询条件 只要包含这三个数值的数据 都查询出来
* 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (age IN (18,28,22))
*/
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (age IN (18,28,22))
lambdaQueryWrapper.inSql(User::getAge, "18,28,22");
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* inSql的第二个用法 通过sql语句查询子查询的一个结果 作为一个值的匹配结果
*/
@Test
void inSql2() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
int age = 20;
// 通过inSql 自定义sql语句查询大于20年龄的数据
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (age IN (select age from mybatisplus_user where age > 20))
lambdaQueryWrapper.inSql(User::getAge, "select age from mybatisplus_user where age > " + age);
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 不包含查询
*/
@Test
void notInSql() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 自定义in sql查询条件 不包含这三个数值的数据 都查询出来
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (age NOT IN (18,28,22))
lambdaQueryWrapper.notInSql(User::getAge, "18,28,22");
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* notInSql与in相反
*/
@Test
void notInSql2() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
int age = 20;
// 通过notInSql 自定义sql语句查询大于20年龄的数据 都不查询 只查询小于20的
// SELECT id,name,age,email FROM mybatisplus_user WHERE (age NOT IN (select age from mybatisplus_user where age > 20))
lambdaQueryWrapper.notInSql(User::getAge, "select age from mybatisplus_user where age > " + age);
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 分组查询
* groupBy
*/
@Test
void groupBy() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 需要分组字段
queryWrapper.groupBy("age");
// 分组后的查询字段
queryWrapper.select("age,count(*) as filed_count");
// 封装成一个map对象 通过键值对的形式 返回查询出来
// 底层拼接 --> SELECT age,count(*) as filed_count FROM mybatisplus_user GROUP BY age
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
}
/**
* 聚合查询 将分好组的字段整合
* having
*/
@Test
void having() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 分组字段
queryWrapper.groupBy("age");
// 需要查询的字段
queryWrapper.select("age,count(*) as filed_count");
// 聚合条件的筛选 总值大于等于2的聚合起来
queryWrapper.having("filed_count >=2");
// 封装成键值对的形式
// 底层拼接 --> SELECT age,count(*) as filed_count FROM mybatisplus_user GROUP BY age HAVING filed_count >=2
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
}
/**
* 升序排序
* orderByAsc
*/
@Test
void orderByAsc() {
LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
// 升序排序 排序的条件可以选择多个 假如第一个条件 存在相同的情况 那么就按照下一个条件进行排序 根据id排序 是一定可以排序出来的 因为id是唯一的
userLambdaQueryWrapper.orderByAsc(User::getAge, User::getId);
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user ORDER BY age ASC,id ASC
List<User> users = userMapper.selectList(userLambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 降序排序
* orderByDesc
*/
@Test
void orderByDesc() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 降序排序 根据用户的年龄进行降序排序 假如年龄存在相同的情况 那么就根据id降序排序
lambdaQueryWrapper.orderByDesc(User::getAge, User::getId);
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user ORDER BY age DESC,id DESC
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 自定义orderBy
*/
@Test
void OrderBy() {
LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
// 设置排序字段和排序的方式 参数1:如果排序字段的值为空时候 是否还要作为排序字段参数排序 参数2:是否是升序还是降序 参数3:排序字段
/**
* true 不管是否为空 都作为查询字段
* false 为空就不作为查询字段
*/
userLambdaQueryWrapper.orderBy(true, true, User::getAge);
// 如果上一条件出现相同的数据(升序) 那么就按照id降序排序
userLambdaQueryWrapper.orderBy(true, false, User::getId);
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user ORDER BY age ASC,id DESC
List<User> users = userMapper.selectList(userLambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 内嵌逻辑查询
* func
*/
@Test
void func() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 根据不同的情况选择不同的查询条件 下面的代码可以替代为Lambda表达式
lambdaQueryWrapper.func(new Consumer<LambdaQueryWrapper<User>>() {
@Override
public void accept(LambdaQueryWrapper<User> userLambdaQueryWrapper) {
// 若条件为true 就查询id等于1的数据 为false 就查询id不等于1的数据
if (true) {
userLambdaQueryWrapper.eq(User::getId, "1");
} else {
userLambdaQueryWrapper.ne(User::getId, "1");
}
}
});
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (id = ?)
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 逻辑查询
* and
*/
@Test
void and(){
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 年龄大于20 并且小于30的年龄数据 条件查询后面继续加查询方法 就可以成为and的形式
lambdaQueryWrapper.gt(User::getAge,20).lt(User::getAge,30);
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (age > ? AND age < ?)
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* and嵌套操作
*
*/
@Test
void ands(){
LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
// and的逻辑嵌套
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (name = ? AND (age > ? OR age < ?))
userLambdaQueryWrapper.eq(User::getName,"Jone").and(i-> i.gt(User::getAge,26).or().lt(User::getAge,22));
List<User> users = userMapper.selectList(userLambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* or查询
*/
@Test
void or(){
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// or查询 就查询数据 年龄小于18 或者 大于30 的年龄
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (age < ? OR age > ?)
lambdaQueryWrapper.lt(User::getAge,20).or().gt(User::getAge,30);
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* or嵌套
*/
@Test
void ors(){
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 查询姓名为Jone的数据 然后在查询年龄大于18 or 小于30的你年龄数据
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (name = ? OR (age > ? AND age < ?))
lambdaQueryWrapper.eq(User::getName,"Jone").or(i->i.gt(User::getAge,20).lt(User::getAge,30));
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* nested查询
*/
@Test
void nested(){
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 查询name==为Jone的数据 并且 年龄不等于22的数据 查询出来
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE ((name = ? AND age <> ?))
lambdaQueryWrapper.nested(i-> i.eq(User::getName,"Jone").ne(User::getAge,20));
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* apply 自定义条件查询
*/
@Test
void apply(){
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 自定义查询条件
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (name = 'Jone')
lambdaQueryWrapper.apply("name = 'Jone'");
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* 分页查询
*/
@Test
void last(){
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 从数据1 到 数据3 索引从0开始
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user limit 1,3
lambdaQueryWrapper.last("limit 1,3");
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* exists查询 是否存在
*/
@Test
void exists(){
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 判断exists需要查询的数据是否存在(age=18的数据是否存在 不存在就不查询) 存在返回true 就查询该条件
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (EXISTS (select * from mybatisplus_user where age = 18))
lambdaQueryWrapper.exists("select * from mybatisplus_user where age = 18");
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
@Test
void notExists(){
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 与exists相反 (exists)子查询中有数据就不查询 无数据就查询
// 底层拼接 --> SELECT id,name,age,email FROM mybatisplus_user WHERE (NOT EXISTS (select * from mybatisplus_user where age = 1118))
lambdaQueryWrapper.notExists("select * from mybatisplus_user where age = 1118");
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
/**
* select 查询
*/
@Test
void select(){
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 将需要查询数据添加进行 这里只查询了姓名和年龄
// 底层拼接 --> SELECT name,age FROM mybatisplus_user
lambdaQueryWrapper.select(User::getName,User::getAge);
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}
}
先不急着测试,我们可以先eq的底层。
我们可以看到eq来自这个于Compare接口,并且这个接口有着许多的方法,而且这些方法也是我们要学习的条件查询方法。
那么接下来测试。
由于方法较多,我就不一一运行测试了。
八、Mybatis-PLus分页查询
1、分页查询
在使用MP的分页查询的时候,需要配置一个拦截器,去拦截分页Sql语句后面的limit关键字,来实现分页效果。
package com.demo.mybatispulso2.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @BelongsProject: mybatisplusdemo
* @BelongsPackage: com.demo.mybatispulso2.config
* @Author: 云边小屋(My.Tears)
* @CreateTime: 2023-06-02 20:55
* @Description: TODO
* @Version: 1.0
*/
@Configuration // 声明该类是配置类
public class MybatisPlusConfig {
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加拦截器 指定DbType.MYSQL数据库 ctrl+b查看
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
我们可以看看mybatisplus支持哪些数据库,以下只是一部分。需要查询具体的可以查看DbType类的底层。
MP提供了Mapper和Service的分页方法可用于分页查询,而我值展示基于Mapper的分页方法。
基于Mapper的分页方法
/**
* 第一个参数 page :分页对象
*
* 第二个参数是 queryWrapper :条件对象
*/
<P extends IPage<T>> P selectPage(P page, @Param("ew") Wrapper<T> queryWrapper);
/**
* 第一个参数 page :分页对象
*
* 第二个参数是 queryWrapper :条件对象
*/
<P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param("ew") Wrapper<T> queryWrapper);
page的一些常用参数
属性名 | 类型 | 默认值 | 描述 |
---|---|---|---|
records | List | emptyList | 查询数据列表 |
total | Long | 0 | 查询列表总记录数 |
size | Long | 10 | 每页显示条数,默认 10 |
current | Long | 1 | 当前页 |
pages | Long | 0 | 总页数 |
由于MP内置了分页插件,我们不需要关心具体的操作,只要配置好插件之后,写分页等同于普通 List 查询,那么下面我就来演示一下。
基于Mapper的分页方法
selectPage代码实现 :
private UserMapper userMapper;
@Autowired
public PageTest(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Test
void selectPage() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 指定分页对象 分页对象包含分页信息 IPage
// 参数1 current : 需要当前查询页数
// 参数2 size :需要查询的数据,也就是单页的数据
/**
* 为什么要使用Ipage 我们可以看看 page的底层 底层page类实现了Ipage,也就是page是Ipage的一个实现类
* 那么我们通过Ipage这个对象类接收Page就会方便点。
*/
IPage<User> page = new Page<>(2, 3);
// 执行查询 这个selectPage会可以直接把查询结果封装到里面
userMapper.selectPage(page, lambdaQueryWrapper);
// 获取分页查询的信息
System.out.println("当前页是:" + page.getCurrent());
System.out.println("每页显示条数是:" + page.getSize());
System.out.println("总页数:" + page.getPages());
System.out.println("总条数:" + page.getTotal());
System.out.println("当前页的数据:" + page.getRecords());
}
测试结果:
测试通过,并且数据分页成功。
selectMapsPage代码实现:
@Test
void selectPageMaps1() {
LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
// 创建一个page对象 分页查询 当前页数为1 页面的数据为3条
IPage<Map<String,Object>> page = new Page<>(1, 3);
/**
* 分页查询里面设置条件 那么name == 自定义Mapper4 的都查询出来 并分页
*/
userMapper.selectMapsPage(page, userLambdaQueryWrapper.eq(User::getName, "自定义Mapper4"));
// 获取分页查询的信息
System.out.println("当前页是:" + page.getCurrent());
System.out.println("每页显示条数是:" + page.getSize());
System.out.println("总页数:" + page.getPages());
System.out.println("总条数:" + page.getTotal());
System.out.println("当前页的数据:" + page.getRecords());
page.getRecords().forEach(System.out::println);
}
测试结果:
测试通过,并且把我们所需的条件全部查询出来,并分页成功。