代码及文档地址官方地址: MyBatis-Plus代码发布地址 :Github: https://github.com/baomidou/mybatis-plusGitee: https://gitee.com/baomidou/mybatis-plus文档发布地址 : https://baomidou.com/pages/24112f
目录
一、前期准备
1.依赖引入(springboot):
<!-- MyBatisPlus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
注意
引入
MyBatis-Plus
之后请不要再次引入MyBatis
以及MyBatis-Spring
,以避免因版本差异导1.致的问题
2.application.yml配置信息
spring:
datasource:
# 注意MySQL驱动器的版本
driver-class-name: com.mysql.cj.jdbc.Driver
# 数据库账号名
username root
# 密码
password: xxxx
url: jdbc:mysql://localhost:3306/数据库名字?characterEncoding=utf-8
mybatis-plus:
configuration:
# 打印SQL信息
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 指定mapper.xml 位置何处
mapper-locations:
classpath: mapper/**Mapper.xml
3. MySQL数据表中添加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)
);
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');
4. 项目中添加实体类(需要注入依赖lombok)
5.创建UserMapper.xml( resources 目录下)
<?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.example.mybatisplus.mapper.UserMapper">
</mapper>
6.创建UserMapper接口,接口继承MyBatisPlus提供的BaseMapper<T>
7.测试
到这里,前期工作就做好了,我们测试一下是否成功配置好了
到这个test目录下面创建一个selectTest类,代码如下:
// 标记为测试类
@SpringBootTest
public class selectTest {
// 依赖注入
@Resource
private UserMapper userMapper;
// 标记为测试方法
@Test
public void selectAll(){
List<User> list = userMapper.selectList(null);
list.forEach(System.out::println);
}
}
找到左方的启动按钮
如果是一下情况就说明成功配置好MyBatisPlus,可以正常使用了
二、基本CRUD
BaseMapper<T> 接口所提供的方法
1.插入
insert方法
@Test
public void insertTest(){
// 创建user实例
User user = new User();
// 设置姓名
user.setName("张三");
// 设置年龄
user.setAge(18);
// 设置邮箱
user.setEmail("adfaf@aa.com");
// INSERT INTO user ( id, `name`, age, email ) VALUES ( ?, ?, ?, ? )
userMapper.insert(user);
}
INSERT INTO user ( id, `name`, age, email ) VALUES ( ?, ?, ?, ? )
这条语句是insert所对应的SQL语句,这个insert方法是从BaseMapper中继承过来的
在上面代码中,我们没有设置id值,所以在插入数据的时候,MyBatisPlus使用了默认的雪花算法,雪花算法是基于时间戳+机器ID+流水号实现的,这样以保证id尽量不存在相同的
2.删除
(1)deleteById()
通过id删除数据
@Test
public void testDeleteById(){
//通过id删除用户信息
//DELETE FROM user WHERE id=?
int result = userMapper.deleteById( 1628657874697723905L);
System.out.println("受影响行数:"+result);
}
结果:
(2)deleteBatchByIds()
通过id列表批量删除表中数据
@Test
public void testDeleteBatchIds(){
//通过多个id批量删除
//DELETE FROM user WHERE id IN ( ? , ? , ? )
List<Long> idList = Arrays.asList(1L, 2L, 3L);
int result = userMapper.deleteBatchIds(idList);
System.out.println("受影响行数:"+result);
}
结果:
(3)deleteByMap(Map<String, Object> var1)
这个方法是实现条件删除,使用Map来构建条件语句
@Test
public void testDeleteByMap(){
//根据map集合中所设置的条件删除记录
//DELETE FROM user WHERE name = ? AND age = ?
Map<String, Object> map = new HashMap<>();
map.put("age", 23);
map.put("name", "李四");
int result = userMapper.deleteByMap(map);
System.out.println("受影响行数:"+result);
}
从上面代码中,可以看到,map中所设置的就where后面的条件,map的key就是表中的字段,value就是筛选值
结果:
3.修改
(1)updateById()
根据id值来更新数据,这个方法的参数是T, 将user传入方法中,updateById 会根据对象中的id值为条件,其他值为更新内容来进行更新数据
@Test
public void testUpdateById(){
User user = new User(4L, "admin", 22, null);
//UPDATE user SET name=?, age=? WHERE id=?
int result = userMapper.updateById(user);
System.out.println("受影响行数:"+result);
}
结果:
4.查询
(1)selectById()
根据Id查询数据
@Test
public void testSelectById(){
//根据id查询用户信息
//SELECT id,name,age,email FROM user WHERE id=?
User user = userMapper.selectById(4L);
System.out.println(user);
}
(2)selectBatchIds()
根据Id列表查询数据
@Test
public void testSelectBatchIds(){
//根据多个id查询多个用户信息
//SELECT id,name,age,email FROM user WHERE id IN ( ? , ? )
List<Long> idList = Arrays.asList(4L, 5L);
List<User> list = userMapper.selectBatchIds(idList);
list.forEach(System.out::println);
}
(3)selectByMap(Map<String, Object>)
根据设置条件来进行查询数据
@Test
public void testSelectByMap(){
//通过map条件查询用户信息
//SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
Map<String, Object> map = new HashMap<>();
map.put("age", 22);
map.put("name", "admin");
List<User> list = userMapper.selectByMap(map);
list.forEach(System.out::println);
}
(4)selectList()
直接查询所有数据
@Test
public void testSelectList(){
//查询所有用户信息
//SELECT id,name,age,email FROM user
List<User> list = userMapper.selectList(null);
list.forEach(System.out::println);
}
在这里我们可以注意到这个方法传进去的参数为null,实际上参数类型为: Wrapper,这个参数的意义是传入一个条件构造器(这个条件构造器,我们在后面会介绍),在这里,由于我们传进去的是一个null,默认为没有条件,所以就直接查询所有了
5.通用Service
- 通用 Service CRUD 封装IService接口,进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆,
- 泛型 T 为任意实体对象
- 建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类
- 官网地址:https://baomidou.com/pages/49cc81/#service-crud-%E6%8E%A5%E5%8F%A3
/**
* UserService继承IService模板提供的基础功能
*/
public interface UserService extends IService<User> {
}
/**
* ServiceImpl实现了IService,提供了IService中基础功能的实现
* 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements
UserService {
}
测试查询记录数
@Autowired
private UserService userService;
@Test
public void testGetCount(){
long count = userService.count();
System.out.println("总记录数:" + count);
}
测试批量插入
@Test
public void testSaveBatch(){
// SQL长度有限制,海量数据插入单条SQL无法实行,
// 因此MP将批量插入放在了通用Service中实现,而不是通用Mapper
ArrayList<User> users = new ArrayList<>();
for (int i = 0; i < 5; i++) {
User user = new User();
user.setName("ybc" + i);
user.setAge(20 + i);
users.add(user);
}
//SQL:INSERT INTO t_user ( username, age ) VALUES ( ?, ? )
userService.saveBatch(users);
}
三、常用注解
1.@TableId
我们可以从TableId源码中可以看到,我们可以设置两个值,一个是value,一个 type,
value | 设置字段名 |
type | 设置主键模式 |
(1)value
回顾到我们之前所创建的User实体中的id属性,在我们MySQL表中,user表的第一列也是id,假如我们把user表中的id修改为uid,再执行插入代码时,看看会出现什么结果
不出意外,发现我们SQL语句使用的是id字段,由于user表中是uid,所以就无法执行语句,在开发中,也会出现设置的实体类属性和表字段不一致的情况,所有为了解决这种情况,MyBatisPlus提供了@TableId注解
我们可以使用value属性,将User实体类的id属性改名为user表中对应的uid
public class User {
@TableId(value="uid")
private Long id;
private String name;
private Integer age;
private String email;
}
此时我们再次调用方法
这时就通过了修改,而mybatisplus所生成是SQL语句也发生了变化
(2)type
在前面的插入时,我们可以注意到表中的主键生成是利用的雪花算法,如果不想使用使用雪花算法,就可以使用type属性中的不同主键模式
如果我们想使用自增,那么就需要选择IdType.AUTO来实现自增效果,于此同时,user表也要将主键uid修改为自增模式,然后执行insert方法
查看数据库数据:
这个时候id值也不在是雪花算法计算出来的了。
type的其他模式,感兴趣可以去官网查看,这里不做过多赘述
2.@TableField
同样的,既然有对主键进行备注数据库中字段名的修饰,对于一般字段来说,可以使用TableField来进行备注数据库中字段名
@Data
public class User {
@TableId(type = IdType.AUTO)
private Long id;
@TableField("username")
private String name;
private Integer age;
private String email;
}
结果:
SQL语句使用的是TabelField所备注的字段username
3.@TableLogic
这个注释标注的是逻辑删除
所以我们需要对User实体和user表进行添加字段,
user表:
User实体:
@Data
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
@TableLogic
private Boolean is_delete;
}
执行的删除操作
这时可以发现,删除操作生成的是update语句,而不再是delete。使用update语句实现逻辑删除。
四、条件构造器和常用接口
1、wapper介绍
- Wrapper : 条件构造抽象类,最顶端父类
- AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
- QueryWrapper : 查询条件封装
- UpdateWrapper : Update 条件封装
- AbstractLambdaWrapper : 使用Lambda 语法
- LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
- LambdaUpdateWrapper : Lambda 更新封装Wrapper
2、QueryWrapper
QueryWrapper的每个方法返回的值都是QueryWrapper对象,所有每次后面直接‘.’即可
(1)组转查询条件
@Test
public void test01(){
//查询用户名包含a,年龄在20到30之间,并且邮箱不为null的用户信息
// SELECT id,username AS name,age,email,is_deleted FROM user WHERE
// is_deleted=0 AND (username LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("username", "a")
.between("age", 20, 30)
.isNotNull("email");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
(2)组装排序条件
@Test
public void test02(){
//按年龄降序查询用户,如果年龄相同则按id升序排列
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE
//is_deleted=0 ORDER BY age DESC,id ASC
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.orderByDesc("age")
.orderByAsc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
(3)组装删除条件
@Test
public void test03(){
//删除email为空的用户
//DELETE FROM user WHERE (email IS NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
//条件构造器也可以构建删除语句的条件
int result = userMapper.delete(queryWrapper);
System.out.println("受影响的行数:" + result);
}
(4)条件优先级
@Test
public void test04() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//将(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息修改
//UPDATE user SET age=?, email=? WHERE (username LIKE ? AND age > ? OR email IS NULL)
queryWrapper
.like("username", "a")
// gt表示大于
.gt("age", 20)
.or()
.isNull("email");
User user = new User();
user.setAge(18);
user.setEmail("user@atguigu.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影响的行数:" + result);
}
(5)组装select子句
@Test
public void test05() {
//查询用户信息的username和age字段
//SELECT username,age FROM t_user
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("username", "age");
//selectMaps()返回Map集合列表,通常配合select()使用,避免User对象中没有被查询到的列值
//为null
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
}
(6)实现子查询
@Test
public void test06() {
// 查询id小于等于3的用户信息
// SELECT id,username AS name,age,email,is_deleted FROM user WHERE (id IN (select id from t_user where id <= 3))
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id", "select id from t_user where id <= 3");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
3.UpdateWrapper
@Test
public void test07() {
//将(年龄大于20或邮箱为null)并且用户名中包含有a的用户信息修改
//组装set子句以及修改条件
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
//lambda表达式内的逻辑优先运算
updateWrapper
.set("age", 18)
.set("email", "user@atguigu.com")
.like("username", "a")
.and(i -> i.gt("age", 20).or().isNull("email"));
//这里必须要创建User对象,否则无法应用自动填充。如果没有自动填充,可以设置为null
//UPDATE user SET username=?, age=?,email=? WHERE (username LIKE ? AND
//(age > ? OR email IS NULL))
//User user = new User();
//user.setName("张三");
//int result = userMapper.update(user, updateWrapper);
//UPDATE t_user SET age=?,email=? WHERE (username LIKE ? AND (age > ? OR
//email IS NULL))
int result = userMapper.update(null, updateWrapper);
System.out.println(result);
}
4、condition
在真正开发的过程中,组装条件是常见的功能,而这些条件数据来源于用户输入,是可选的,因 此我们在组装这些条件时,必须先判断用户是否选择了这些条件,若选择则需要组装该条件,若没有选择则一定不能组装,以免影响SQL 执行的结果
思路一:
@Test
public void test08() {
//定义查询条件,有可能为null(用户未输入或未选择)
String username = null;
Integer ageBegin = 10;
Integer ageEnd = 24;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//StringUtils.isNotBlank()判断某字符串是否不为空且长度不为0且不由空白符(whitespace)
//构成
if(StringUtils.isNotBlank(username)){
queryWrapper.like("username","a");
}
if(ageBegin != null){
queryWrapper.ge("age", ageBegin);
}
if(ageEnd != null){
queryWrapper.le("age", ageEnd);
}
//SELECT id,username AS name,age,email,is_deleted FROM t_user
//WHERE (age >=? AND age <= ?)
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
思路二:
@Test
public void test08UseCondition() {
//定义查询条件,有可能为null(用户未输入或未选择)
String username = null;
Integer ageBegin = 10;
Integer ageEnd = 24;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//StringUtils.isNotBlank()判断某字符串是否不为空且长度不为0且不由空白符(whitespace)
//构成
queryWrapper
.like(StringUtils.isNotBlank(username), "username", "a")
.ge(ageBegin != null, "age", ageBegin)
.le(ageEnd != null, "age", ageEnd);
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (age >=
//? AND age <= ?)
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
5、LambdaQueryWrapper
@Test
public void test09() {
//定义查询条件,有可能为null(用户未输入)
String username = "a";
Integer ageBegin = 10;
Integer ageEnd = 24;
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
//避免使用字符串表示字段,防止运行时错误
queryWrapper
.like(StringUtils.isNotBlank(username), User::getName, username)
.ge(ageBegin != null, User::getAge, ageBegin)
.le(ageEnd != null, User::getAge, ageEnd);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}
6、LambdaUpdateWrapper
@Test
public void test10() {
//组装set子句
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper
.set(User::getAge, 18)
.set(User::getEmail, "user@atguigu.com")
.like(User::getName, "a")
.and(i -> i.lt(User::getAge, 24).or().isNull(User::getEmail));
//lambda表达式内的逻辑优先运算
User user = new User();
int result = userMapper.update(user, updateWrapper);
System.out.println("受影响的行数:" + result);
}
五、分页功能
1.分页插件
(1)配置
在项目目录下创建config文件夹,在config文件夹中创建MybatisPlusConfig类,配置拦截器
@Configuration
public class MybatisPlusConfig {
// 分页插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new
PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
(2)测试
@Test
public void testPage(){
//设置分页参数
Page<User> page = new Page<>(1, 5);
userMapper.selectPage(page, null);
//获取分页数据
List<User> list = page.getRecords();
list.forEach(System.out::println);
System.out.println("当前页:"+page.getCurrent());
System.out.println("每页显示的条数:"+page.getSize());
System.out.println("总记录数:"+page.getTotal());
System.out.println("总页数:"+page.getPages());
System.out.println("是否有上一页:"+page.hasPrevious());
System.out.println("是否有下一页:"+page.hasNext());
}
2.xml自定义分页
(1)UserMapper中定义接口方法
/**
* 根据年龄查询用户列表,分页显示
* @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位
* @param age 年龄
* @return 返回分页对象IPage
*/
IPage<User> selectPageVo(@Param("page") Page<User> page, @Param("age")Integer age);
(2)UserMapper.xml中编写SQL
<!--SQL片段,记录基础字段-->
<sql id="BaseColumns">id,name,age,email</sql>
<!--IPage<User> selectPageVo(Page<User> page, Integer age);-->
<select id="selectPageVo" resultType="User">
SELECT <include refid="BaseColumns"></include> FROM t_user WHERE age > #
{age}
</select>
(3)测试
@Test
public void testSelectPageVo(){
//设置分页参数
Page<User> page = new Page<>(1, 5);
userMapper.selectPageVo(page, 20);
//获取分页数据
List<User> list = page.getRecords();
list.forEach(System.out::println);
System.out.println("当前页:"+page.getCurrent());
System.out.println("每页显示的条数:"+page.getSize());
System.out.println("总记录数:"+page.getTotal());
System.out.println("总页数:"+page.getPages());
System.out.println("是否有上一页:"+page.hasPrevious());
System.out.println("是否有下一页:"+page.hasNext());
}
======THE END======
感谢阅读,本文章仅为个人学习笔记使用