Files
java-g/README.md
2026-01-23 17:41:45 +08:00

4.6 KiB
Raw Permalink Blame History

Java Low-Code / Dynamic Query Engine

本项目展示了两种在 Java (Spring Boot) 环境下实现“低代码”后端查询引擎的方案。这两种方案都旨在解决同一个问题:前端按需索取数据GraphQL 风格),后端自动构建查询,无需手动编写 CRUD 代码。

方案对比

特性 方案 A: Hibernate/JPA + Squiggly 方案 B: MyBatis 动态生成引擎
核心理念 运行时动态构建 Predicate + JSON 裁剪 自动生成优化的 SQL 和 ResultMap XML
查询构建 JPA Criteria API 字符串拼接 / AST 构建 SQL
字段裁剪 查全量数据 -> 内存中 JSON 裁剪 (Squiggly) 数据库层直接只查所需字段 (Select Partial)
关联查询 依赖 Hibernate 懒加载 (可能导致 N+1) 单条 SQL LEFT JOIN (性能最优)
递归/深层 容易 StackOverflow (需 @JsonIdentityInfo) 通过 ResultMap + ColumnPrefix 完美支持无限递归
适用场景 快速开发、中后台管理、逻辑简单 高性能 API、复杂报表、移动端接口

方案 A: Hibernate/JPA 策略

利用 Spring Data JPA 的 Specification 和 Jackson 的过滤器实现。

特性

  1. 通用 Controller/Repository:只需继承 BaseControllerBaseRepository,即可获得完整的 CRUD 和动态搜索能力。
  2. 动态过滤:支持 key=value 精确匹配和 key=val* 模糊匹配。
  3. 动态投影:通过 fields=id,title,author.name 参数,后端自动裁剪返回的 JSON 结构。
  4. 全能写操作
    • Create (POST): 支持级联创建(如一次性提交 Book + Author
    • Update (PUT): 全量更新。
    • Patch (PATCH): 支持深度合并 (Deep Merge),仅更新 JSON 中提供的字段,保留嵌套对象的其他属性。
    • Delete (DELETE): 支持级联删除。

代码示例

// 继承通用基类
public interface BookRepository extends BaseRepository<Book, Long> {}

// 1. 动态查询
// GET /books?author.name=John*&fields=title,author.name

// 2. 级联创建 (POST /books)
/*
{
  "title": "New Book",
  "author": { "name": "New Author" } // 同时创建 Author
}
*/

// 3. 局部更新 (PATCH /books/1)
// 仅修改标题Author 关联保持不变
/*
{ "title": "Updated Title" }
*/

方案 B: MyBatis 动态生成引擎

这是本项目最核心的创新点。我们要解决 MyBatis "手写 XML 太累,自动生成又不灵活" 的痛点。

我们实现了一个运行时 XML 生成器 (MyBatisGeneratorUtils),它能根据请求动态生成完整的 MyBatis Mapper XML。

核心技术点

  1. 递归 ResultMap 复用 利用 MyBatis 的 columnPrefix 特性,我们只需要定义一套标准的 ResultMapBookMap, AuthorMap),就可以通过前缀(author$, books$)实现无限层级的嵌套映射。

  2. 智能别名策略 生成的 SQL 别名严格遵循 path$ColumnName 格式(例如 book$author$publish_month。MyBatis 在处理 ResultMap 时会逐层剥离前缀,最终将 publish_month 映射到正确的实体属性上。这解决了递归查询中列名冲突和映射失效的问题。

  3. 一对多 (List) 自动折叠 引擎会自动识别 @OneToMany 关系,生成正确的 JOIN 条件,并强制投影 ID 列。这确保了 MyBatis 能够正确地进行结果集去重Result Folding将多行数据合并为一个对象列表。

生成效果示例

输入:查询书籍,要求返回 titleauthor.books.title

自动生成的 SQL

SELECT 
  book.id AS id,
  book.title AS title,
  book$author.id AS author$id,
  book$author$books.id AS author$books$id,
  book$author$books.title AS author$books$title
FROM book book
LEFT JOIN author book$author ON book.author_id = book$author.id
LEFT JOIN book book$author$books ON book$author.id = book$author$books.author_id

自动生成的 XML(片段):

<resultMap id="AuthorMap" type="Author">
    <!-- 引用 BookMap前缀为 books$ -->
    <collection property="books" resultMap="BookMap" columnPrefix="books$"/>
</resultMap>

如何使用

目前引擎逻辑位于 com.example.demo.util.MyBatisGeneratorUtils,并配有完整的单元测试 MyBatisGeneratorUtilsTest。下一步计划是将其封装为 MyBatisDynamicService,在 Controller 中直接调用。


快速开始

  1. 环境Java 17+, Maven
  2. 运行测试
    mvn test -Dtest=MyBatisGeneratorUtilsTest
    
    查看控制台输出,观察自动生成的 SQL 和 XML 结构。