以t_order、t_user、t_product三表联合查询为例,其中t_order表为主表。
0.数据源配置
在springboot项目中,像往常一样正常配置数据源。
1.引入依赖
首先在项目中引入引入依赖坐标,因为mpj
中依赖较高版本mybatis-plus
中的一些api,所以项目建议直接使用高版本。
<dependency>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>https://www.zhihu.com/search?q=mybatis-plus-boot-starter&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22%3A%22answer%22%2C%22sourceId%22%3A2591368778%7D</artifactId>
<version>3.5.1</version>
</dependency>
2.加入分页拦截器
mpj
中也能很好的支持列表查询中的分页功能,首先我们要在项目中加入分页拦截器:
java
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
2.
修改Mapper
把原Mapper
层接口都是继承的BaseMapper
,改为继承MPJBaseMapper
接口。
java
@Mapper
public interface OrderMapper extends MPJBaseMapper<Order> {
}
对其余几个参与联表查询的表的Mapper
接口也进行相同的改造。
3.修改Service
service
也可以选择继承MPJBaseService
,serviceImpl
选择继承MPJBaseServiceImpl
,这两者为非必须继承。
4.注入Mapper
Mapper
接口改造完成后,我们把它注入到各自Service
中,以t_order
作为主表的话,那么只注入这一个对应的OrderMapper
就可以。
java
@Service
@AllArgsConstructor
public class OrderServiceImpl implements OrderService {
private final OrderMapper orderMapper;
}
5.使用MPJLambdaWrapper联表查询
java
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);
}
如果不需要分页,则:
java
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);
}
参数OrderDto.class
代表接收返回查询结果的类,作用和我们之前在xml
中写的resultType
类似。
这个类可以直接继承实体,再添加上需要在关联查询中返回的列即可:
java
@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
以此类推。
另:也可以使用MPJQueryWrapper
和mybatis-plus
非常类似,除了MPJLamdaWrapper
外还提供了普通MPJQueryWrapper
,写法上比MPJLambdaWrapper麻烦
。改造上面的代码:
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 = https://www.zhihu.com/search?q=t.product_id&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22%3A%22answer%22%2C%22sourceId%22%3A2591368778%7D")
.eq("t.status", "3")
);
list.forEach(System.out::println);
}
运行结果与之前完全相同,需要注意的是,这样写时在引用表名时不要使用数据库中的原表名,主表默认使用t
,其他表使用join
语句中我们为它起的别名,如果使用原表名在运行中会出现报错。
并且,在MPJQueryWrapper
中,可以更灵活的支持子查询操作,如果业务比较复杂,那么使用这种方式也是不错的选择。
参考:mybatis-plus的queryWrapper能不能实现表关联? - 知乎
QueryWrapper
类似的,如果不适用mybatis-plus-join来联合查询,也可以直接使用mybatis-plus的QueryWrapper 来写复杂点的SQL查询,用法如MPJQueryWrapper。
例如:
分页拦截器依然需要:
java
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
改造Mapper:
java
@Repository
public interface BlogMapper extends BaseMapper<Blog> {
/**
* 静态查询
*/
@Select("SELECT t_user.user_name " +
" FROM t_blog, t_user " +
" WHERE t_blog.id = #{id} " +
" AND t_blog.user_id = t_user.id")
String findUserNameByBlogId(@Param("id") Long id);
* 动态查询
@Select("SELECT * " +
" ${ew.customSqlSegment} ")
IPage<BlogVO> findBlog(IPage<BlogVO> page, @Param("ew") Wrapper wrapper);
}
改造联合查询:
java
public IPage<BlogVO> dynamicQuery(Page<BlogVO> page, String nickName, String title) {
QueryWrapper<BlogVO> queryWrapper = new QueryWrapper<>();
queryWrapper.like(StringUtils.hasText(nickName), "t_user.nick_name", nickName);
queryWrapper.like(StringUtils.hasText(title), "t_blog.title", title);
queryWrapper.eq("t_blog.deleted_flag", 0);
queryWrapper.eq("t_user.deleted_flag", 0);
queryWrapper.apply("t_blog.user_id = t_user.id");
return blogMapper.findBlog(page, queryWrapper);
}
查看:MyBatis-Plus怎么实现多表联查 - 开发技术 - 亿速云
最后保底的办法
不使用Mybatis-plus,和之前一样直接在xml中写SQL。