当前位置: 首页 > 科技观察

最后还有一个工具可以弥补MyBatis-Plus联表查询的不足

时间:2023-03-21 15:33:45 科技观察

大家好,我是Hydra。Mybatis-plus作为mybatis的增强工具,大大简化了开发中的数据库操作,但是长期以来,其联表查询能力一直为大家所诟病。一旦遇到leftjoin或者rightjoin,还是老老实实打开xml文件,手写一大段sql语句。直到前几天,偶然间接触到这样一个叫做mybatis-plus-join(以下简称mpj)的工具。用过之后不得不说真的很好吃!彻底将我从xml地狱中解放出来,终于可以像mybatis-plus中的QueryWrapper一样进行联表查询了。话不多说,下面就开始体验吧。引入依赖首先引入项目中的依赖坐标,因为mpj依赖高版本mybatis-plus中的一些API,所以项目推荐直接使用高版本。com.github.yulichangmybatis-plus-join1.2.4com.baomidoumybatis-plus-boot-starter3.5.1引入相关依赖后,在springboot项目中,照常配置数据源连接信息会做。数据准备就是实现联表查询,我们先建几张表测试一下。订单表:用户表,包括用户名:产品表,包括产品名称和单价:在订单表中,通过用户id和产品id关联其他两个表。修改Mapper以往使用myatis-plus时,我们的Mapper层接口都是直接继承自BaseMapper。使用mpj后,需要修改继承MPJBaseMapper接口。@MapperpublicinterfaceOrderMapperextendsMPJBaseMapper{}对其余两张表的Mapper接口进行同样的改造。另外我们的服务也可以选择继承MPJBaseService,serviceImpl可以选择继承MPJBaseServiceImpl,这两个都是可选的。QueryMapper接口改造完成后,我们将其注入到Service中。虽然我们要完成三张表的联表查询,但是如果Order作为主表,那么只能注入对应的OrderMapper。很简单。@Service@AllArgsConstructorpublicclassOrderServiceImplimplementsOrderService{privatefinalOrderMapperorderMapper;}MPJLambdaWrapper下面来体验一下不再需要写sql的联表查询:publicvoidgetOrder(){Listlist=orderMapper.selectJoinList(OrderDto.类,新MPJLambdaWrapper().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)publicclassOrderDtoextendsOrder{StringuserName;字符串产品名称;DoubleunitPrice;}接下来MPJLambdaWrapper是构造查询条件的核心。来看看我们上面使用的方法:selectAll():查询指定实体类的所有字段select():查询指定字段,可以支持变长参数同时查询多个字段,但是同一个select只能查询同一个表的字段,所以如果查询多个表的字段,需要单独写selectAs():字段别名查询,用于数据库字段和接收结果时dto中的属性名不一致,转成leftJoin():leftjoin,其中第一个参数是参与联表的表对应的实体类,第二个参数是这个表联表的ON字段,第三个参数是另一个参与联合表的ON的实体类属性。另外mybatis-plus中的各种native方法都可以正常调用。文档中还提到,默认的主表别名是t,其他表的别名按照调用顺序使用t1、t2、t3等。我们使用插件读取日志,转换成可读的sql语句。我们可以看到左边的两个join条件已经正确添加到sql中了:MPJQueryWrapper和mybatis-plus非常相似。除了LamdaWrapper,它还提供了普通QueryWrapper的写法,修改以上代码:class).select("t2.unit_price","t2.nameasproduct_name").select("t1.nameasuser_name").leftJoin("t_usert1ont1.id=t.user_id").leftJoin("t2上的t_productt2.id=t.product_id").eq("t.status","3"));list.forEach(System.out::println);}运行结果和之前完全一样。需要注意的是,这样写的时候,不要引用表名使用数据库中原来的表名,主表默认使用t,其他表使用我们在join语句中给它起的别名。如果使用原来的表名,运行时会报错。而且在MPJQueryWrapper中,可以更加灵活的支持子查询操作。如果业务比较复杂,那么使用这种方式也是一个不错的选择。分页查询mpj也可以很好的支持列表查询中的分页功能。首先,我们需要在项目中添加一个分页拦截器:@BeanpublicMybatisPlusInterceptormybatisPlusInterceptor(){MybatisPlusInterceptorinterceptor=newMybatisPlusInterceptor();interceptor.addInnerInterceptor(newPaginationInnerInterceptor(DbType.H2));returninterceptor;}接着,对上面的代码进行改造,调用selectJoinPage()方法:publicvoidpage(){IPageorderPage=orderMapper.selectJoinPage(newPage(2,10),OrderDto.class,newMPJLambdaWrapper().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语句:可以通过添加l查看底层imit进行了分页。同样,MPJQueryWrapper也可以通过这种方式进行分页。最后,经过简单的测试,个人觉得mpj是一个在联表查询方面比较实用的工具,能够更好的应对不是很复杂的项目。sql查询,大大提高了我们的生产效率。当然,在项目的issues中,我们也可以看到当前版本还存在一些问题。希望能在后续的版本迭代中不断完善。