基于Lambda查询器快速实现非JDBC通用查询工具

在日常开发中,非JDBC数据源(如OData、REST API等)的查询往往需要重复编写HTTP请求、参数拼接、数据解析逻辑,适配成本高且代码冗余。本文将介绍一款参考MyBatis-Plus设计的Lambda查询器,通过“最少3个类”即可快速扩展非JDBC数据源查询能力,以SAP BW4的OData接口为例,带你体验“一行代码查数据”的快捷性。

一、Lambda查询器核心设计思路

Lambda查询器的核心是通过抽象封装+开放接口,实现“数据源适配与查询逻辑解耦”,整体架构分为三层:

  1. BaseDao抽象层:定义通用查询方法(list、getFirst、page等),屏蔽查询逻辑差异;
  2. DaoFactory工厂层:管理数据源配置(如地址、认证信息),统一创建Dao实例;
  3. QueryBuilder构建层:根据Lambda表达式生成数据源对应的查询语句(如OData的$filter)。

只需针对特定数据源实现上述三层的具体逻辑,即可接入Lambda查询能力,无需重复编写通用代码。

二、SAP BW4 OData接口适配实现(仅需3个类)

本节将完整展示如何通过3个核心类,实现SAP BW4 OData接口的Lambda查询适配,全程无需编写冗余的HTTP请求与解析代码。

1. 实现Dao层:SapBw4Dao

继承BaseDao抽象类,实现SAP BW4 OData接口的具体查询逻辑(HTTP请求发送、响应解析、异常处理)。核心是重写buildQuery(生成OData查询语句)和execSapBw4(执行HTTP请求)方法。

@Slf4j
public class SapBw4Dao<T> extends BaseDao<T, String> {
    private String url; // BW4 OData接口地址
    private String username; // 认证用户名
    private String pwd; // 认证密码

    // 构造方法:传入实体类、数据源配置
    public SapBw4Dao(Class<T> entityCls, String url, String username, String pwd) {
        super(entityCls);
        this.url = url;
        this.username = username;
        this.pwd = pwd;
    }

    // 生成OData的$filter查询语句(委托给SapBw4QueryBuilder)
    @Override
    public String buildQuery() {
        QueryInfo queryInfo = baseWhereQuery.buildQueryInfo();
        return SapBw4QueryBuilder.build(entityCls, queryInfo);
    }

    // 核心查询方法:执行HTTP请求并解析结果
    @Override
    public List<T> list() {
        String filter = buildQuery();
        return execSapBw4(filter, null, null);
    }

    // 分页查询实现(适配OData的$skip/$top参数)
    @Override
    public IJPage<T> page(IJPage<T> page) {
        int top = page.getPageSize();
        int skip = (page.getPageNo() - 1) * page.getPageSize();
        String query = buildQuery();
        List<T> list = execSapBw4(query, skip, top);
        page.setData(list);
        return page;
    }

    // 内部方法:发送HTTP请求到BW4 OData接口
    private List<T> execSapBw4(String filter, Integer skip, Integer top) {
        // 1. 构建HTTP请求(基础认证+JSON格式)
        HttpRequest request = HttpUtil.createGet(url)
                .basicAuth(username, pwd);
        request.form("$format", "json");
        
        // 2. 拼接查询参数($filter/$skip/$top)
        if (StrUtil.isNotBlank(filter)) request.form("$filter", filter);
        if (skip != null) request.form("$skip", skip);
        if (top != null) request.form("$top", top);

        // 3. 执行请求并解析JSON结果
        try (HttpResponse httpResponse = request.execute()) {
            log.info("BW4 OData请求地址:{}", request.getUrl());
            if (httpResponse.getStatus() == 200) {
                String body = httpResponse.body();
                JSONObject jsonObject = JSON.parseObject(body);
                JSONArray results = jsonObject.getJSONObject("d").getJSONArray("results");
                log.info("BW4查询结果数量:{}", results.size());
                return results.toJavaList(entityCls); // 直接转为实体类列表
            } else {
                throw new RRException("BW4请求失败,状态码:" + httpResponse.getStatus());
            }
        } catch (Exception e) {
            log.error("BW4查询异常", e);
            throw new RRException("BW4查询异常:" + e.getMessage());
        }
    }

    // 未实现的方法(根据业务需求选择性实现)
    @Override public T getFirst() { /* 已实现,见上文 */ }
    @Override public Long count() { throw new UnsupportedOperationException(); }
    @Override public T findById(Serializable id) { throw new UnsupportedOperationException(); }
    // 其他增删改方法按需实现...
}

2. 实现工厂层:SapBw4DaoFactory

管理SAP BW4的数据源认证信息(按实体类隔离),统一创建SapBw4Dao实例,避免硬编码配置。

