Mybatis-Plus
- 条件构造器:提供了强大的条件构造器,可以构造各种复杂的查询条件,以应对各种复杂查询。
- 动态sql很方便,不需要mybaties那样去 写 if 实现
- 内置分页插件:配置好插件之后,写分页等同于普通 List 查询,无需关注分页逻辑。
- 不用一条条sql去敲,更接近人类说话的方式
- MyBatis Plus 如何实现连表查询 mybatis plus join_mybatisplus inner join-CSDN博客
1.boot中集成
- 先导入依赖
- 一般在model模块中实体类头顶,需要加入注解 @Data @TableName(“user”), ⭕⭕⭕有点类似于JPA,
- 主键注解 @TableId(value = “id”, type = IdType.AUTO)
- 普通字段注解 @TableField(“name”)
2.传统的mapper和Mapper.xml编写方式
public interface AdjustSalaryMapper { //以下mapper接口 中的方法提供给service层调用
int deleteByPrimaryKey(Integer id);
int insert(AdjustSalary record);
int insertSelective(AdjustSalary record);
AdjustSalary selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(AdjustSalary record);
int updateByPrimaryKey(AdjustSalary record);
List<AdjustSalary> getAllAdjustSalary();
}
//以下是mapper.xml写具体的sql
<?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="org.javaboy.vhr.mapper.AdjustSalaryMapper" >
<resultMap id="BaseResultMap" type="org.javaboy.vhr.model.AdjustSalary" >
<id column="id" property="id" jdbcType="INTEGER" />
<result column="eid" property="eid" jdbcType="INTEGER" />
<result column="asDate" property="asdate" jdbcType="DATE" />
<result column="beforeSalary" property="beforesalary" jdbcType="INTEGER" />
<result column="afterSalary" property="aftersalary" jdbcType="INTEGER" />
<result column="reason" property="reason" jdbcType="VARCHAR" />
<result column="remark" property="remark" jdbcType="VARCHAR" />
</resultMap>
<sql id="Base_Column_List" >
id, eid, asDate, beforeSalary, afterSalary, reason, remark
</sql>
<resultMap id="adjustSalaWithNname" type="org.javaboy.vhr.model.AdjustSalary" extends="BaseResultMap">
<association property="employee" javaType="org.javaboy.vhr.model.Employee">
<result column="ename" property="name" jdbcType="VARCHAR"/>
</association>
</resultMap>
<!-- 查询所有工资调整记录-->
<select id="getAllAdjustSalary" resultMap="adjustSalaWithNname">
select a.* ,e.`name` as ename from adjustsalary a left join employee e on(e.id=a.eid)
</select>
3.❗❗❗MP的通用Mapper接口
mybaties没有的,MP最大的特点就是提供了这些通用的crud,不用手写。
第一步:
创建 UserMapper接口,并继承由Mybatis Plus提供的 BaseMapper接口,如下
@Mapper //记得加@mapper
public interface UserMapper extends BaseMapper<User> { //记得继承于MP的 💹BaseMapper <xxxx>
}
偷懒方法:若mapper interface太多,可用扫描的方式去进行统一管理,不用每一个 都加 注解@mapper
第二步:测试UserMapper
💹Users是数据库中的一个表,也是Model中的一个实体类
SpringBootTest
class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelectList() {
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
@Test
public void testSelectById() {
User user = userMapper.selectById(1); //💹userMapper.selectById(1) 中selectById()这个方法是MP自动写好的
System.out.println(user);
}
@Test
public void testInsert() {
User user = new User();
user.setName("zhangsan");
user.setAge(11);
user.setEmail("test@test.com");
userMapper.insert(user);
}
@Test
public void testUpdateById() {
User user = userMapper.selectById(1);
user.setName("xiaoming");
userMapper.updateById(user);
}
@Test
public void testDeleteById() {
userMapper.deleteById(1); // .deleteById(1)也是 MP自带的
}
}
4.❗❗❗通用Service
💹通用Service进一步封装了通用Mapper的CRUD方法
这个也是MP提供的通用 service层,例如saveOrUpdate
、saveBatch
等高级方法。通用mapper的crud的方法名更贴近数据库,eg: select、 insert
通用service的crud的方法名更贴近业务 eg: get 、save
第一步创建service接口
public interface UserService extends IService<User> { //IService<User>是💹 MP特有的, 💹泛型接口
}
第二步创建service实现类 impl
//@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
//extends ServiceImpl<UserMapper, User> 注意他还继承这个类
//ServiceImpl<UserMapper, User>里面实现了IService<User>里面的所有接口,也是泛型
}
第三步测试通用service
@SpringBootTest
class UserServiceImplTest {
@Autowired
private UserService userService;
@Test
public void testSaveOrUpdate() {
User user1 = userService.getById(2); //💹getById(x)是通用方法
user1.setName("xiaohu");
User user2 = new User();
user2.setName("lisi");
user2.setAge(27);
user2.setEmail("lisi@email.com");
userService.saveOrUpdate(user1); //💹如果id不为空,这插入,如果id为空,这更新数据库记录
userService.saveOrUpdate(user2);
}
@Test
public void testSaveBatch() {
User user1 = new User();
user1.setName("dongdong");
user1.setAge(49);
user1.setEmail("dongdong@email.com");
User user2 = new User();
user2.setName("nannan");
user2.setAge(29);
user2.setEmail("nannan@email.com");
List<User> users = List.of(user1, user2);
userService.saveBatch(users); //💹saveBatch保存一组user,本质上是insert一组数据在数据库中
}
}
这个方法本身也是调用 mapper层的方法,⭕本质上就是 mapper层的 selectById () 方法,只是名字不一样而已
5.条件构造器Querywrapper
⭕通用的mapper和service是只有 selectById()这些方法是无法满足使用需求的,所以需要构造器
条件构造器用于构造复杂的查询条件,例如获取name='zhangsan'
的用户。MyBatis Plus共提供了两类构造器,分别是QueryWrapper
和UpdateWrapper
。其中QueryWrapper
主要用于查询、删除操作,UpdateWrapper
主要用于更新操作,下面通过几个案例学习条件构造器的基础用法。
- AbstractWrapper:这是一个抽象基类,提供了所有 Wrapper 类共有的方法和属性。它定义了条件构造的基本逻辑,包括字段(column)、值(value)、操作符(condition)等。💹所有的 QueryWrapper、UpdateWrapper、LambdaQueryWrapper 和 LambdaUpdateWrapper 都继承自 AbstractWrapper。
- 💹QueryWrapper:专门用于构造查询条件,支持基本的等于、不等于、大于、小于等各种常见操作。它允许你以💹链式调用的方式添加多个查询条件,并且可以组合使用
and
和or
逻辑。 - 💹UpdateWrapper:用于构造更新条件,可以在更新数据时指定条件。与 QueryWrapper 类似,它也支持链式调用和逻辑组合。使用 UpdateWrapper 可以在不创建实体对象的情况下,直接设置更新字段和条件。
- LambdaQueryWrapper:这是一个基于 Lambda 表达式的查询条件构造器,它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名。这种方式提高了代码的可读性和可维护性,尤其是在字段名可能发生变化的情况下。
- LambdaUpdateWrapper:类似于 LambdaQueryWrapper,LambdaUpdateWrapper 是基于 Lambda 表达式的更新条件构造器。它允许你使用 Lambda 表达式来指定更新字段和条件,同样避免了硬编码字段名的问题
缺点
- RPC 调用中的 Wrapper:不支持也不赞成在 RPC 调用中传输 Wrapper 对象。Wrapper 对象通常包含大量信息,不适合作为传输对象。正确的做法是定义一个 DTO(数据传输对象)进行传输,然后在被调用方根据 DTO 执行相应的操作。
- 维护性:避免在 Controller 层使用 Map 接收值,💹这种做法虽然开发时方便,但会给后续的维护带来困难。
- 问题反馈:不接受任何关于 RPC 传输 Wrapper 报错相关的 issue 或 pr。
- 安全性:
QueryWrapper
UpdateWrapper
字段部分,如有允许前端传入 SQL 片段
这可能会导致SQL 注入风险
需要校验,更多查看 预防安全漏洞。
❗主要使用QueryWrapper 和 UpdateWrapper 这两个条件构造器
第一步:创建WrapperTest测试类
@SpringBootTest
public class WrapperTest { 💹💹💹//这里相当于在controller层,因为下面是调用userService层
@Autowired
private UserService userService;
@Test
public void testQueryWrapper() {
//查询name=Tom的所有用户
QueryWrapper<User> queryWrapper1 = new QueryWrapper<>(); //💹QueryWrapper<User>中user也是泛型
queryWrapper1.eq("name", "Tom"); //💹第一个是数据库字段,第二个是 字段值
List<User> list = userService.list(querywrapper);
//💹userService.list(传入一个querywrapper对象), .list这个方法是Mybatis-plus的特有的通用的service方法
//查询邮箱域名为baomidou.com的所有用户
QueryWrapper<User> queryWrapper2 = new QueryWrapper<>();
queryWrapper2.like("email", "baomidou.com"); //💹相当于sql中 ‘% baomindou.com %’ 匹配xxbaomindou.comxx
//查询所有用户信息并按照age字段降序排序
QueryWrapper<User> queryWrapper3 = new QueryWrapper<>();
queryWrapper3.orderByDesc("age");
List<User> list = userService.list(queryWrapper3);
//查询age介于[20,30]的所有用户
QueryWrapper<User> queryWrapper4 = new QueryWrapper<>();
queryWrapper4.between("age", 20, 30);
List<User> list = userService.list(queryWrapper4);
//查询age小于20 或 大于30的用户
QueryWrapper<User> queryWrapper5 = new QueryWrapper<>();
queryWrapper5.lt("age", 20).or().gt("age", 30); //💹注意中间有个or
List<User> list = userService.list(queryWrapper5);
//邮箱域名为baomidou.com且年龄小于30或大于40且的用户
//where email like "%baomidou.coms' and (age <30 or age >40)
QueryWrapper<User> queryWrapper6 = new QueryWrapper<>();
queryWrapper6.like("email", "baomidou.com").and(wrapper -> wrapper.lt("age", 30).or().gt("age", 40));
List<User> list = userService.list(queryWrapper6);
list.forEach(System.out::println);
}
@Test
public void testUpdateWrapper() {
//将name=Tom的用户的email改为Tom@baobidou.com
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name", "Tom");
updateWrapper.set("email", "Tom@baobidou.com");
userService.update(updateWrapper);
}
}
allEq
// 设置所有字段的相等条件,如果字段值为null,则根据null2IsNull参数决定是否设置为IS NULL
allEq(Map<String, Object> params)
allEq(Map<String, Object> params, boolean null2IsNull)
allEq(boolean condition, Map<String, Object> params, boolean null2IsNull)
// 设置所有字段的相等条件,通过filter过滤器决定哪些字段应该被包含,如果字段值为null,则根据null2IsNull参数决定是否设置为IS NULL
allEq(BiPredicate<String, Object> filter, Map<String, Object> params)
allEq(BiPredicate<String, Object> filter, Map<String, Object> params, boolean null2IsNull)
allEq(boolean condition, BiPredicate<String, Object> filter, Map<String, Object> params, boolean null2IsNull)
eg:
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.allEq(Map.of("id", 1, "name", "老王", "age", null));
eq
// 设置指定字段的相等条件
eq(R column, Object val)
// 根据条件设置指定字段的相等条件
eq(boolean condition, R column, Object val)
column
:数据库字段名或使用Lambda
表达式的字段名。val
:与字段名对应的值。condition
:一个布尔值,用于控制是否应用这个相等条件。
eg:
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "老王");
like模糊查询
- notLike
- likeLeft
- likeRight
- notLikeLeft
- notLikeRight
后序用到再去官网查阅
条件构造器 | MyBatis-Plus (baomidou.com)
6.条件构造器Updatewrapper
主要关注 where 和 set
//将name=Tom的用户的email改为Tom@baobidou.com
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name", "Tom").set("email", "Tom@baobidou.com"); //💹eq就是where语句 set就是set语句
userService.update (updatewrapper);
//标准sql UPDATE table_name SET column1 = value1, column2 = value2 WHERE condition;
7.分页插件
-
先创建一个配置类,具体看官网
-
构造分页对象 Page
IPage<T> page = new Page<>(current, size); //💹只传入两个参数,current和size
-
分页查询
Mybatis Plus的
BaseMapper
和ServiceImpl
均提供了常用的分页查询的方法,例如:-
1.通用BaseMapper的分页查询:
IPage<T> selectPage(IPage<T> page,Wrapper<T> queryWrapper); //通用Mapper分页查询 @Test public void testPageMapper() { IPage<User> page = new Page<>(2, 3); IPage<User> userPage = 💹userMapper.selectPage(page, null) 💹//这个方法是MP中通用usermapper的selectPage() userPage.getRecords().forEach(System.out::println); }
-
2.通用ServiceImpl的分页查询:
// 无条件分页查询 IPage<T> page(IPage<T> page); // 条件分页查询 IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper); //通用Service分页查询 @Test public void testPageService() { Page<User> page = new Page<>(2, 3); //💹我想查询第2页,每页有3条数据记录 Page<User> userPage = 💹userService.page(page); //userService.page是MP提供的接口 userPage.getRecords().forEach(System.out::println); //record就是那一页的 每一条的所有记录 }
-
3.自定义Mapper
💹MP会提供通用mapper,但是自己也可以定义,自己定义的就回到了mybaties时代了
对于自定义SQL,也可以十分方便的完成分页查询,如下
①Mapper接口:
IPage<UserVo> selectPageVo(IPage<?> page, Integer state);
②编写Mapper.xml
创建
resources/mapper/UserMapper.xml
文件,内容如下//💹这里面只负责查询所有,不用关注分页
<?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.atguigu.hellomp.mapper.UserMapper"> <select id="selectUserPage" resultType="com.atguigu.hellomp.entity.User"> select * from user </select> </mapper>
注意:
Mapper.xml
中的SQL只需实现查询list
的逻辑即可,无需关注分页的逻辑。public void testCustomMapper() { IPage<User> page = new Page<>(2, 3); IPage<User> userPage = 💹userMapper.selectUserPage(page); //💹selectUserPage这个方法是直接定义的,不是MP自带的 userPage.getRecords().forEach(System.out::println); }
-
8.Mybatis X 插件
❗❗❗类似于以前用的 mybatis-generator-core-1.3.1代码生成器
MyBatis Plus提供了一个IDEA插件——
MybatisX
,使用它可根据数据库快速生成Entity
、Mapper
、Mapper.xml
、Service
、ServiceImpl
等代码,使用户更专注于业务。
-
先安装插件 (在IDEA插件市场搜索
MyBatisX
) -
配置数据库连接
-
生成代码
首先将之前编写的
User
、UserMapper
、UserServcie
、UserServiceImpl
全部删除,然后按照下图指示使用插件生成代码
SpringBoot中MybatisX插件的简单使用教程(超详细!!)-CSDN博客
9.偷懒方法
在上面的示例中,我们定义了一个UserMapper接口,并使用注解标注了查询、插入、更新和删除方法。通过这种方式,我们可以省去编写XML配置文件的步骤,使代码更加简洁和易于理解。
@Mapper
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}") // 直接在这里写select
User getUserById(@Param("id") int id);
@Insert("INSERT INTO user(name, age) VALUES(#{name}, #{age})")
void addUser(User user);
@Update("UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}")
void updateUser(User user);
@Delete("DELETE FROM user WHERE id = #{id}")
void deleteUser(@Param("id") int id);
}