版权说明: 本文由博主keep丶原创,转载请注明出处。
原文地址: https://blog.csdn.net/qq_38688267/article/details/112621293
开发环境: MyBatis-Plus3.4.1

场景说明

  简单来说,我们系统中许多数据都是树状结构的,所以我定义了一个实体类父类BaseTreePO,并且想封装一个通用的树状对象的Service类,部分代码如下:

public interface TreeService<T extends BaseTreePO> extends IService<T> {

    default String getCurrentMaximumChildPath(T entity, String parentPath) {
        LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<T>()
                .orderByDesc(T::getPath);//在这里通过T::getPath获取字段名时报错

        if (StrUtil.isEmpty(parentPath)) {
            wrapper.isNull(T::getParentPath);
        } else {
            wrapper.eq(T::getParentPath, parentPath);
        }

        T maxPathChild = this.getBaseMapper().selectOne(this.doAppend(wrapper, entity));
        return ObjectUtil.isEmpty(maxPathChild) ? null : maxPathChild.getPath();
    }
}


  如上述代码所示,在执行T::getPath时报错,报错堆栈信息如下:

com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: can not find lambda cache for this entity [com.copm.ifm.base.basic.pojo.BaseTreePO]
	at com.baomidou.mybatisplus.core.toolkit.ExceptionUtils.mpe(ExceptionUtils.java:49)
	at com.baomidou.mybatisplus.core.toolkit.Assert.isTrue(Assert.java:38)
	at com.baomidou.mybatisplus.core.toolkit.Assert.notNull(Assert.java:72)
	at com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper.tryInitCache(AbstractLambdaWrapper.java:94)
	at com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper.getColumn(AbstractLambdaWrapper.java:79)
	at com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper.columnToString(AbstractLambdaWrapper.java:62)
	at com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper.columnToString(AbstractLambdaWrapper.java:58)
	at com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper.columnToString(AbstractLambdaWrapper.java:38)
	at com.baomidou.mybatisplus.core.conditions.AbstractWrapper.lambda$orderBy$82c52469$1(AbstractWrapper.java:310)
	at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
	at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
	at com.baomidou.mybatisplus.core.conditions.segments.OrderBySegmentList.transformList(OrderBySegmentList.java:37)
	at com.baomidou.mybatisplus.core.conditions.segments.AbstractISegmentList.addAll(AbstractISegmentList.java:60)
	at com.baomidou.mybatisplus.core.conditions.segments.MergeSegments.add(MergeSegments.java:50)
	at com.baomidou.mybatisplus.core.conditions.AbstractWrapper.doIt(AbstractWrapper.java:469)
	at com.baomidou.mybatisplus.core.conditions.AbstractWrapper.orderBy(AbstractWrapper.java:310)
	at com.baomidou.mybatisplus.core.conditions.AbstractWrapper.orderBy(AbstractWrapper.java:47)
	at com.baomidou.mybatisplus.core.conditions.interfaces.Func.orderByDesc(Func.java:264)
	at com.baomidou.mybatisplus.core.conditions.interfaces.Func.orderByDesc(Func.java:245)
	at com.copm.ifm.base.service.TreeService.getCurrentMaximumChildPath(TreeService.java:161)

解决方案

  • 第一种: 给对应的Wrapper指定对象类型,使用AbstractWrapper#setEntityClass(Class)方法:
        LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<T>()
        		.setEntityClass((Class<T>) entity.getClass())//指定实体类类型
                .orderByDesc(T::getPath);
		
		//或
		LambdaQueryWrapper<T> wrapper = 
				new LambdaQueryWrapper<>((Class<T>)entity.getClass());

		//或
		//这种慎用!!!因为该wrapper的条件中会加入entity中有值的属性值
		//即如果entity.getId() = 1,则wrapper的条件中就会增加 and id = 1的条件。
		LambdaQueryWrapper<T> wrapper = 
				new LambdaQueryWrapper<>(entity);

这种方案需要每个相关Wrapper都指定,如果要一劳永逸的方式请使用第二种:

  • 第二种:给对应的父类也单独增加一个Mapper类即可:
package com.copm.ifm.base.service.base.mapper;

import com.baomidou.mybatisplus.core.MybatisMapperAnnotationBuilder;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.copm.ifm.base.basic.pojo.BaseTreePO;

/**
 * 在使用存在父类的泛型的Lambda表达式时会报错:
 * {@code MybatisPlusException: can not find lambda cache for this entity [com.copm.ifm.base.basic.pojo.BaseTreePO]}
 * <p>
 * 原因是在执行{@link com.baomidou.mybatisplus.core.toolkit.LambdaUtils#getColumnMap(Class)}时
 * {@code COLUMN_CACHE_MAP}中没有{@link BaseTreePO}的信息
 * <p>
 * 根据源码
 * {@link com.baomidou.mybatisplus.core.MybatisMapperRegistry#addMapper(Class)}
 * {@link com.baomidou.mybatisplus.core.MybatisMapperAnnotationBuilder#parse()}
 * {@link com.baomidou.mybatisplus.core.MybatisMapperAnnotationBuilder#parsePendingMethods()}
 * {@link com.baomidou.mybatisplus.core.injector.AbstractSqlInjector#inspectInject(MapperBuilderAssistant, Class)}
 * {@link com.baomidou.mybatisplus.core.metadata.TableInfoHelper#initTableInfo(Configuration, String, Class)}
 * {@link com.baomidou.mybatisplus.core.toolkit.LambdaUtils#installCache(TableInfo)}
 * 方法的加载逻辑
 * <p>
 * 他会将所有扫描到的mapper中的泛型({@link BaseMapper<Class>}中的Class,即实体类)的字段信息缓存到
 * {@link com.baomidou.mybatisplus.core.toolkit.LambdaUtils}中的{@code COLUMN_CACHE_MAP}中。
 * 但是没有单独缓存父类的信息,所以{@code COLUMN_CACHE_MAP}中没有相关缓存,就报错了。
 * <p>
 * 因此我们单独为{@link BaseTreePO}添加一个的Mapper类,这样他就会缓存该类的信息了。
 * <p>
 * 另外一个解决方案是给相关Wrapper指定泛型类型,告诉mp让他加载子类的字段信息,也可以解决该问题:
 * 使用{@link com.baomidou.mybatisplus.core.conditions.AbstractWrapper#setEntityClass(Class)}
 *
 */
public interface BaseTreeMapper extends BaseMapper<BaseTreePO> {
}

  具体原因也写在了代码的注释上,这样别人在看到这个类的时候也知道是怎么回事了。

  需要注意的是,我这里是没有加@Mapper注解的,因为我在启动类上加了@MapperScan注解。加了@MapperScan就不需要给每个Mapper类单独加@Mapper了,如果你没有加@MapperScan,则需要给他加上@Mapper

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