文章目录
- 前言
- 引入依赖
- 数据准备
- 修改 Mapper
- 查询
- 分页查询
前言
它的联表查询能力
一直被大家所诟病。一旦遇到
left join
或 right join
的左右连接,你还是得老老实实的打开 xml 文件
,手写上一大段的 sql 语句。
直到前几天,偶然碰到了这么一款叫做 mybatis-plus-join
的工具(后面就简称 mpj 了)
以
类似 mybatis-plus 中 QueryWrapper 的方式来进行联表查询
了,话不多说,我们下面开始体验
引入依赖
<dependency>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join</artifactId>
<version>1.3.5</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
数据准备
订单表:
用户表,包含用户姓名:
商品表,包含商品名称和单价
在订单表中,
通过用户 id 和商品 id 与其他两张表进行关联
修改 Mapper
@Mapper
public interface OrderMapper extends MPJBaseMapper<Order> {
}
对其余两个表的 Mapper
接口也进行相同的改造。此外,我们的 service 也
可以选择继承 MPJBaseService
,serviceImpl 选择继承 MPJBaseServiceImpl
,
这两者为非必须继承
查询
Mapper
接口改造完成后,我们把它注入到 Service 中
,虽然说我们要完成 3张表的联表查询
但是以 Order 作为主表的话,那么只注入这一个对应的OrderMapper 就可以,非常简单。
@Service
@AllArgsConstructor
public class OrderServiceImpl implements OrderService {
private final OrderMapper orderMapper;
}
public void getOrder() {
List<OrderDto> list = orderMapper.selectJoinList (OrderDto.class,
new MPJLambdaWrapper<Order> ()
.selectAll (Order.class)
.select (Product::getUnitPrice)
.selectAs (User::getName, OrderDto::getUserName)
.selectAs (Product::getName, OrderDto::getProductName)
.leftJoin (User.class, User::getId, Order::getUserId)
.leftJoin (Product.class, Product::getId, Order::getProductId)
.eq (Order::getStatus, 3));
list.forEach (System.out::println);
}
调用 mapper 的 selectJoinList()
方法,进行关联查询,返回多条结
果。后面的第一个参数 OrderDto.class
代表 接收 返回查询结果的类
,作用和我们之前在 xml 中写的 resultType 类似。
这个类可以直接继承实体
,再添加上需要在关联查询中返回的列即可:
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class OrderDto extends Order {
String userName;
String productName;
Double unitPrice;
}
接下来的 MPJLambdaWrapper
就是构建查询条件的核心了,看一下我们在上面用到的几个方法:
selectAll()
:查询指定实体类的全部字段
select()
:查询指定的字段
,支持可变长参数同时查询多个字段
,但是在同一个 select 中 只能查询相同表 的字段
,所以如果查询多张表的字段需要分开写
selectAs()
:字段别名查询,用于数据库字段与接收结果的 dto 中属性名称不一致时转换leftJoin()
:左连接,其中第一个参数是参与联表的表对应的实体类
,第二个参数是这张表联表的ON 字段
,第三个参数是参与联表的 ON 的另一个实体类
属性除此之外,还可以正常调用mybatis-plus 中的各种原生方法
,文档中还提到,默认主表别名是 t,其他的表别名以先后调用的顺序使用 t1、t2、t3 以此类推。
我们用插件读取日志转化为可读的 sql 语句,可以看到两条左连接条件都被
正确地添加到了 sql 中:
MPJQueryWrapper
public void getOrderSimple() {
List<OrderDto> list = orderMapper.selectJoinList (OrderDto.class,
new MPJQueryWrapper<Order> ()
.selectAll (Order.class)
.select ("t2.unit_price", "t2.name as product_name")
.select ("t1.name as user_name")
.leftJoin ("t_user t1 on t1.id = t.user_id")
.leftJoin ("t_product t2 on t2.id = t.product_id")
.eq ("t.status", "3")
);
list.forEach (System.out::println);
}
运行结果与之前完全相同
,需要注意的是,这样写时在 引用表名时 不要使用数据库中的原表名
,主表默认使用 t
,其他表使用 join 语句中我们为它起的别名
,如果使用原表名在运行中会出现报错。
并且,在 MPJQueryWrapper 中,可以更灵活的支持子查询操作,如果业务比较复杂,那么使用这种方式也是不错的选择
分页查询
mpj 中也能很好的支持列表查询中的分页功能
,首先我们要在项目中加入分页拦截器
:
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor ();
interceptor.addInnerInterceptor (new PaginationInnerInterceptor (DbType.H2));
return interceptor;
}
接下来改造上面的代码,调用
selectJoinPage()
方法:
public void page() {
IPage<OrderDto> orderPage = orderMapper.selectJoinPage (
new Page<OrderDto> (2, 10),
OrderDto.class,
new MPJLambdaWrapper<Order> ()
.selectAll (Order.class)
.select (Product::getUnitPrice)
.selectAs (User::getName, OrderDto::getUserName)
.selectAs (Product::getName, OrderDto::getProductName)
.leftJoin (User.class, User::getId, Order::getUserId)
.leftJoin (Product.class, Product::getId, Order::getProductId)
.orderByAsc (Order::getId));
orderPage.getRecords ().forEach (System.out::println);
}
注意在这里 需要添加一个分页参数的 Page 对象
,我们再执行上面的代码,并对日志进行解析,查看 sql 语句:
可以看到底层 通过添加 limit 进行了分页
,同理,MPJQueryWrapper 也可以这样进行分页