1
This commit is contained in:
129
README.md
129
README.md
@@ -1,40 +1,111 @@
|
||||
## 运行指南
|
||||
# Java Low-Code / Dynamic Query Engine
|
||||
|
||||
这是一个使用 Spring Boot, SQLite, Hibernate (JPA) 和 MyBatis 的示例项目。
|
||||
本项目展示了两种在 Java (Spring Boot) 环境下实现“低代码”后端查询引擎的方案。这两种方案都旨在解决同一个问题:**前端按需索取数据(GraphQL 风格),后端自动构建查询,无需手动编写 CRUD 代码。**
|
||||
|
||||
### 1. 启动应用
|
||||
## 方案对比
|
||||
|
||||
在项目根目录下运行:
|
||||
| 特性 | 方案 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、复杂报表、移动端接口 |
|
||||
|
||||
```bash
|
||||
mvn spring-boot:run
|
||||
---
|
||||
|
||||
## 方案 A: Hibernate/JPA 策略
|
||||
|
||||
利用 Spring Data JPA 的 `Specification` 和 Jackson 的过滤器实现。
|
||||
|
||||
### 特性
|
||||
1. **通用 Controller/Repository**:只需继承 `BaseController` 和 `BaseRepository`,即可获得完整的 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)**: 支持级联删除。
|
||||
|
||||
### 代码示例
|
||||
```java
|
||||
// 继承通用基类
|
||||
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" }
|
||||
*/
|
||||
```
|
||||
或者打包运行:
|
||||
```bash
|
||||
mvn clean package -DskipTests
|
||||
java -jar target/demo-sqlite-mybatis-hibernate-0.0.1-SNAPSHOT.jar
|
||||
|
||||
---
|
||||
|
||||
## 方案 B: MyBatis 动态生成引擎
|
||||
|
||||
这是本项目最核心的创新点。我们要解决 MyBatis **"手写 XML 太累,自动生成又不灵活"** 的痛点。
|
||||
|
||||
我们实现了一个**运行时 XML 生成器** (`MyBatisGeneratorUtils`),它能根据请求动态生成完整的 MyBatis Mapper XML。
|
||||
|
||||
### 核心技术点
|
||||
|
||||
1. **递归 ResultMap 复用**:
|
||||
利用 MyBatis 的 `columnPrefix` 特性,我们只需要定义一套标准的 ResultMap(如 `BookMap`, `AuthorMap`),就可以通过前缀(`author$`, `books$`)实现无限层级的嵌套映射。
|
||||
|
||||
2. **智能别名策略**:
|
||||
生成的 SQL 别名严格遵循 `path$ColumnName` 格式(例如 `book$author$publish_month`)。MyBatis 在处理 ResultMap 时会逐层剥离前缀,最终将 `publish_month` 映射到正确的实体属性上。这解决了递归查询中列名冲突和映射失效的问题。
|
||||
|
||||
3. **一对多 (List) 自动折叠**:
|
||||
引擎会自动识别 `@OneToMany` 关系,生成正确的 `JOIN` 条件,并**强制投影 ID 列**。这确保了 MyBatis 能够正确地进行结果集去重(Result Folding),将多行数据合并为一个对象列表。
|
||||
|
||||
### 生成效果示例
|
||||
|
||||
**输入**:查询书籍,要求返回 `title` 和 `author.books.title`。
|
||||
|
||||
**自动生成的 SQL**:
|
||||
```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
|
||||
```
|
||||
*注意:端口配置在 8081*
|
||||
|
||||
### 2. 测试接口
|
||||
**自动生成的 XML**(片段):
|
||||
```xml
|
||||
<resultMap id="AuthorMap" type="Author">
|
||||
<!-- 引用 BookMap,前缀为 books$ -->
|
||||
<collection property="books" resultMap="BookMap" columnPrefix="books$"/>
|
||||
</resultMap>
|
||||
```
|
||||
|
||||
项目启动时会自动插入一些测试数据。你可以使用以下接口进行测试:
|
||||
### 如何使用
|
||||
目前引擎逻辑位于 `com.example.demo.util.MyBatisGeneratorUtils`,并配有完整的单元测试 `MyBatisGeneratorUtilsTest`。下一步计划是将其封装为 `MyBatisDynamicService`,在 Controller 中直接调用。
|
||||
|
||||
- **获取所有书籍 (使用 JPA):**
|
||||
`GET http://localhost:8081/books/jpa`
|
||||
---
|
||||
|
||||
- **获取所有书籍 (使用 MyBatis - 简单映射):**
|
||||
`GET http://localhost:8081/books/mybatis`
|
||||
## 快速开始
|
||||
|
||||
- **按作者搜索 (使用 MyBatis - 复杂 XML 映射):**
|
||||
`GET http://localhost:8081/books/search?author=Orwell`
|
||||
|
||||
### 3. 项目结构
|
||||
|
||||
- **实体类**: `src/main/java/com/example/demo/entity` (Book, Author, Publisher)
|
||||
- **JPA Repository**: `src/main/java/com/example/demo/repository`
|
||||
- **MyBatis Mapper**:
|
||||
- 接口: `src/main/java/com/example/demo/mapper`
|
||||
- XML: `src/main/resources/mapper`
|
||||
- **Service**: `src/main/java/com/example/demo/service`
|
||||
- **Controller**: `src/main/java/com/example/demo/controller`
|
||||
1. **环境**:Java 17+, Maven
|
||||
2. **运行测试**:
|
||||
```bash
|
||||
mvn test -Dtest=MyBatisGeneratorUtilsTest
|
||||
```
|
||||
查看控制台输出,观察自动生成的 SQL 和 XML 结构。
|
||||
|
||||
Reference in New Issue
Block a user