public class SapBw4DaoFactory extends DaoFactory {
    // 存储“实体类→BW4账号”的映射(线程安全)
    private Map<Class<?>, Bw4Account> accountMap = new ConcurrentHashMap<>();

    // 构造方法:指定Dao实现类为SapBw4Dao
    public SapBw4DaoFactory() {
        super(SapBw4Dao.class);
    }

    // 外部调用:添加实体类对应的BW4认证信息
    public void addAccount(Class<?> entity, Bw4Account bw4Account) {
        accountMap.put(entity, bw4Account);
    }

    // 重写:创建SapBw4Dao实例(自动注入认证信息)
    @Override
    public <T> BaseDao<T, ?> createDao(Class<T> entityCls) {
        Bw4Account account = accountMap.get(entityCls);
        if (account == null) {
            throw new RRException("实体类[" + entityCls.getName() + "]未配置BW4认证信息");
        }
        // 传入实体类、OData地址、用户名、密码,创建Dao实例
        return new SapBw4Dao<>(entityCls, account.getPath(), account.getUserName(), account.getPassword());
    }
}

其中Bw4Account为简单的实体类,用于封装BW4的认证信息(路径、用户名、密码),此处省略代码。

3. 实现构建层:SapBw4QueryBuilder

将Lambda表达式转化为SAP BW4 OData接口支持的$filter查询语句(如KURST eq 'E' and FCURR eq 'CNY')。

public class SapBw4QueryBuilder {
    // 缓存“实体类→字段映射”(避免重复反射)
    static ConcurrentHashMap<Class<?>, Map<String, String>> entityMap = new ConcurrentHashMap<>();

    // 核心方法:生成OData的$filter语句
    public static String build(Class<?> entityCls, QueryInfo queryInfo) {
        StringBuilder sb = new StringBuilder();

        // 1. 处理AND条件(拼接多个条件为“a and b and c”)
        String andConditions = queryInfo.getAnd().stream()
                .map(it -> buildFilterItem(entityCls, it))
                .filter(Objects::nonNull)
                .collect(Collectors.joining(" and "));
        sb.append(andConditions);

        // 2. OR条件(按需实现,本文暂不支持)
        if (CollUtil.isNotEmpty(queryInfo.getOr())) {
            throw new UnsupportedOperationException("BW4 OData暂未实现OR查询");
        }

        return sb.toString();
    }

    // 构建单个条件(如“KURST eq 'E'”)
    private static String buildFilterItem(Class<?> entityCls, QueryItem queryItem) {
        // 1. 将实体类字段名转为BW4 OData字段名(如getType()→KURST)
        String odataField = buildField(entityCls, queryItem.getField());

        // 2. 处理运算符(eq/ne/gt等,适配OData语法)
        for (Map.Entry<WhereOperation, Object> entry : queryItem.getVal().entrySet()) {
            switch (entry.getKey()) {
                case eq: return StrUtil.format("{} eq '{}'", odataField, entry.getValue());
                case ne: return StrUtil.format("{} ne '{}'", odataField, entry.getValue());
                case gt: return StrUtil.format("{} gt '{}'", odataField, entry.getValue());
                case lt: return StrUtil.format("{} lt '{}'", odataField, entry.getValue());
                case gte: return StrUtil.format("{} ge '{}'", odataField, entry.getValue());
                case lte: return StrUtil.format("{} le '{}'", odataField, entry.getValue());
                default: throw new UnsupportedOperationException("不支持的运算符:" + entry.getKey());
            }
        }
        return null;
    }

    // 映射实体类字段到OData字段(基于@JSONField注解)
    public static String buildField(Class<?> entityCls, FieldInfo fieldInfo) {
        Map<String, String> fieldMap = entityMap.computeIfAbsent(entityCls, k -> {
            Map<String, String> map = new ConcurrentHashMap<>();
            // 反射获取实体类字段,解析@JSONField注解(如@JSONField(name="KURST"))
            Field[] fields = ReflectUtil.getFields(entityCls);
            for (Field field : fields) {
                JSONField jsonField = AnnotationUtil.getAnnotation(field, JSONField.class);
                if (jsonField != null && StrUtil.isNotBlank(jsonField.name())) {
                    map.put(field.getName(), jsonField.name());
                } else {
                    map.put(field.getName(), field.getName()); // 无注解则用字段名本身
                }
            }
            return map;
        });

        // 返回OData字段名(支持一级字段,暂不支持嵌套)
        String odataField = fieldMap.getOrDefault(fieldInfo.getField(), StrUtil.toUnderlineCase(fieldInfo.getField()));
        if (fieldInfo.getSubList().size() > 0) {
            throw new LambdaQueryException("BW4 OData暂不支持嵌套字段查询");
        }
        return odataField;
    }
}

