mybatis plus 使用 lambda 方式 demon

1.查询
通过 字段匹配查询,并按照 时间排序

baseMapper.selectList(
        new QueryWrapper<SupplyEntity>()
                .lambda()
                .eq(SupplyEntity::getCardId, cardBasicInfoVM.getId())
                .orderByDesc(SupplyEntity::getCreateTime));
entryStatService.delete(
        new QueryWrapper<CardEntryStat>()
        .lambda()
        .eq(CardEntryStat::getShareType, shareType)

3 update

 cardGroupRefService.update(cardGroupREntity,
        new QueryWrapper<CardGroupREntity>().lambda().eq(CardGroupREntity::getGroupId, groupId));

4 计算count

int count = this.selectCount(new QueryWrapper<SupplyEntity>().lambda()
                .eq(SupplyEntity::getCardId,cardId)
                .eq(SupplyEntity::getType,type));

遇到问题 BuilderException

错误日志如下:

Caused by: org.apache.ibatis.builder.BuilderException: 
Error evaluating expression 'ew!=null and !ew.emptyOfWhere'. 
Cause: org.apache.ibatis.ognl.OgnlException: emptyOfWhere [com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: 
该模式不能应用于非 baseMapper 的泛型 entity 之外的 entity!]

查询用到的实体类如下
猜测可能和父类有关系,父类只有一个 id 属性。

@TableName("group")
@Getter
@Setter
@NoArgsConstructor
public class GroupEntity extends SuperEntity<GroupEntity> {
    private String userId;
    private String name;
    private String createBy;

//我们报错的删除方法

public boolean delete( String id ,String userId) {
        // TODO: 2019/3/4 bug 删除报错
        this.delete(new QueryWrapper<GroupEntity>().lambda()
                .eq(GroupEntity::getId,id)
                .eq(GroupEntity::getCreateBy, id)
        return true;

经过测试 发现改动如下就OK 了

public boolean delete( String id ,String userId) {
        this.delete(new QueryWrapper<GroupEntity>().lambda()
                .eq(GroupEntity::getCreateBy, id)
                .eq(GroupEntity::getId,id)
        return true;

好神奇,那我们来分析下是什么原因呢?
通过报异常的位置定位到源码如下

@SuppressWarnings("serial")
public abstract class AbstractLambdaWrapper<T, This extends AbstractLambdaWrapper<T, This>>
    extends AbstractWrapper<T, Property<T, ?>, This> implements Serializable {
    private Map<String, String> columnMap = null;
    private boolean initColumnMap = false;
    @Override
    protected String columnToString(Property<T, ?> column) {
        return getColumn(LambdaUtils.resolve(column));
    private String getColumn(SerializedLambda lambda) {
        if (!initColumnMap) {
            String entityClassName = lambda.getImplClass().replace("/", ".");
            columnMap = LambdaUtils.getColumnMap(entityClassName);
            if (CollectionUtils.isEmpty(columnMap)) {
                throw new MybatisPlusException("该模式不能应用于非 baseMapper 的泛型 entity 之外的 entity!");
            initColumnMap = true;
        return Optional.ofNullable(columnMap.get(StringUtils.resolveFieldName(lambda.getImplMethodName())))
            .orElseThrow(() -> new MybatisPlusException("该模式不能应用于非数据库字段!"));

可以看到 if (!initColumnMap) 这一行去判断有没有初始化 column map
如果没有初始化的话 会通过反射去找到 实体类的名称,
而我们 new 的 QueryWrapper中 第一个 eq 判断

.eq(GroupEntity::getCreateBy, id)

用到的字段是 id ,这个字段 id是父类SuperEntity的属性,所以反射时候拿到实体的类是 父类了。
导致了这个的bug。
如何规避这个bug呢?

if (!initColumnMap)

这个判断ColumnMap 一个查询只会初始一次,所以我们调换 查询条件,先去判断子类的属性,这样
就会反射为子类的实体,就可以执行成功了。

新的版本已经修复了此问题

private String getColumn(SerializedLambda lambda, boolean onlyColumn) {
        String fieldName = StringUtils.resolveFieldName(lambda.getImplMethodName());
        if (!initColumnMap || !columnMap.containsKey(fieldName.toUpperCase(Locale.ENGLISH))) {
            String entityClassName = lambda.getImplClassName();
            columnMap = LambdaUtils.getColumnMap(entityClassName);
            Assert.notEmpty(columnMap, "cannot find column's cache for \"%s\", so you cannot used \"%s\"!",
                entityClassName, typedThis.getClass());
            initColumnMap = true;
        return Optional.ofNullable(columnMap.get(fieldName.toUpperCase(Locale.ENGLISH)))
            .map(onlyColumn ? ColumnCache::getColumn : ColumnCache::getColumnSelect)