黑马商城:MybatisPlus
黑马商城:MybatisPlus
文章目录
- 黑马商城:MybatisPlus
- Mybatis-plus介绍
- Mybatis-plus使用
- Mybatis-plus配置
- Mybatis-plus条件构造器
- 1.QueryWrapper
- 2.UpdateWrapper
- 3.LambdaQueryWrapper
- 自定义SQL
- service层CRUD
- 分页插件
Mybatis-plus介绍
Mybatis-plus简介:Mybatis增强工具,只做增强,不作改变,简化开发,提高效率。
-
1、无侵入:Mybatis-Plus 在 Mybatis 的基础上进行扩展,只做增强不做改变,引入 Mybatis-Plus 不会对现有的 Mybatis 构架产生任何影响,而且 MP 支持所有 Mybatis 原生的特性。
-
2、依赖少:仅仅依赖 Mybatis 以及 Mybatis-Spring。
-
3、损耗小:启动即会自动注入基本CRUD,性能基本无损耗,直接面向对象操作。
-
4、通用CRUD操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求。
-
5、多种主键策略:支持多达4种主键策略(内含分布式唯一ID生成器),可自由配置,完美解决主键问题。
-
6、内置分页插件:基于Mybatis物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于写基本List查询。
-
7、内置性能分析插件:可输出Sql语句以及其执行时间,建议开发测试时启用该功能,能有效解决慢查询。
-
8、内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,预防误操作。
Mybatis-plus使用
为了简化单表CRUD,MybatisPlus提供了一个基础的BaseMapper接口,其中已经实现了单表的CRUD。因此我们自定义的Mapper只要实现了BaseMapper,就无需自己实现单表CRUD了。
package com.itheima.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
public interface UserMapper extends BaseMapper<User> {
}
MybatisPlus是根据PO实体的信息来推断出表的信息,从而生成SQL的。默认情况下:
- MybatisPlus会把PO实体的类名驼峰转下划线作为表名
- MybatisPlus会把PO实体的所有变量名驼峰转下划线作为表的字段名,并根据变量类型推断字段类型
- MybatisPlus会把名为id的字段作为主键
但很多情况下,默认的实现与实际场景不符,因此MybatisPlus提供了一些注解便于我们声明表信息。
@TableName:表名注解,标识实体类对应的表
- 除了value属性外还有:resultMap指定xml 中 resultMap 的 id等属性
@TableId:主键注解,标识实体类中的主键字段
- 除了value属性外还有:type指定主键策略
@TableField:普通字段注解,使用情况如下
- 成员变量名与数据库字段名不一致。
- 成员变量是以isXXX命名,按照JavaBean的规范,MybatisPlus识别字段时会把is去除,这就导致与数据库不符。
- 成员变量名与数据库一致,但是与数据库的关键字冲突。使用@TableField注解给字段名添加转义字符。
- 除了value属性外还有:exist指定是否为数据库表字段
@TableName("user")
public class User {
@TableId
private Long id;
private String name;
private Integer age;
@TableField("isMarried")
private Boolean isMarried;
@TableField("concat")
private String concat;
}
Mybatis-plus配置
mybatis-plus:
type-aliases-package: com.itheima.mp.domain.po
global-config:
db-config:
id-type: auto # 全局id类型为自增长
- 实体类的别名扫描包
- 全局id类型
需要注意的是,MyBatisPlus也支持手写SQL的,而mapper文件的读取地址可以自己配置:
mybatis-plus:
mapper-locations: "classpath*:/mapper/**/*.xml" # Mapper.xml文件地址,当前这个是默认值。
例如我们定义一个mapper
<?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.itheima.mp.mapper.UserMapper">
<select id="queryById" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
</mapper>
@Test
void testQuery() {
User user = userMapper.queryById(1L);
System.out.println("user = " + user);
}
Mybatis-plus条件构造器
除了新增以外,修改、删除、查询的SQL语句都需要指定where条件。因此BaseMapper中提供的相关方法除了以id作为where条件以外,还支持更加复杂的where条件。
1.QueryWrapper
无论是修改、删除、查询,都可以使用QueryWrapper来构建查询条件。
查询示例:
@Test
void testQueryWrapper() {
// 1.构建查询条件 where name like "%o%" AND balance >= 1000
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.select("id", "username", "info", "balance")
.like("username", "o")
.ge("balance", 1000);
// 2.查询数据
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
更新示例:
@Test
void testUpdateByQueryWrapper() {
// 1.构建查询条件 where name = "Jack"
QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "Jack");
// 2.更新数据,user中非null字段都会作为set语句
User user = new User();
user.setBalance(2000);
userMapper.update(user, wrapper);
}
2.UpdateWrapper
基于BaseMapper中的update方法更新时只能直接赋值,对于一些复杂的需求就难以实现。
更新时SET的赋值结果是基于字段现有值的,这个时候就要利用UpdateWrapper中的setSql功能。
@Test
void testUpdateWrapper() {
List<Long> ids = List.of(1L, 2L, 4L);
// 1.生成SQL
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
.setSql("balance = balance - 200") // SET balance = balance - 200
.in("id", ids); // WHERE id in (1, 2, 4)
// 2.更新,注意第一个参数可以给null,也就是不填更新字段和数据,
// 而是基于UpdateWrapper中的setSQL来更新
userMapper.update(null, wrapper);
}
3.LambdaQueryWrapper
无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,会出现字符串魔法值。这在编程规范中显然是不推荐的。
那怎么样才能不写字段名,又能知道字段名呢?
其中一种办法是基于变量的gettter方法结合反射技术。因此我们只要将条件对应的字段的getter方法传递给MybatisPlus,它就能计算出对应的变量名了。而传递方法可以使用JDK8中的方法引用和Lambda表达式。
因此MybatisPlus又提供了一套基于Lambda的Wrapper,包含两个:
- LambdaQueryWrapper
- LambdaUpdateWrapper
分别对应QueryWrapper和UpdateWrapper
@Test
void testLambdaQueryWrapper() {
// 1.构建条件 WHERE username LIKE "%o%" AND balance >= 1000
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.lambda()
.select(User::getId, User::getUsername, User::getInfo, User::getBalance)
.like(User::getUsername, "o")
.ge(User::getBalance, 1000);
// 2.查询
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
自定义SQL
除了像Mybatis一样在xml中写完整SQL,MybatisPlus还提供了自定义SQL功能,可以让我们利用Wrapper生成查询条件,再结合Mapper.xml编写SQL。
@Test
void testCustomWrapper() {
// 1.准备自定义查询条件
List<Long> ids = List.of(1L, 2L, 4L);
QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", ids);
// 2.调用mapper的自定义方法,直接传递Wrapper
userMapper.deductBalanceByIds(200, wrapper);
}
package com.itheima.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.annotations.Param;
public interface UserMapper extends BaseMapper<User> {
@Select("UPDATE user SET balance = balance - #{money} ${ew.customSqlSegment}")
void deductBalanceByIds(@Param("money") int money, @Param("ew") QueryWrapper<User> wrapper);
}
service层CRUD
MyBatis-Plus 服务层的增删改查功能是为了在常见的业务场景中提高开发效率,尤其是对于简单的单表操作,它提供了一种快速开发的途径,减少了开发人员在持久层编写大量简单 SQL 语句的工作。而持久层则更侧重于 SQL 的精确控制和复杂 SQL 逻辑的实现,为更复杂的数据库操作提供了灵活的实现基础。两者结合使用可以使开发工作更高效,满足不同的业务需求,在开发中可以根据实际情况选择使用持久层的精确控制还是服务层的便捷通用功能。
- service添加了批量方法
- service层的方法自动添加事务
由于Service中经常需要定义与业务有关的自定义方法,因此我们不能直接使用IService,而是自定义Service接口,然后继承IService以拓展方法。同时,让自定义的Service实现类继承ServiceImpl,这样就不用自己实现IService中的接口了。
接口继承IService接口
public interface UserService extends IService<User> {
}
类继承ServiceImpl实现类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService{
}
按照Restful风格编写Controller接口方法:
package com.itheima.mp.controller;
import cn.hutool.core.bean.BeanUtil;
import com.itheima.mp.domain.dto.UserFormDTO;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.service.IUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Api(tags = "用户管理接口")
@RequiredArgsConstructor
@RestController
@RequestMapping("users")
public class UserController {
private final IUserService userService;
@PostMapping
@ApiOperation("新增用户")
public void saveUser(@RequestBody UserFormDTO userFormDTO){
// 1.转换DTO为PO
User user = BeanUtil.copyProperties(userFormDTO, User.class);
// 2.新增
userService.save(user);
}
@DeleteMapping("/{id}")
@ApiOperation("删除用户")
public void removeUserById(@PathVariable("id") Long userId){
userService.removeById(userId);
}
@GetMapping("/{id}")
@ApiOperation("根据id查询用户")
public UserVO queryUserById(@PathVariable("id") Long userId){
// 1.查询用户
User user = userService.getById(userId);
// 2.处理vo
return BeanUtil.copyProperties(user, UserVO.class);
}
@GetMapping
@ApiOperation("根据id集合查询用户")
public List<UserVO> queryUserByIds(@RequestParam("ids") List<Long> ids){
// 1.查询用户
List<User> users = userService.listByIds(ids);
// 2.处理vo
return BeanUtil.copyToList(users, UserVO.class);
}
}
分页插件
在未引入分页插件的情况下,MybatisPlus是不支持分页功能的,IService和BaseMapper中的分页方法都无法正常起效。所以,我们必须配置分页插件。
MybatisConfig 配置类
@Configuration
public class MybatisConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 初始化核心插件
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
使用分页插件
@Test
void testPageQuery() {
// 1.分页查询,new Page()的两个参数分别是:页码、每页大小
Page<User> p = userService.page(new Page<>(2, 2));
// 2.总条数
System.out.println("total = " + p.getTotal());
// 3.总页数
System.out.println("pages = " + p.getPages());
// 4.数据
List<User> records = p.getRecords();
records.forEach(System.out::println);
//.hasPrevious()是否有上一页
//.hasNext()是否有下一页
//.getSize()每页显示的条数
//.getCurrent()当前页
}