一、快速入门
1、入门案例
使用步骤:
1)引入依赖(引入MyBatisPlus依赖就不需要再引入MyBatis了,它集成了MyBatis和MyBatisPlus的所有功能)
将
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
把
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
替换掉即可
2)自定义Mapper并继承MybatisPlus提供的BaseMapper接口:
public interface UserMapper extends BaseMapper<User> {
注意:继承时要指定泛型为操作实体类的类型
3)调用crud方法
优点:1)润物无声:制作增强不做改变,引入他不会对现有工程产生影响,如丝般顺滑。
2)效率至上:只需要简单配置,即可快速进行单表CRUD操作,从而节省大量时间。
2、常见注解
MyBatisPlus通过扫描实体类,并基于反射获取实体类信息作为数据库表信息。
其中约定:1)类名驼峰转下划线作为表名
2)名字id的字段作为主键
3)变量名驼峰转下划线作为表的字段名
如果实体类不符合约定就需要自己配置,使用提供的注解进行自定义配置。
常见注解:
- @TableName:用来指定表名
- @TableId:用来指定表中的主键字段信息
- @TableField:用来指定表中的普通字段信息 (使用场景:①成员变量名与数据库字段不一致、②成员变量名以is开头且是布尔值、③成员变量名与数据库关键字冲突、④成员变量不是数据库字段@TableField(exist = false))
这里的id其实可以不用配置,因为字段名相同 。
在MP中,id的自增称为id策略,可以通过type来设置 ,即@TableId(value="id" type = IdType.AUTO),如果设置了id自增的话应该加上注解否则默认雪花算法
id策略有以下常见类型:
- AUTO:数据库自增长
- INPUT:通过set方法自行输入
- ASSIGN_ID:分配ID,由MP提供的,通过雪花算法生成的Long型,20位id
3、常见配置
大多数的配置都有默认值,因此我们都无需配置。但还有一些是没有默认值的,例如:
实体类的别名扫描包
全局id类型
mybatis-plus:
type-aliases-package: com.itheima.mp.domain.po
global-config:
db-config:
id-type: auto # 全局id类型为自增长
需要注意的是,MyBatisPlus也支持手写SQL的,而mapper文件的读取地址可以自己配置:
mybatis-plus:
mapper-locations: "classpath*:/mapper/**/*.xml" # Mapper.xml文件地址,当前这个是默认值。
二、核心功能
1、条件构造器
MybatisPlus支持各种复杂的where条件,可以满足日常开发的所有需求。
条件构造方法
方法 | 说明 | 示例 |
---|---|---|
eq | 等于 = | eq("name", "老王") |
ne | 不等于 <> | ne("name", "老王") |
gt | 大于 > | gt("age", 18) |
ge | 大于等于 >= | ge("age", 18) |
lt | 小于 < | lt("age", 30) |
le | 小于等于 <= | le("age", 30) |
between | BETWEEN 值1 AND 值2 | between("age", 18, 30) |
notBetween | NOT BETWEEN | notBetween("age", 18, 30) |
like | LIKE '%值%' | like("name", "王") |
notLike | NOT LIKE '%值%' | notLike("name", "王") |
in | 字段 IN (value.get(0), value.get(1), ...) | in("age", Arrays.asList(18, 20, 22)) |
部分代码:
@Test
/**
* 查找名字中带o的,存款大于等于1000元的id、username、info、balance
*/
void testQueryWrapper() {
//构建查询条件
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.select("id", "username", "info", "balance")
.like("username", "o")
.ge("balance", 1000);
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
@Test
void testLambdaQueryWrapper() {
//构建查询条件
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
.select(User::getId, User::getUsername, User::getInfo, User::getBalance)
.like(User::getUsername, "o")
.ge(User::getBalance, 1000);
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
/**
* 更新用户名为jack的用户的余额为2000
*/
@Test
void testUpdateByQueryWrapper() {
//更新的数据
User user = new User();
user.setBalance(2000);
//更新的条件
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.eq("username", "jack");
//执行更新
userMapper.update(user, wrapper);
}
/**
* 更新id为1,2,4的用户的余额,扣200
*/
@Test
void testUpdateWrapper() {
List<Long> ids = List.of(1L, 2L, 4L);
//更新的条件
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
.setSql("balance = balance - 200")
.in("id", ids);
//执行更新
userMapper.update(null, wrapper);
}
2、自定义sql
利用MP的Wrapper来构建复杂的where条件,然后自己定义SQL语句中剩下的部分。
①基于Wrapper构建where条件
@Test
void testCustomSqlUpdate() {
//1、更新条件
List<Long> ids = List.of(1L, 2L, 4L);
int amount = 200;
//定义条件
QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id",ids);
//调用自定义方法
userMapper.updateBalanceByIds( wrapper, amount);
}
②在mapper方法参数中用Para注解声明wrapper变量名称,必须是ew
void updateBalanceByIds(@Param("ew") QueryWrapper<User> wrapper,@Param("amount") int amount);
③自定义SQL,并使用Wrapper条件
<update id="updateBalanceByIds">
update user set balance = balance - #{amount} ${ew.customSqlSegment}
</update>
3、Service接口
1)自定义接口继承IService接口。
public interface IUserService extends IService<User> {
}
2)自定义实现类,实现类自定义接口并继承ServiceImpl类。
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
}
4、BeanUtil
BeanUtil.copyProperties 与 BeanUtils.copyProperties 的区别
这两个方法都是用于对象属性拷贝的工具方法,但来自不同的工具库,有以下几个关键区别:
1. 来源不同
方法 | 所属库 | Maven依赖 |
---|---|---|
BeanUtil.copyProperties |
Hutool工具库 | cn.hutool:hutool-all |
BeanUtils.copyProperties |
Apache Commons BeanUtils 或 Spring Framework | org.apache.commons:commons-beanutils 或 org.springframework:spri |
2. 主要区别对比
2.1 Hutool的BeanUtil.copyProperties
User user = BeanUtil.copyProperties(userDTO, User.class);
特点:
直接创建目标对象:自动实例化目标类
支持更多类型转换:内置更强大的类型转换器
性能优化:Hutool针对反射做了优化
支持别名:可通过注解指定字段映射关系
链式调用:返回目标对象实例
2.2 Apache Commons/Spring的BeanUtils.copyProperties
User user = new User();
BeanUtils.copyProperties(userDTO, user);
特点:
需要预先创建对象:必须先实例化目标对象
基本类型转换:只支持简单的类型转换
Spring版本增强:Spring的版本比Apache的更安全
直接修改目标对象:无返回值
3. 功能对比表
特性 | Hutool BeanUtil | Apache/Spring BeanUtils |
---|---|---|
是否需要预先创建对象 | 否 | 是 |
类型转换能力 | 强大(支持更多类型) | 基础 |
性能 | 较高(缓存反射) | 一般 |
字段映射配置 | 支持@Alias注解 | 不支持 |
异常处理 | 更友好 | 可能抛BeanException |
嵌套对象拷贝 | 支持 | 不支持 |
链式调用 | 支持 | 不支持 |
来源库大小 | 较大(整个Hutool) | 较小 |
4. 使用场景建议
使用Hutool的BeanUtil当:
需要从DTO直接创建实体对象
涉及复杂类型转换
需要处理字段别名映射
项目已经使用了Hutool工具库
使用Spring的BeanUtils当:
已经存在目标对象实例
只需要简单属性拷贝
项目已经是Spring环境
希望减少额外依
5、实战
@Autowired注解并不推荐
推荐构造函数注解,但是构造函数比较麻烦,可以使用lombok的注解@RequiredArgsConstructor
@RestController
@RequestMapping("/users")
@Api(tags="用户管理接口")
@RequiredArgsConstructor //添加该注解代替@Autowired
public class UserController {
private final IUserService userService;
lambdaqurey()方法,不用写wrapper并且可以直接链式编程出查询语句即.list()
@Override
public List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {
return lambdaQuery()
.like(name != null, User::getUsername, name)
.eq(status != null, User::getStatus, status)
.ge(minBalance != null, User::getBalance, minBalance)
.le(maxBalance != null, User::getBalance, maxBalance)
.list();
lambdaUpdate()方法
//4、扣减余额
// baseMapper.deductBalance(id, money);
int remainBalance = user.getBalance() - money;
lambdaUpdate()
.set(User::getBalance, remainBalance)
.set(remainBalance == 0 ,User::getStatus,2)
.eq(User::getId, id)
.update();
rewriteBatchedStatements
是 MySQL JDBC 驱动程序的一个重要配置参数,主要用于优化批量(Batched)SQL 语句的执行性能。该参数默认为false,进行的批处理操作默认会转化为单个的SQL,当改为true是,会进行真正的批处理。
配置:
url: jdbc:mysql://localhost:3306/db?rewriteBatchedStatements=true
三、扩展功能
1、代码生成
1)在file->settings->Plugins中搜索mybatisplus安装插件
2)重启idea,上方找到other打开
3)填写对应参数
4)点击另一个按钮,进行一些配置
2、静态工具
@Override
public UserVO queryUserAddressesById(Long id) {
//1、查询用户
User user = getById(id);
if(user == null || user.getStatus() == 2){
throw new RuntimeException("用户状态异常");
}
//2、查询地址
Db.lambdaQuery(Address.class) //静态方法,可以不用注入address的service
.eq(Address::getUserId , id)
.list();
return null;
通过Stream流按照userid进行分组,并存储到map中
addressMap = addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId));
3、逻辑删除
逻辑删除就是基于代码逻辑模拟删除效果,但并不会真正删除数据。思路如下:
- 在表中添加一个字段标记数据是否被删除
- 删除时标记为1
- 查询时只查询标记为0的数据
例如逻辑删除的字段为delete 删除操作使用update: update user set delete = 1 where id = 1 and deleted = 0
在mybatisPlus中,其提供了逻辑删除功能,无需改变方法调用的方式,而是在底层帮我们自动修改CRUD的语句。我们要做的就是在application.yaml文件中配置逻辑删除的字段名称和值即可:
mybatis-plus:
type-aliases-package: com.itheima.mp.domain.po
global-config:
db-config:
id-type: auto
logic-delete-field: deleted #配置逻辑删除字段
4、枚举处理器
枚举
是一种特殊类,格式:
注意:
- 枚举中的第一行,只能写一些合法的标识符(枚举项),多个名称用,隔开。
- 这些枚举项本质是常量,每一个枚举项都会记录 枚举类的一个对象。
需要在配置文件中添加配置
mybatis-plus:
configuration:
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
编写类
@Getter //自动生成getter方法
public enum UserStatus {
NORMAL(1, "正常"),
FREEZE(2, "冻结"),
;
@EnumValue //写在哪个属性,哪个属性与数据库对应
private final int value;
@JsonValue //写在哪个属性,哪个属性就在前端展示,,默认是枚举项的名字如NORMAL
private final String desc;
UserStatus(int value, String desc){
this.value = value;
this.desc = desc;
}
}
在使用时就可以使用 UserStatus.FREEZE 或 UserStatus.NORMAL 了,更易阅读
5、JSON处理器
首先要定义实体类
将字段改为实体类类型
加上@TableFiled(typeHandler = JacksonTypeHandler.class)注解
开启自动结果集映射 在类注解:@TableName中加上 atuoResultMap = true
@Data
@TableName(value = "user", autoResultMap = true)
public class User {
/**
* 详细信息
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private UserInfo info;
}
四、插件功能
1、分页查询
编写配置类
@Configuration
public class MyBatisConfig {
@Bean
public MybatisPlusInterceptor MybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//创建分页插件
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
paginationInnerInterceptor.setMaxLimit(1000L);
//添加分页插件
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}
}
测试使用
OrderItem中传递 排序的字段 以及升序还是降序