三、实战使用:3步实现BW4数据查询

完成上述3个类的适配后,只需3步即可通过Lambda表达式查询SAP BW4数据,代码简洁且直观。

1. 定义查询实体类

通过@JSONField注解映射BW4 OData的字段名,@DaoSelect指定使用SapBw4Dao。

@NoArgsConstructor
@Data
@DaoSelect(daoCls = SapBw4Dao.class) // 指定该实体类使用的Dao
public class BwRmbUsd {
    /** 汇率类型(BW4字段:KURST) */
    @JSONField(name = "KURST")
    private String type;

    /** 源货币(BW4字段:FCURR) */
    @JSONField(name = "FCURR")
    private String source;

    /** 目标货币(BW4字段:TCURR) */
    @JSONField(name = "TCURR")
    private String target;

    /** 有效期(BW4字段:GDATU) */
    @JSONField(name = "GDATU")
    private String expires;

    /** 汇率(BW4字段:UKURS) */
    @JSONField(name = "UKURS")
    private String rate;

    // 其他字段...
}

2. 配置BW4认证信息

在项目初始化时,通过SapBw4DaoFactory注入实体类对应的BW4 OData地址和认证信息(避免硬编码)。

@Component
public class Bw4Config {
    @Autowired
    @Lazy // 延迟加载,避免循环依赖
    private SapBw4DaoFactory sapBw4DaoFactory;

    // 项目启动时初始化认证信息
    @PostConstruct
    private void initBw4Account() {
        // 1. 构建BW4认证信息(实际项目中建议从配置文件读取)
        Bw4Account rmbUsdAccount = new Bw4Account();
        rmbUsdAccount.setPath("https://xxx.sap.com/odata/bw/rmb_usd"); // BW4 OData地址
        rmbUsdAccount.setUserName("bw_user"); // 用户名
        rmbUsdAccount.setPassword("bw_pwd"); // 密码

        // 2. 绑定“实体类→认证信息”
        sapBw4DaoFactory.addAccount(BwRmbUsd.class, rmbUsdAccount);
    }
}

3. 执行Lambda查询

通过JQuerys.lambdaQuery()方法,结合Lambda表达式编写查询条件,一行代码即可获取结果,无需关注HTTP请求细节。

示例1:查询所有数据
// 查询BwRmbUsd的所有数据
List<BwRmbUsd> allList = JQuerys.lambdaQuery(BwRmbUsd.class).list();
示例2:多条件精准查询
// 查询“汇率类型=E、源货币=CNY、目标货币=USD”的数据
List<BwRmbUsd> rateList = JQuerys.lambdaQuery(BwRmbUsd.class)
        .eq(BwRmbUsd::getType, "E")       // 汇率类型=E
        .eq(BwRmbUsd::getSource, "CNY")  // 源货币=CNY
        .eq(BwRmbUsd::getTarget, "USD")  // 目标货币=USD
        .list(); // 执行查询并返回列表
示例3:分页查询
// 分页查询:第1页,每页10条数据
IJPage<BwRmbUsd> page = JQuerys.lambdaQuery(BwRmbUsd.class)
        .eq(BwRmbUsd::getType, "E")
        .page(new IJPage<>(1, 10)); // 页码从1开始,每页10条
List<BwRmbUsd> pageData = page.getData(); // 分页数据
long total = page.getTotal(); // 总条数(需BW4支持$count,本文暂未实现)

四、快捷性核心体现

相比传统的非JDBC查询开发,Lambda查询器的快捷性主要体现在3个方面:

  1. 最少代码扩展:新增数据源仅需实现3个核心类,无需重复编写HTTP请求、参数拼接、数据解析逻辑;
  2. Lambda语法直观:查询条件通过Lambda表达式编写(如BwRmbUsd::getType),避免字符串硬编码(如"KURST eq 'E'"),降低拼写错误风险;
  3. 统一查询风格:无论适配OData、REST API还是其他协议,查询调用方式完全一致(如lambdaQuery().eq().list()),降低跨数据源开发的学习成本。

五、总结与扩展

本文以SAP BW4 OData接口为例,展示了Lambda查询器如何快速实现非JDBC数据源的通用查询能力。其核心优势在于“解耦数据源适配与查询逻辑”,通过抽象封装让开发者聚焦业务查询条件,而非底层技术细节。

后续可基于该框架扩展更多数据源,如:

  • 适配Elasticsearch:实现EsDaoEsDaoFactoryEsQueryBuilder
  • 适配REST API:实现RestDao,支持自定义请求头、参数格式;
  • 扩展查询能力:增加likeinorderBy等条件支持。

通过Lambda查询器,可大幅提升非JDBC数据源的开发效率,让“一行代码查数据”成为常态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值