目录
MyBatis-Plus(简称 MP)是⼀个MyBatis的增强⼯具,在MyBatis的基础上只做增强不做改变,为简化开 发.提⾼效率⽽⽣
⽀持数据库: PostgreSQL, MySQL, MariaDB, Oracle, SQL Server, OceanBase, H2, DB2... (任何能使⽤MyBatis进⾏增删改查,并且⽀持标准SQL的数据库应该都在MyBatis-Plus的⽀持范围内)
1.创建数据库
这里我使用的是mysql数据库,大家可以根据自己的喜好来选择自己喜欢的数据库
-- 创建数据库
DROP DATABASE IF EXISTS mybatis_plus_test;
CREATE DATABASE mybatis_plus_test DEFAULT CHARACTER SET utf8mb4;
-- 使用数据库
USE mybatis_plus_test;
-- 创建表 user_info
DROP TABLE IF EXISTS user_info;
CREATE TABLE `user_info` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(127) NOT NULL,
`password` VARCHAR(127) NOT NULL,
`age` TINYINT(4) NOT NULL,
`gender` TINYINT(4) DEFAULT '0' COMMENT '1: 男, 2: 女, 0: 默认',
`phone` VARCHAR(15) DEFAULT NULL,
`delete_flag` TINYINT(4) DEFAULT 0 COMMENT '0: 正常, 1: 删除',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
-- 添加用户信息
INSERT INTO `user_info` (`username`, `password`, `age`, `gender`, `phone`) VALUES ('admin', 'admin', 18, 1, '18612340001');
INSERT INTO `user_info` (`username`, `password`, `age`, `gender`, `phone`) VALUES ('zhangsan', 'zhangsan', 18, 1, '18612340002');
INSERT INTO `user_info` (`username`, `password`, `age`, `gender`, `phone`) VALUES ('lisi', 'lisi', 18, 1, '18612340003');
INSERT INTO `user_info` (`username`, `password`, `age`, `gender`, `phone`) VALUES ('wangwu', 'wangwu', 18, 1, '18612340004');
运行完上面这段代码,数据库表就变成这样了
2.创建springboot工程
在创建springboot工程的时候就需要把mysql依赖添加进来
添加MyBatis-Plus依赖
这里我们可以选择去官网查看复制,也可以选择复制我下面这段代码
SpringBoot2版本
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.9</version>
</dependency>
SpringBoot3版本
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.9</version>
</dependency>
如果在创建springboot工程的时候没有添加mysql依赖可以加上下面这段代码,反之不用
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
然后把自己需要的代码复制到 pom.xml 里面并放到相应的位置
配置数据库
找到 src 路径下面的 main 下面的 resources 下面的 application.properties
这里可以根据个人习惯把 application.properties 改成 application.yml
application.yml 的写法
# 数据库连接配置
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mybatis_plus_test?characterEncoding=utf8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
application.properties 的写法
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mybatis_plus_test?characterEncoding=utf8&useSSL=false
#连接数据库的⽤⼾名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=123456
常见的几个错误:
- 使用的数据库是否和自己配置的数据库正确,并且启动springboot工程的时候要启动相对应的数据库
- 连接的数据库名是否正确
- 账号密码是否正确
创建实体类和编写接口
创建实体类UserInfo
注意自己创建的mapper包和model包一定要和启动类在demo包下
首先创建一个 mapper 包和一个 model 包,然后在 mapper 下面写一个接口 UserInfoMapper ,在model 下新建一个类 UserInfo
import lombok.Data;
import java.util.Date;
@Data
public class UserInfo {
private Integer id;
private String username;
private String password;
private Integer age;
private Integer gender;
private String phone;
private Integer deleteFlag;
private Date createTime;
private Date updateTime;
}
想要使用@Data注解得先引入 lombok ,前面创建springboot 工程的时候已经一并添加进来了
当然如果不想用 lombok 也可以使用传统的 getter 和 setter ,效果是一样的,只不过看起来比较长
编写Mapper接⼝类
MybatisPlus提供了⼀个基础的 BaseMapper 接⼝,已经实现了单表的CRUD,我们⾃定义的 Mapper只需要继承这个BaseMapper,就⽆需⾃⼰实现单表CRUD了
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserInfoMapper extends BaseMapper<UserInfo> {
}
写成上面这个样子就好了,这里提醒一下,如果没有BaseMapper这个类可能是因为没有提示,这时候就需要自己手动敲出来了,也可能是因为 pom.xml 没有刷新,需要重新刷新一下pom.xml文件
CRUD单元测试
首先创建一个测试类,名字随便起,但是一定要记得在类的上方加上 @SpringBootTest 注解,
注入 UserInfoMapper 类
比如这里有一个select,当我们输入前面几个字母的时候,就会给提示,我们根据提示来选择自己需要的 sql 语句就好了
简单的增删改查大致就是下面这段代码,如果想了解更复杂的请往后看
import com.example.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class UserInfoMapperTest {
@Autowired
private UserInfoMapper userInfoMapper;
//查询
@Test
void testSelect(){
System.out.println(userInfoMapper.selectById(1));
}
//增加
@Test
void testInsert(){
UserInfo userInfo = new UserInfo();
userInfo.setUsername("xixi");
userInfo.setPassword("haha");
userInfo.setAge(18);
userInfoMapper.insert(userInfo);
}
//改
@Test
void testUpdate(){
UserInfo userInfo = new UserInfo();
userInfo.setId(11);
userInfo.setPassword("haha");
userInfo.setAge(18);
userInfoMapper.updateById(userInfo);
}
//删
@Test
void testDelete(){
userInfoMapper.deleteById(1);
}
}
MyBatis-Plus复杂操作
常见注解
在上⾯的程序中,MyBatis是如何知道,我们要操作的是哪张表,表⾥有哪些字段呢?
我们来看下咱们Mapper的代码
@Mapper
public interface UserInfoMapper extends BaseMapper<UserInfo> {
}
UserInfoMapper 在继承 BaseMapper 时,指定了⼀个泛型,这个UserInfo就是与数据库表相对应的实 体类. MyBatis-Plus会根据这个实体类来推断表的信息.
默认情况下:
- 表名:实体类的驼峰表⽰法转换成蛇形表⽰法(下划线分割),作为表名.⽐如UserInfo->user_info
- 字段:根据实体类的属性名转换为蛇形表⽰法作为字段名.⽐如deleteFlag->delete_flag
- 主键:默认为id
那如果实体类和数据库不是按照上述规则定义的呢?MyBatis-Plus也给我们提供了⼀下注解,让我们标识表的信息.
@TableName
当类名自动转换之后不能和数据库名相匹配之后就会报错,比如我将UserInfo 写成了 Userinfo 此时类名自动转换之后就不能与数据库名相匹配了,就会报类似下面这个错误
那如果我们就想使用这个类名呢?此时就需要拿出我们的@TableName注解
使用方法就是在类上面加上@TableName注解,里面写上对应的数据库表名,一定不能写错
@TableField
这个注解和@TableName相似,这个是重新映射字段名与数据库字段
@TableId
如果userid不是主键,那么就需要使用@TableId,里面的id就是指定的数据库的主键名
如果属性名和字段名不⼀致,需要在 @TableId 指明对应的字段名
属性名和字段⼀致的情况下,直接加 @TableId 注解就可以.
type:用于指定主键类型,包括 IdType.AUTO
、IdType.INPUT
、IdType.NONE
等。常用的有
IdType.AUTO
:自增主键,适用于自增长类型的主键。IdType.INPUT
:手动输入主键值,适用于需要手动指定主键值的情况。IdType.NONE
:无主键,适用于没有主键的情况。
条件构造器
QueryWrapper
这是一个可以用来自定义查询语句的,无论是修改,删除,查询都可以使用,相当于where语句部分
例如这个 SQL 语句原本是下面这样的
SELECT id,username,password,age FROM user_info WHERE age = 18 AND username
"%min%"
我们想利用QueryWrapper来完成应该怎么做呢?请看下面这段代码
@Test
void testQueryWrapper(){
QueryWrapper<UserInfo> userInfoQueryWrapper = new QueryWrapper<UserInfo>()
.select("id","username","password","age")
.eq("age",18)
.like("username", "min");
List<UserInfo> userInfos = userInfoMapper.selectList(userInfoQueryWrapper);
userInfos.forEach(System.out::println);
}
解释:
- UserInfo就是你定义的一个实体类,对应数据库中的某个表
.select("id", "username", "password", "age")就是
指定查询结果中包含的字段.eq("age", 18)就是
设置一个等于条件,查询age
等于 18 的记录(详情看下面).like("username", "min")
就是设置一个模糊匹配条件,查询username
包含 "min" 的记录- List<UserInfo> userInfos = userInfoMapper.selectList(userInfoQueryWrapper);这段代码的意思是调用userInfoMapper类的selectList方法并传入userInfoQueryWrapper构造器,返回值为List<UserInfo>
UpdateWrapper
对于更新,我们也可以直接使⽤UpdateWrapper,在不创建实体对象的情况下,直接设置更新字段和条 件
UPDATE user_info SET delete_flag=0, age=5 WHERE id IN (1,2,3)
@Test
void testUpdateByUpdateWrapper(){
UpdateWrapper<UserInfo> updateWrapper = new UpdateWrapper<UserInfo>()
.set("delete_flag",0)
.set("age", 5)
.in("id", List.of(1,2,3));
userInfoMapper.update(updateWrapper);
}
.set 是因为这个是更新语句,将“delete_flag” 改成 “0”,判断条件就是 id 等于 “1,2,3” 的
LambdaQueryWrapper
QueryWrapper和UpdateWrapper存在⼀个问题,就是需要写死字段名,如果字段名发⽣变更,可能会 因为测试不到位酿成事故.
@Test
void testLambdaQueryWrapper(){
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<UserInfo>();
queryWrapper.lambda()
.select(UserInfo::getUsername, UserInfo::getPassword,UserInfo::getAge)
.eq(UserInfo::getUserId, 1);
userInfoMapper.selectList(queryWrapper).forEach(System.out::println);
}
其实和QueryWrapper差不多,只不过是要多写一个 .lambda()
LambdaUpdateWrapper
@Test
void testLambdUpdateByUpdateWrapper(){
UpdateWrapper<UserInfo> updateWrapper = new UpdateWrapper<UserInfo>();
updateWrapper.lambda()
.set(UserInfo::getDeleteFlag, 0)
.set(UserInfo::getAge, 5)
.in(UserInfo::getUserId, List.of(1,2,3));
userInfoMapper.update(updateWrapper);
}
代码意思可以参考上面的
条件判断式
-
等于 (
eq
)- 方法:
.eq("column", value)
- 对应 SQL:
column = value
- 方法:
-
不等于 (
ne
)- 方法:
.ne("column", value)
- 对应 SQL:
column <> value
- 方法:
-
大于 (
gt
)- 方法:
.gt("column", value)
- 对应 SQL:
column > value
- 方法:
-
大于等于 (
ge
)- 方法:
.ge("column", value)
- 对应 SQL:
column >= value
- 方法:
-
小于 (
lt
)- 方法:
.lt("column", value)
- 对应 SQL:
column < value
- 方法:
-
小于等于 (
le
)- 方法:
.le("column", value)
- 对应 SQL:
column <= value
- 方法:
-
模糊匹配 (
like
)- 方法:
.like("column", value)
- 对应 SQL:
column LIKE '%value%'
- 方法:
-
不模糊匹配 (
notLike
)- 方法:
.notLike("column", value)
- 对应 SQL:
column NOT LIKE '%value%'
- 方法:
-
左模糊匹配 (
likeLeft
)- 方法:
.likeLeft("column", value)
- 对应 SQL:
column LIKE 'value%'
- 方法:
-
右模糊匹配 (
likeRight
)- 方法:
.likeRight("column", value)
- 对应 SQL:
column LIKE '%value'
- 方法:
-
为空 (
isNull
)- 方法:
.isNull("column")
- 对应 SQL:
column IS NULL
- 方法:
-
非空 (
isNotNull
)- 方法:
.isNotNull("column")
- 对应 SQL:
column IS NOT NULL
- 方法:
-
在某个集合中 (
in
)- 方法:
.in("column", values)
- 对应 SQL:
column IN (value1, value2, ...)
- 方法:
-
不在某个集合中 (
notIn
)- 方法:
.notIn("column", values)
- 对应 SQL:
column NOT IN (value1, value2, ...)
- 方法:
-
介于某个范围之间 (
between
)- 方法:
.between("column", value1, value2)
- 对应 SQL:
column BETWEEN value1 AND value2
- 方法:
-
不在某个范围之间 (
notBetween
)- 方法:
.notBetween("column", value1, value2)
- 对应 SQL:
column NOT BETWEEN value1 AND value2
- 方法:
-
排序 (
orderByAsc
,orderByDesc
)- 方法:
.orderByAsc("column1", "column2")
- 对应 SQL:
ORDER BY column1 ASC, column2 ASC
- 方法:
.orderByDesc("column1", "column2")
- 对应 SQL:
ORDER BY column1 DESC, column2 DESC
- 方法:
-
分组 (
groupBy
)- 方法:
.groupBy("column1", "column2")
- 对应 SQL:
GROUP BY column1, column2
- 方法:
-
聚合函数 (
having
)- 方法:
.having("sum(column) > {0}", value)
- 对应 SQL:
HAVING sum(column) > value
- 方法:
-
自定义条件 (
apply
)- 方法:
.apply("column = {0}", value)
- 对应 SQL:
column = value
- 方法:
-
逻辑与 (
and
)- 方法:
.and(i -> i.eq("column1", value1).lt("column2", value2))
- 对应 SQL:
column1 = value1 AND column2 < value2
- 方法:
-
逻辑或 (
or
)- 方法:
.or(i -> i.eq("column1", value1).lt("column2", value2))
- 对应 SQL:
column1 = value1 OR column2 < value2
- 方法:
这些大家作为参考就好,也可以去官网查看
自定义SQL
为了使⽤这⼀功能, mybatis-plus 版本不低于3.0.7
下面来举一个例子
SQL语句:
select id,username,password,age FROM user_info WHERE username = "admin"
Mapper:
@Mapper
public interface UserInfoMapper extends BaseMapper<UserInfo> {
@Select("select id,username,password,age FROM user_info ${ew.customSqlSegment}")
List<UserInfo> queryUserByCustom(@Param(Constants.WRAPPER) Wrapper<UserInfo> wrapper);
}
这里需要注意一点的是引包的时候不要引错了,得引入下面这个包才对,注意一定要是baomidou的,因为相似的代码实在太多了,一旦没引对,发生错误就不好排查
测试代码:
@Test
void testQueryUserByCustom(){
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<UserInfo>()
.eq("username","admin");
userInfoMapper.queryUserByCustom(queryWrapper).forEach(System.out::println);
}
- 参数命名:在⾃定义SQL时,传递Wrapper对象作为参数时,参数名必须为ew,或者使⽤注解@Param(Constants.WRAPPER)明确指定参数为Wrapper对象.
- 使用${ew.customSqlSegment}:在SQL语句中,使⽤ ${ew.customSqlSegment} 来引⽤Wrapper对象⽣成的SQL⽚段.
- 不⽀持基于entity的where语句:⾃定义SQL时,Wrapper对象不会基于实体类⾃动⽣成 where⼦句,你需要⼿动编写完整的SQL语句
需要注意的是MyBatis-Plus 在 MyBatis 的基础上只做增强不做改变,所以也⽀持XML的实现⽅式,实现方式和MyBatis一样,大家之前怎么用Mybatis实现的xml现在就怎么用MyBatis-Plus实现xml就好了