You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让QueryBuilder查询无结果时返回默认实体?

解决方案思路

1. 装饰器模式包装Dao层(推荐)

创建通用Dao装饰类,统一拦截unique()调用,处理无结果异常并返回默认实体。这种方式无需修改原有Dao代码,仅需替换注入的Dao实例为装饰后的版本,侵入性极低。

代码示例:

首先定义默认实体生成接口,让所有需要默认值的实体实现它:

public interface Defaultable<T> {
    T getDefaultInstance();
}

// 实体类实现接口,定义自身默认值
public class MyEntity implements Defaultable<MyEntity> {
    // 原有字段定义...
    
    @Override
    public MyEntity getDefaultInstance() {
        MyEntity defaultEntity = new MyEntity();
        defaultEntity.setId(-1);
        defaultEntity.setName("默认内容");
        // 补充其他字段的默认值
        return defaultEntity;
    }
}

实现Dao装饰类,包装原QueryBuilder的unique()方法:

public class DefaultableDaoDecorator<T extends Defaultable<T>, DAO extends AbstractDao<T, Long>> implements DAO {
    private final DAO delegateDao;

    public DefaultableDaoDecorator(DAO delegateDao) {
        this.delegateDao = delegateDao;
    }

    @Override
    public QueryBuilder<T> queryBuilder() {
        return new DefaultableQueryBuilder<>(delegateDao.queryBuilder());
    }

    // 其他Dao方法直接委托给原Dao实现
    @Override
    public void insert(T entity) {
        delegateDao.insert(entity);
    }

    // 按需重写其他需要处理的Dao方法...

    // 自定义QueryBuilder,重写unique()做统一异常处理
    private static class DefaultableQueryBuilder<T extends Defaultable<T>> extends QueryBuilder<T> {
        private final QueryBuilder<T> delegateQuery;

        public DefaultableQueryBuilder(QueryBuilder<T> delegateQuery) {
            super(delegateQuery.getDao());
            this.delegateQuery = delegateQuery;
        }

        @Override
        public T unique() {
            try {
                return delegateQuery.unique();
            } catch (DaoException e) {
                // 判断是否为无结果异常(匹配GreenDao无结果时的异常信息)
                if (e.getMessage() != null && e.getMessage().contains("No entity found for query")) {
                    try {
                        return getDao().getEntityClass().newInstance().getDefaultInstance();
                    } catch (InstantiationException | IllegalAccessException ex) {
                        throw new RuntimeException("创建默认实体失败", ex);
                    }
                }
                // 非无结果异常,继续抛出
                throw e;
            }
        }
    }
}

使用时替换原Dao实例:

// 获取原始Dao
MyEntityDao originalDao = daoSession.getMyEntityDao();
// 包装为带默认值返回的Dao
MyEntityDao defaultableDao = new DefaultableDaoDecorator<>(originalDao);

// 调用方式与原有代码完全一致,无结果时自动返回默认实体
MyEntity entity = defaultableDao.queryBuilder().where(MyEntityDao.Properties.Id.eq(100)).unique();

2. 泛型工具类封装查询逻辑

如果不想包装Dao,可创建通用工具类,封装带默认返回的查询逻辑,所有查询通过工具类调用即可。

代码示例:

public class QueryUtils {
    public static <T extends Defaultable<T>> T queryWithDefault(QueryBuilder<T> queryBuilder) {
        try {
            return queryBuilder.unique();
        } catch (DaoException e) {
            if (e.getMessage() != null && e.getMessage().contains("No entity found for query")) {
                try {
                    return queryBuilder.getDao().getEntityClass().newInstance().getDefaultInstance();
                } catch (InstantiationException | IllegalAccessException ex) {
                    throw new RuntimeException("创建默认实体失败", ex);
                }
            }
            throw e;
        }
    }
}

使用方式:

MyEntity entity = QueryUtils.queryWithDefault(
    myDao.queryBuilder().where(MyEntityDao.Properties.Id.eq(100))
);

3. 自定义Dao基类(适合新项目)

若项目处于初期阶段,可让所有Dao继承自定义基类,在基类中重写queryBuilder()方法,返回自定义QueryBuilder统一处理unique()逻辑。

代码示例:

public abstract class DefaultableBaseDao<T extends Defaultable<T>, K> extends AbstractDao<T, K> {
    protected DefaultableBaseDao(DaoConfig config) {
        super(config);
    }

    @Override
    public QueryBuilder<T> queryBuilder() {
        return new DefaultableQueryBuilder<>(super.queryBuilder());
    }

    private static class DefaultableQueryBuilder<T extends Defaultable<T>> extends QueryBuilder<T> {
        private final QueryBuilder<T> delegate;

        public DefaultableQueryBuilder(QueryBuilder<T> delegate) {
            super(delegate.getDao());
            this.delegate = delegate;
        }

        @Override
        public T unique() {
            try {
                return delegate.unique();
            } catch (DaoException e) {
                if (e.getMessage() != null && e.getMessage().contains("No entity found for query")) {
                    try {
                        return getDao().getEntityClass().newInstance().getDefaultInstance();
                    } catch (Exception ex) {
                        throw new RuntimeException("创建默认实体失败", ex);
                    }
                }
                throw e;
            }
        }
    }
}

后续生成Dao时,指定继承该基类(可通过GreenDao的生成配置设置)即可实现全局统一处理。


注意事项

  • 异常判断:GreenDao无结果时抛出的DaoException,可通过异常信息中的关键词No entity found for query识别,确保仅处理无结果场景,其他异常正常抛出。
  • 默认实体创建:通过getEntityClass().newInstance()创建实例时,需确保实体类存在无参构造函数,否则会抛出初始化异常。
  • 场景适配:装饰器模式适合已有大量Dao实现的老项目,自定义基类适合新项目统一规范,工具类则是轻量化的折中方案。

内容的提问来源于stack exchange,提问作者Jacks

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 18:05:22