2026/3/27 15:35:48
网站建设
项目流程
可以做推广的网站有哪些,商城网站如何搭建,网站开发招聘职位,设计网站会员数据访问层#xff08;DAL#xff09;技术已经非常成熟#xff0c;从最早的 JDBC 到 Hibernate、MyBatis#xff0c;再到 Spring Data JPA。我们习惯了用这些框架处理数据。然而#xff0c;当我们把目光投向“数据”本身的变化时#xff0c;会发现这些经典的框架似乎正逐…数据访问层DAL技术已经非常成熟从最早的 JDBC 到 Hibernate、MyBatis再到 Spring Data JPA。我们习惯了用这些框架处理数据。然而当我们把目光投向“数据”本身的变化时会发现这些经典的框架似乎正逐渐变为“老一代”。新一代的挑战不再仅仅是如何优雅地写 SQL而是如何用统一的方式访问那些不再仅仅存储在关系型数据库中的数据。一、老旧的一代谈论 “老一代” 数据访问库并非贬义而是指它们诞生的时代背景和核心使命。在过去以及现在MyBatis、Hibernate、JPA (Hibernate)、Spring JDBC Template以及Apache Commons DbUtils统治了 Java 开发者的工具箱。它们的共同特征非常明显关系型数据库它们的设计初衷就是为了更好地操作 Oracle, MySQL, PostgreSQL 等数据库。核心逻辑无论是 ORM 映射还是 SQL 模板都紧紧围绕着 SQL 标准。专有性当 NoSQL 兴起后这些框架显得力不从心。于是我们看到了MongoDB Java Driver、Elasticsearch RestHighLevelClient等专有 SDK。这种格局导致了一个现象要么专有要么偏向纯关系型数据库。如果你的应用既要查 MySQL 也要查 Elasticsearch你通常需要引入两套完全不同的技术栈写两套风格迥异的代码。二、破旧尝试数据库技术一直在不断的迭代文档型数据库MongoDB、搜索引擎Elasticsearch/OpenSearch、键值存储Redis、时序数据库乃至现在的向量数据库蜂拥而至。面对这些 “新东西”我们熟悉的 “老配方” 也在尝试去解决新问题。于是我们看到了一系列试图弥合裂痕的动作Easy-ES试图用 MyBatis-Plus 的习惯去操作 Elasticsearch让开发者像操作数据库表一样操作索引。Hibernate OGM试图将 JPA 标准延续到 NoSQL 领域用注解映射非关系型数据。Spring Data通过统一的 Repository 接口抽象试图掩盖底层实现的差异如JpaRepository与MongoRepository。然而这些努力虽然在一定程度上缓解了问题但依然难以掩盖核心的困境套用 SQL 思维 SQL 是关系型数据库的通用语但对于具有嵌套结构、倒排索引或图关系的数据强行套用 SQL 或表格思维。 像 Easy-ES 这样的工具虽然方便但在处理 ES 特有的聚合或复杂 DSL 时往往还是需要回退到原生 QueryDSL。中间件对 JDBC 的态度 JDBC 本是 Java 届最成功的抽象之一但它被打上了深深的关系型数据库烙印。Elasticsearch曾经尝试提供 JDBC 支持但限制诸多不支持嵌套对象复杂查询甚至一度计划废弃 SQL 插件。MongoDB虽然有商业版的 JDBC 驱动但社区生态中大家更习惯用 MongoTemplate 或原声 BSON API。API 接口层面的持续割裂 尽管有 Spring Data 这样的封装但底层的割裂依然存在。关系型数据库mapper.selectById(id)MongoDBmongoTemplate.findById(id, class)Elasticsearchclient.get(new GetRequest(index, id))这种割裂不仅增加了学习成本更让架构设计变得复杂。我们看似有了一堆工具但依然没有一个真正的 “One API” 来统一所有数据访问。三、One API Access Any DataBase既然已经走向多元化数据访问层DAL也必须进化。新一代数据访问库的使命应当是让数据访问重新实现标准化和统一化。我们不再应该问 “这是什么数据库”而应该问 “我想在这个数据源上做什么操作”。继承 JDBC 和 SQL 的普世精神但打破其对关系型数据库的枷锁这就是新一代数据访问库的目标。将其概括为一句就是“One API Access Any DataBase”。四、 技术选择与可行路径要实现这个宏大的愿景技术上有两条主要路径可供选择路径 1统一 DSL这条路试图定义一种 “万能语言”能同时表达关系查询、文档检索、图遍历等逻辑。类似 SQL 的统一 DSL 本质的难点在于应用场景的不同导致很难有一个统一的 DSL 能在所有场景下适用。比如 Oracle、MongoDB、Elasticsearch 甚至是 Redis 在语法层面达成共识。自然语言 一种更大胆的假设基于 LLM 大语言模型直接将自然语言解释为数据库引擎可执行的物理执行计划也就是自然语言 - AI - 算子树 - 存储引擎 AI 在这一过程中充当了 Parser 和 Optimizer 的角色直接驱动数据库内核运行具体的物理任务。但在当下AI 在处理语义精确性、数据访问安全性以及复杂逻辑推理时仍存在“幻觉”风险。 将不确定的 AI 推理直接作用于确定性的数据存储内核这将会是一场极具冒险的行为。因此它更多被视为辅助工具Copilot而非底层的、确定性的数据访问标准。路径 2基本范式的抽象操作Operation是数据访问的本质。相比发明新语言或依赖 AI显得更加务实且可控。无论数据存在 MySQL 的行、Redis 的 Key、Elasticsearch 的 Document、还是 Neo4j 的节点。应用程序对数据的使用场景绝大多数时候都逃不出增Create、删Delete、改Update、查Read这四个基本范式。行为为中心 不同于 SQL 关注“如何描述数据”统一 API 关注“应用想对数据做什么”。“根据 ID 获取对象”是一个通用的意图无论底层是SELECT * FROM table WHERE id?还是GET /index/_doc/id其业务语义是完全一致的。逃生舱Escape Hatch 当然如果仅有简单的 CRUD 是无法覆盖真实业务中 20% 的复杂场景如深度聚合、图算法分析。因此统一 API 方案必须包含一个逃生舱机制。 当标准 CRUD 无法满足需求时开发者可以借助 JDBC 的Statement接口直接下发专有 DSL如 Elasticsearch JSON Query或标准化 SQL。 底层的适配器不仅负责翻译标准 CRUD也允许透传原生 API 或 SDK 调用确保简单场景统一化复杂场景如初般强大。适配器模式 通过定义一套标准的 API如insert,update,query我们可以在底层通过适配器模式将这些标准请求动态 “翻译” 为不同数据源的方言Dialect。五、 新一代数据访问库我认为 “新一代 Java 数据访问库” 应该具是以One API Access Any DataBase为核心愿景通过标准化的 API 屏蔽底层数据源的差异为开发者提供统一、简单、高效的数据操作体验为目标。它不应再区分“这是 ORM”还是“这是 Client”它就是应用通往数据的统一大门。六、 dbVisitor 的技术尝试dbVisitor正是基于这一理念诞生的技术尝试。它的架构设计非常独特可以概括为API访问库 JDBC Driver的双层适配架构。1. API访问库提供统一 APIdbVisitor 的数据访问层不依赖于具体的 SQL 语法而是提供高度抽象的 API。例如查询构造器// 无论是 MySQL 还是 MongoDB代码看起来都是一样的 lambdaTemplate.lambdaQuery(User.class) .eq(User::getAge, 18) .list();这一层负责屏蔽 Java 对象与数据模型之间的映射差异。在此过程中方言Dialect扮演了关键的翻译官角色。它负责根据上层统一 API 的调用行为如.list(),.eq()生成目标数据源能够理解的专有 DSL如 MySQL 的 SQL、MongoDB 的 BSON Command、Elasticsearch 的 JSON DSL。这些生成的 DSL 随后会被下发到JDBC Driver 适配层由对应的驱动执行器完成最终的数据交互。这种机制确保了业务代码的纯净性同时保留了对底层特性的精确控制。2. JDBC Driver 适配器标准下的选择性实现这是 dbVisitor 最具创新性的地方。它没有重新发明轮子去写一套私有协议的 Driver而是选择复用 JDBC 标准接口但对其内涵进行了扩展和适配。dbVisitor 的解法是引入一个轻量级的驱动适配器框架。它将 JDBC 繁琐的状态管理和复杂的接口规范封装为简单的Request/Response 模型。开发者不需要去实现一个完整的 JDBC 规范只需要关注核心的数据交互逻辑并实现Request/Response 模型即可。这种简化极大降低了适配新数据源的成本从而能够快速适配绝大多数的数据访问需求。通过这种“旧瓶装新酒”的方式dbVisitor 既保留了 JDBC 生态的兼容性你可以直接用 Druid 连接池管理 ES 连接又实现了对 NoSQL 的原生级支持。七、目前的挑战尽管 dbVisitor 的双层适配架构解决了大部分通用问题但在实现 “One API” 的征途中我们依然面临着一些客观存在的挑战封装与穿透 Request/Response 模型可以极大地简化了适配器开发但没有任何一种抽象能完美覆盖所有底层特性。当开发者需要使用某个数据源极其特殊的特性时 目前的解法是允许使用 JDBC 的unwrap方法直达底层 SDK。虽然这在一定程度上破坏了封装性不作为推荐用法通过这种“开后门”的方式保证了在极端场景下问题依然可解。DSL 困境 并非所有 NoSQL 都有完善的查询语言对于那些没有标准 DSL 的数据库dbVisitor 不得不采用一种折中方案用 DSL 语法来模仿 SDK 的 API 调用结构。 这样做的好处是保留了近似官方的习惯用法降低了认知门槛。但坏处也很明显不同版本的 SDK API 差异甚至是不兼容的 API 结构。 这会削弱了 DSL 本身的标准化程度和稳定性加重认知负担。这个问题只能寄希望于数据库厂商可以有一个属于它自己的标准的查询语法出现例如 Elasticsearch 的 QueryDSL。最佳实践总结经过大量的适配实践我发现实现 “One API” 的最佳路径是依赖于数据库厂商提供的标准 DSL 或 Shell Commands。如果数据库本身提供了一套稳定的文本协议如 SQL, MongoDB Shell Command, Elasticsearch DSL那么基于这些标准协议构建适配器对接底层 API是最稳健、兼容性最好的方式。对于没有 DSL 的 数据库只需要模仿它 API 的调用方式提供一个 Shell Command这一点可以借鉴 MongoDB 的思路。八、 dbVisitor 实战演示为了让大家更直观地感受 “One API” 的魅力以最常见的 CRUD 操作为例展示 dbVisitor 如何在不同数据源间保持统一的编码体验。1. 统一的 CRUD 体验无论底层是MySQL、MongoDB还是Elasticsearch开发者都可以使用完全一致的 API 进行数据操作。// 初始化 (仅需更改 Connection 创建方式) // Connection conn DriverManager.getConnection(jdbc:mysql://...); // Connection conn DriverManager.getConnection(jdbc:dbvisitor:mongo://...); Connection conn DriverManager.getConnection(jdbc:dbvisitor:elastic://...); LambdaTemplate template new LambdaTemplate(conn); // 1. 插入数据 (Insert) UserInfo user new UserInfo(); user.setId(1001); user.setName(dbVisitor); template.insert(UserInfo.class) .applyEntity(user) .executeSumResult(); // 2. 查询数据 (Select) // 自动适配MySQL WHERE / Mongo Filter / ES BoolQuery ListUserInfo list template.query(UserInfo.class) .eq(UserInfo::getName, dbVisitor) .list(); // 3. 更新数据 (Update) template.update(UserInfo.class) .eq(UserInfo::getId, 1001) .updateTo(UserInfo::getName, Updated Name) .doUpdate(); // 4. 删除数据 (Delete) template.delete(UserInfo.class) .eq(UserInfo::getId, 1001) .doDelete();2. 底层 API 可达 (Escape Hatch)当统一 API 无法满足特殊需求时例如 Redis 的特定原子操作或 ES 的特殊聚合dbVisitor 允许通过unwrap机制“穿透”到底层驱动直接使用原生 SDK。// 场景 ARedis 原生调用 (穿透到 Jedis) if (conn.isWrapperFor(Jedis.class)) { Jedis jedis conn.unwrap(Jedis.class); jedis.set(native_key, value); // 使用 Jedis 所有原生能力... } // 场景 BElasticsearch 原生调用 (穿透到 RestClient) if (conn.isWrapperFor(RestClient.class)) { RestClient client conn.unwrap(RestClient.class); // 构造原生 Request... Request request new Request(GET, /user_index/_search); request.setJsonEntity({\query\:{\match_all\:{}}}); client.performRequest(request); }九、 dbVisitor 的生态现状目前dbVisitor 已经实现了对多类数据源的统一访问支持正在一步步践行新一代数据访问库的承诺关系型数据库支持 MySQL, PostgreSQL, Oracle, SQLServer, H2, SQLite 等主流关系型数据库。NoSQL 支持Elasticsearch支持复杂的索引查询和聚合。MongoDB支持文档的 CRUD 及复杂过滤。Redis将 Redis 抽象为数据表进行操作。在 dbVisitor 的世界里开发者不再需要为了引入一个新的中间件而重构整个数据访问层代码。One API, Access Any DataBase这不仅仅是一句口号。