新一代 Java 数据访问库:dbVisitor
什么是“新一代”数据访问库?
数据访问层(DAL)技术已经非常成熟,从最早的 JDBC 到 Hibernate、MyBatis,再到 Spring Data JPA。我们习惯了用这些框架处理数据。然而,当我们把目光投向“数据”本身的变化时,会发现这些经典的框架似乎正逐渐变为“老一代”。
新一代的挑战不再仅仅是如何优雅地写 SQL,而是如何用统一的方式访问那些不再仅仅存储在关系型数据库中的数据。
什么是“新一代”数据访问库?
数据访问层(DAL)技术已经非常成熟,从最早的 JDBC 到 Hibernate、MyBatis,再到 Spring Data JPA。我们习惯了用这些框架处理数据。然而,当我们把目光投向“数据”本身的变化时,会发现这些经典的框架似乎正逐渐变为“老一代”。
新一代的挑战不再仅仅是如何优雅地写 SQL,而是如何用统一的方式访问那些不再仅仅存储在关系型数据库中的数据。
在 Java 生态中,操作 ElasticSearch 最常见的方式莫过于使用官方的 elasticsearch-java (或旧版的 RestHighLevelClient) 或者 Spring 家族的 spring-data-elasticsearch。这些工具非常强大,但对于习惯了关系型数据库(RDBMS)和 MyBatis 开发模式的开发者来说,切换到 ElasticSearch 往往意味着需要适应一套全新的 API 和思维模式(DSL 构建、Builder 模式等)。
特别是在一个混合架构的项目中,如果同时存在 MySQL 和 ElasticSearch,数据访问层的代码风格割裂感会非常强:一边是 MyBatis 的 Mapper 接口和 XML,另一边是复杂的 DSL 构建代码或 Repository 接口。这种差异不仅增加了学习成本,也让诸如“分页查询”这样的通用功能难以统一实现。
本文将介绍如何使用 dbVisitor,以一种“类 MyBatis”的方式来操作 ElasticSearch,实现架构上的统一。
在 Java 生态中,操作 MongoDB 最常见的方式莫过于使用官方的 mongo-java-driver 或者 Spring 家族的 spring-data-mongodb。这些工具非常强大,但对于习惯了关系型数据库(RDBMS)和 MyBatis 开发模式的开发者来说,切换到 MongoDB 往往意味着需要适应一套全新的 API 和思维模式。
特别是在一个混合架构的项目中,如果同时存在 MySQL 和 MongoDB,数据访问层的代码风格割裂感会非常强:一边是 MyBatis 的 Mapper 接口和 XML,另一边是 MongoTemplate 的链式调用或 Repository 接口。这种差异不仅增加了学习成本,也让诸如“分页查询”这样的通用功能难以统一实现。
本文将介绍如何使用 dbVisitor,以一种“类 MyBatis”的方式来操作 MongoDB,实现架构上的统一。
在 Java 开发领域,关系型数据库(RDBMS)的 ORM 工具(如 Hibernate, MyBatis)已经非常成熟。然而,随着 NoSQL 数据库特别是 MongoDB 的普及,开发者对于在 Java 中如何优雅、高效地操作 MongoDB 提出了新的需求。
虽然 MongoDB 是文档型数据库,天生具备 Schema-less 的特性,但在强类型的 Java 语言中,我们依然需要一种机制将 BSON 文档映射为 Java 对象,以便于业务逻辑的处理。这就是 ODM(Object-Document Mapping)应运而生的背景。
本文将探讨 MongoDB 开发中的 ORM/ODM 现状,并介绍一款基于 JDBC 协议的 MongoDB 新工具 —— dbVisitor。
因此规则还可稍微复杂一点
@{and, (age = :age and sex = '1') or (name = :name and id in (:ids)) }
对应的 SQL 为:
(age = ? and sex = '1') or (name = ? and id in (?, ?, ?))
当查询一张超大表并获取它的结果集时要使用 流式返回 否则内存极易出现溢出。
不同的数据库开启流式返回的方式虽有差异,但都需要设置 Statement/PreparedStatement 的参数。
下面就以 MySQL 为例展示一下通过定制 Statement 实现流式查询的例子:
// 定制 PreparedStatement
PreparedStatementCreator creator = con -> {
PreparedStatement ps = con.prepareStatement(
"select * from test_user",
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY
);
ps.setFetchSize(Integer.MIN_VALUE);
return ps;
};
// 行读取工具
MappingRowMapper<TestUser> rowMapper = new MappingRowMapper<>(TestUser.class);
// 流式消费数据
RowCallbackHandler handler = (rs, rowNum) -> {
TestUser dto = rowMapper.mapRow(rs, rowNum);
};
// 执行流式处理
jdbcTemplate.executeCreator(creator, handler);