MyBatis-plus笔记 (上)
简介
[MyBatis-Plus](简称 MP)是一个 [MyBatis]的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
mybatis-plus总结:
注意:mybatis-puls仅局限于单表操作。
自动生成单表的CRUD功能
提供丰富的条件拼接方式
全自动ORM类型持久层框架
快速入门
代码举例:
正常创建一个springboot的工程
Application.yaml文件:
# 连接池配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
url: jdbc:mysql:///mybatis-example
username: root
password: abc123LQ
driver-class-name: com.mysql.cj.jdbc.Driver
启动类:
@SpringBootApplication
@MapperScan("com.atguigu.mapper")
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class,args);
System.out.println("Hello world!");
}
}
mapper接口:
/**
* mybatis-plus使用步骤:
* 正常创建实体类,创建mapper接口,注意此时不需要我们再创建mapper.xml文件了
* 让mapper接口继承BaseMapper<实体类>
* BaseMapper 提供了增删改查的方法。
*/
public interface UserMapper extends BaseMapper<User> {
}
实体类:
略
测试类
/**
* springboot测试类
* 之前的测试类需要使用@SpringJUnitConfig(value = 配置类.class----------指定配置类
* 现在只需加一个@SpringBootTest即可:
* 自动帮我们完成ioc的初始化
*/
@SpringBootTest
public class SpringBootMybatisPlusTest {
@Autowired
private UserMapper userMapper;
@Test
public void test(){
List<User> users = userMapper.selectList(null);
System.out.println("users = " + users);
}
}
基于baseMapper的crud增强
通用 CRUD 封装BaseMapper接口, `Mybatis-Plus` 启动时自动解析实体表关系映射转换为 `Mybatis` 内部对象注入容器! 内部包含常见的单表操作!
Insert方法
// 插入一条记录
// T 就是要插入的实体对象
// 默认主键生成策略为雪花算法(后面讲解)
int insert(T entity);
|类型| | 参数名| | 描述| |
|T| | entity| | 实体对象| |
Delete方法
// 根据 entity 条件,删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 删除(根据ID 批量删除)
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 ID 删除
int deleteById(Serializable id);
// 根据 columnMap 条件,删除记录
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
|类型 | |参数名 | |描述| |
|Wrapper<T> | |wrapper | |实体对象封装操作类(可以为 null)| |
|Collection<? extends Serializable> | |idList| | 主键 ID 列表(不能为 null 以及 empty)| |
|Serializable | |id | |主键 ID| |
|Map<String, Object> | |columnMap | |表字段 map 对象| |
Update方法
注意:传入对象的属性为空时,元数据不做修改;所以建议再创建实体类时,将基础类型做为包装类型,
例如int age,如果该对象没有给id赋值的话,那么对象的id为0而不是为空,这样再修改数据时就会将对象的id列修改为0
// 根据 whereWrapper 条件,更新记录
int update(@Param(Constants.ENTITY) T updateEntity,
@Param(Constants.WRAPPER) Wrapper<T> whereWrapper);//第一个装要修改的数据,第二个装修改条件
// 根据 ID 修改 主键属性必须值
int updateById(@Param(Constants.ENTITY) T entity);
|类型| | 参数名| | 描述| |
|Wrapper<T>| | wrapper| | 实体对象封装操作类(可以为 null)| |
|Collection<? extends Serializable> | |idList| | 主键 ID 列表(不能为 null 以及 empty)| |
|Serializable| | id| | 主键 ID| |
|Map<String, Object> | |columnMap | |表字段 map 对象| |
Select方法
// 根据 ID 查询
T selectById(Serializable id);
// 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据ID 批量查询)
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 entity 条件,查询全部记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据 columnMap 条件)
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 entity 条件,查询全部记录(并翻页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
|类型 | |参数名 | |描述| |
|Serializable | |id| | 主键 ID| |
|Wrapper<T> | |queryWrapper| | 实体对象封装操作类(可以为 null)| |
|Collection<? extends Serializable> | |idList | |主键 ID 列表(不能为 null 以及 empty)| |
|Map<String, Object> | |columnMap| | 表字段 map 对象| |
|IPage<T> | |page | |分页查询条件(可以为 RowBounds.DEFAULT)| |
测试代码:
@SpringBootTest
public class MyBatisPlusTest {
@Autowired
private UserMapper userMapper;
@Test
public void test1(){
User user = new User();
user.setAge(88);
user.setName("Jerry2");
user.setEmail("test6@baomidou.com");
int i = userMapper.insert(user);
System.out.println("i = " + i);
}
@Test
public void test2(){
//根据id删除
int i = userMapper.deleteById(6);
System.out.println("i = " + i);
//根据age=88 name=Jerry2删除
HashMap map = new HashMap();
map.put("age",88);
map.put("name","Jerry2");
int i1 = userMapper.deleteByMap(map);
System.out.println("i1 = " + i1);
//userMapper.delete(wrapper)
//wrapper是条件封装对象,内部可以无限的封装条件
}
@Test
public void test3(){
//根据主键修改:updatebyId()传入的对象id必须又值
// 将id=1的age改0为30
User user = new User();
user.setId(1L);
user.setAge(30);
int i = userMapper.updateById(user);
System.out.println("i = " + i);
//update() 传入对象可以没有id值
//将所有人的年龄改为22
User user1 = new User();
user1.setAge(22);
int update = userMapper.update(user1, null);//null代表每=没条件
System.out.println("update = " + update);
}
@Test
public void test4(){
//通过id查询数据
User user = userMapper.selectById(1);
System.out.println("user = " + user);
//根据ids集合查询
List<Long> ids = new ArrayList<>();
ids.add(1L);
ids.add(2L);
List list = userMapper.selectBatchIds(ids);
System.out.println("list = " + list);
}
}
自定义与多表映射
mybatis-plus的默认mapperxml位置
mybatis-plus: # mybatis-plus的配置
# 默认位置 private String[] mapperLocations = new String[]{"classpath*:/mapper/**/*.xml"};
mapper-locations: classpath:/mapper/*.xml
自定义mapper方法:
public interface UserMapper extends BaseMapper<User> {
//正常自定义方法!
//可以使用注解@Select或者mapper.xml实现
List<User> queryAll();
}
基于mapper.xml实现:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace = 接口的全限定符 -->
<mapper namespace="com.atguigu.mapper.UserMapper">
<select id="queryAll" resultType="user" >
select * from user
</select>
</mapper>
基于Service的crud增强
通用 Service CRUD 封装IService接口,进一步封装 CRUD 采用 `get 查询单行` `remove 删除` `list 查询集合` `page 分页` 前缀命名方式区分 `Mapper` 层避免混淆,
IService接口中提供了一般的crud的实现,一半没实现还是抽象方法,强制自定义实现另一半crud方法。
将另一半,没有实现的crud方法再ServiceImpl中实现了
对比Mapper接口CRUD区别:
- service添加了批量方法
- service层的方法自动添加事务
-mapper层重点是对单条数据的操作,而service层则增加了批量操作
步骤:
①实现IService接口
②继承ServiceImlp类 ServiceImpl<实体类对应的Mapper接口, 实体类>
实现代码:
service接口
public interface UserService extends IService<User> {
}
service实现类
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
CRUD方法测试代码:
@SpringBootTest
public class MyBatisPlusTest {
@Autowired
private UserService userService;
@Test
public void test_save(){
List<User> list = new ArrayList<>();
User user = new User();
user.setAge(18);
user.setEmail("JJ");
user.setName("驴蛋蛋");
list.add(user);
User user1 = new User();
user1.setAge(18);
user1.setEmail("JJ");
user1.setName("狗剩子");
list.add(user1);
boolean b = userService.saveBatch(list);
System.out.println("b = " + b);
}
@Test
public void test_saveOrUpdate(){
//如果id有值那么就修改;没有就插入
User user = new User();
user.setAge(18);
user.setEmail("JJ");
user.setName("驴蛋蛋22");
boolean b = userService.saveOrUpdate(user);
System.out.println("b = " + b);
}
@Test
public void test_remove(){
boolean b = userService.removeById(1907418896856911874L);
System.out.println("b = " + b);
}
@Test
public void tes_update(){
User user = new User();
user.setId(1L);
user.setAge(18);
user.setEmail("JJ");
user.setName("驴蛋蛋22");
boolean b = userService.updateById(user);
System.out.println("b = " + b);
}
@Test
public void test_getOrList(){
User byId = userService.getById(1L);//get返回的是单个对象
List<User> list = userService.list(null);//查询全部,返回是的集合
}
}
分页查询的使用
启动类:
@SpringBootApplication
@MapperScan("com.atguigu.mapper")
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class,args);
}
/**
* 将mybatis-plus插件导入到ioc容器步骤:
* ①创建一个方法,在该方法中创建一个装所有插件的对象mybatisPlusInterceptor
* 在方法中通过该对象添加想要的插件
* ②返回该对象,同时将该对象加入到ioc容器中
*/
//将mybatis-plus插件导入到ioc容器
@Bean
public MybatisPlusInterceptor plusInterceptor(){
//创建一个mybatis-plus插件集合,所有的插件都集中在此(分页插件,乐观锁插件...)
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return mybatisPlusInterceptor;
}
}
测试类:
@SpringBootTest
@MapperScan("com.atguigu.mapper")
public class MyBatisPlusTest {
/**
* 使用分页插件的步骤:
* ①带入插件(在启动类中)
* ②使用:
* 创建一个page对象,传入页码数与页容量,
* mapper接口中有一个查询+分页的方法selectPage(page,查询条件)
* 最后分页查询的结果会封装在page中
* 通过page调用方法,可以获得当前页的数据,总页数,页容量...
*/
@Autowired
private UserMapper userMapper;
@Test
public void test_page(){
//创建一个page对象,传入页码数与页容量
Page<User> page = new Page<>(1, 3);
//mapper接口中有一个查询+分页的方法----------selectPage(page,查询条件)
userMapper.selectPage(page,null);
//selectPage(page,查询条件)此方法调用后,会将结果封装到page中
long current = page.getCurrent();//获取当前页码
long size = page.getSize();//获取页容量
List<User> records = page.getRecords();//获取当前页的数据
long total = page.getTotal();//获取总条数
System.out.println(records);
}
}
分页添加到自定义方法
①在mapper接口中正常自定义方法
注意此方法的放回值类型一定是IPage<实体类>,且定包含一个参数 IPage<实体类> page;page对象用于装当前的页码数与页容量,最后返回结果也会封装到page对象。
②最后在mapper.xml文件中编写对应的sql语句
代码举例:
mapper接口:
public interface UserMapper extends BaseMapper<User> {
//定义一个根据年龄参数查询,并且分页的方法 age > xx
/**
* 因为分页查询的结果会封装回page,所以方法的返回值填IPage<实体类>
* 方法的形参要有一个IPage<实体类>的page对象,用来装当前页数与页容量,封装结果
*/
IPage<User> queryByAge(IPage<User> page,@Param("age") Integer age);
}
Mapper.xml文件
<mapper namespace="com.atguigu.mapper.UserMapper">
<!-- 查询结果填集合的泛型 -->
<select id="queryByAge" resultType="user">
<!-- sql语句不能添加分号,后面要拼接分页sql -->
select * from user where age > #{age}
</select>
</mapper>
条件构造器与queryWrapper体验
条件构造器继承结构:
Wrapper : 条件构造抽象类,最顶端父类
- AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
- QueryWrapper : 查询/删除条件封装
- UpdateWrapper : 修改条件封装
- AbstractLambdaWrapper : 使用Lambda 语法
- LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
- LambdaUpdateWrapper : Lambda 更新封装Wrapper
基于QueryWapper组装条件:
测试代码:
@SpringBootTest
@MapperScan("com.atguigu.mapper")
public class MybatisPlusQueryWrapperTest {
@Autowired
private UserMapper userMapper;
/** 包含某个字符条件,范围条件,不为空条件---------------------like(),between(),isNotNull()
* 查询用户名包含a,年龄在20到30之间,并且邮箱不为null的用户信息
* 用queryWrapper实现:
* SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0
* AND (username LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
*/
@Test
public void test_01(){
QueryWrapper<User> queryWrapper=new QueryWrapper<>();
queryWrapper.like("name","a");
queryWrapper.between("age",20,30);
queryWrapper.isNotNull("email");
//链式调用:
// queryWrapper.like("name","a").between("age",20,30).isNotNull("email");
List<User> users = userMapper.selectList(queryWrapper);
System.out.println("users = " + users);
}
/**
* 排序---------------orderByASC()
* 按年龄降序查询用户,如果年龄相同则按id升序排列
* 实现:
* SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE
* is_deleted=0 ORDER BY age DESC,id ASC
*/
@Test
public void test_02(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("age").orderByAsc("id");
List<User> userList = userMapper.selectList(queryWrapper);
System.out.println("userList = " + userList);
}
/**
* or条件连接--------------------or()
* 注意:多个条件之间默认用and拼接,想要使用or拼接,就调用or()
* or()第一个条件是or,其他的仍是and
* 将年龄大于20并且用户名中包含有a或邮箱为null的用户信息修改
* UPDATE t_user SET age=?, email=? WHERE
* username LIKE ? AND age > ? OR email IS NULL)
*/
/**
* 查询指定列(默认查询所有列)-------------select(指定列)
* //查询用户信息的name和age字段
* //SELECT name,age FROM t_user where id >1
*/
@Test
public void test_05(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("name","age");
queryWrapper.gt("id",1L);
List<User> users = userMapper.selectList(queryWrapper);
System.out.println("users = " + users);
}
/**
* condition判断组织条件:(实现动态语句)
* 每个方法夺回==都会有一个boolean condition,允许我们的一位放一个比较表达式 ;true 则整个条件生效 false 则不生效
* 要求:前端传入两个参数 name age
* name不为空,作为条件-查询
* age>18,做为条件,查询等于 age=
*/
@Test
public void test6(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
String name="xx";
Integer age=18;
queryWrapper.eq(StringUtils.isNotBlank("name"),"name",name);
queryWrapper.eq(age!=null&&age>18,"age",age);
List<User> users = userMapper.selectList(queryWrapper);
System.out.println("users = " + users);
}
}
UpdateWrapper使用
@SpringBootTest
public class MyBatisPlusUpdateWrapperTest {
@Autowired
private UserMapper userMapper;
/**
* 使用queryWrapper修改值:
* ①准备要修改的实体类数据
* ②不能改为null
* 使用UpdateWrapper
* ①直接可以携带修改数据 set("列名","值")
* ②指定任意修改值
*/
@Test
public void test1(){
UpdateWrapper<User> updateWrapper = new UpdateWrapper();
updateWrapper.gt("age",20).like("name","a").or().isNull("email")
.set("email",null)
.set("age",90);
int update = userMapper.update(null,updateWrapper);
System.out.println("update = " + update);
}
lambdaWrapper使用
LambdaQueryWrapper与LambdaUpdateWrapper是对QueryWrapper与UpdateWrapper的升级,增加了一个列名的提示:
我们可以使用lambda表达式来写列名,以防列名出现错误
lambda表达式回顾:
Lambda 表达式是 Java 8 引入的一种函数式编程特性,它提供了一种更简洁、更直观的方式来表示匿名函数或函数式接口的实现。Lambda 表达式可以用于简化代码,提高代码的可读性和可维护性。
Lambda 表达式的语法可以分为以下几个部分:
1 参数列表: 参数列表用小括号 () 括起来,可以指定零个或多个参数。如果没有参数,可以省略小括号;如果只有一个参数,可以省略小括号。
例如:(a, b), x ->, () ->
2 箭头符号: 箭头符号 -> 分割参数列表和 Lambda 表达式的主体部分。
3 Lambda 表达式的主体: Lambda 表达式的主体部分可以是一个表达式或一个代码块。如果是一个表达式,可以省略 return 关键字;如果是多条语句的代码块,需要使用大括号 {} 括起来,并且需要明确指定 return 关键字。
例如:单个表达式:x -> x * x
代码块:(x, y) -> { int sum = x + y; return sum; }
方法引用回顾:
方法引用是 Java 8 中引入的一种语法特性,它提供了一种简洁的方式来直接引用已有的方法或构造函数。方法引用可以替代 Lambda 表达式,使代码更简洁、更易读。
1 静态方法引用: 引用静态方法,语法为 类名::静态方法名。
2 实例方法引用: 引用实例方法,语法为 实例对象::实例方法名。
3 对象方法引用: 引用特定对象的实例方法,语法为 类名::实例方法名。
4 构造函数引用: 引用构造函数,语法为 类名::new。
具体使用:
总结:"列名"=类名::getXXX
@SpringBootTest
public class MyBatisPlusLambdaQueryWrapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void test_01(){
QueryWrapper<User> queryWrapper=new QueryWrapper<>();
queryWrapper.like("name","a");
queryWrapper.between("age",20,30);
queryWrapper.isNotNull("email");
//lambdaQueryWrapper
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(User::getName,"a")
.between(User::getAge,20,30)
.isNotNull(User::getEmail);
List<User> users = userMapper.selectList(wrapper);
System.out.println("users = " + users);
}
}