docs: 将 database 模块文档拆分为独立文件
This commit is contained in:
80
README.md
80
README.md
@@ -31,83 +31,11 @@ com.lanyuanxiaoyao.service.template.{module}/
|
|||||||
|
|
||||||
## database 模块
|
## database 模块
|
||||||
|
|
||||||
单表 CRUD → REST 接口的快速实现框架。基于 JPA + Fenix + QueryDSL + MapStruct。
|
单表 CRUD → REST 接口快速实现框架。基于 JPA + Fenix + QueryDSL + MapStruct。
|
||||||
|
|
||||||
### 实体继承
|
**文档**:
|
||||||
|
- [开发指南](docs/database-development.md) - 模块架构、核心设计、技术实现、扩展指南
|
||||||
```
|
- [使用指南](docs/database-usage.md) - 快速开始、API 接口、查询条件、高级用法
|
||||||
IdOnlyEntity (id: Long, @SnowflakeId) ← @MappedSuperclass
|
|
||||||
↑
|
|
||||||
SimpleEntity (+ createdTime: LocalDateTime, ← @MappedSuperclass
|
|
||||||
modifiedTime: LocalDateTime)
|
|
||||||
↑
|
|
||||||
业务实体 (具体 @Entity)
|
|
||||||
```
|
|
||||||
|
|
||||||
- ID: Long,雪花算法,字段标 `@SnowflakeId`(自定义注解)
|
|
||||||
- 时间字段: `@CreatedDate`/`@LastModifiedDate`,JPA `AuditingEntityListener` 自动填充
|
|
||||||
- 列注释: `@Column(comment = "...")`
|
|
||||||
- 物理命名: `PhysicalNamingStrategySnakeCaseImpl`(camelCase→snake_case)
|
|
||||||
- 保存: Fenix `saveOrUpdateByNotNullProperties()`,自动 INSERT/UPDATE,仅更新非 null 字段
|
|
||||||
|
|
||||||
### Repository
|
|
||||||
|
|
||||||
```java
|
|
||||||
@NoRepositoryBean
|
|
||||||
public interface SimpleRepository<E> extends
|
|
||||||
FenixJpaRepository<E, Long>,
|
|
||||||
FenixJpaSpecificationExecutor<E>,
|
|
||||||
ListQueryByExampleExecutor<E>,
|
|
||||||
ListQuerydslPredicateExecutor<E> {}
|
|
||||||
```
|
|
||||||
|
|
||||||
业务 Repository 直接继承: `interface EmployeeRepository extends SimpleRepository<Employee> {}`
|
|
||||||
|
|
||||||
无 XML Mapper,无自定义 SQL,查询通过 JPA Criteria / Fenix / QueryDSL 动态构建。
|
|
||||||
|
|
||||||
### 接口组合模式
|
|
||||||
|
|
||||||
```java
|
|
||||||
SimpleService<ENTITY> extends SaveService<ENTITY>, QueryService<ENTITY>, RemoveService<ENTITY>
|
|
||||||
SimpleController<S, L, D> extends SaveController<S>, QueryController<L, D>, RemoveController
|
|
||||||
```
|
|
||||||
|
|
||||||
抽象基类: `SimpleServiceSupport`(Service) / `SimpleControllerSupport`(Controller),用 `*Support` 后缀(非 `*Impl`)。
|
|
||||||
|
|
||||||
Controller 子类必须提供三个 mapper 函数: `saveItemMapper()`, `listItemMapper()`, `detailItemMapper()`
|
|
||||||
|
|
||||||
### CRUD 五步
|
|
||||||
|
|
||||||
1. 创建实体 — 继承 `SimpleEntity`
|
|
||||||
2. 创建 Repository — 继承 `SimpleRepository<Entity>`
|
|
||||||
3. 创建 Service — 继承 `SimpleServiceSupport<Entity>`
|
|
||||||
4. 创建 DTO — 在 Controller 内定义 `SaveItem`/`ListItem`/`DetailItem` record
|
|
||||||
5. 创建 Controller — 继承 `SimpleControllerSupport<Entity, SaveItem, ListItem, DetailItem>`,实现 3 个 mapper
|
|
||||||
|
|
||||||
也可用 `DatabaseHelper.generateBasicFiles()` 自动生成上述脚手架,`@RequestMapping` 路径由实体名 camelCase→snake_case 推导。`DatabaseHelper.generateDDL()` 可从实体类生成 DDL SQL。
|
|
||||||
|
|
||||||
### API 端点
|
|
||||||
|
|
||||||
| 操作 | 方法 | 路径 |
|
|
||||||
| --------- | ---- | ---------------- |
|
|
||||||
| 新增/更新 | POST | `/save` |
|
|
||||||
| 全部列表 | GET | `/list` |
|
|
||||||
| 条件列表 | POST | `/list` |
|
|
||||||
| 详情 | GET | `/detail/{id}` |
|
|
||||||
| 删除 | GET | `/remove/{id}` |
|
|
||||||
|
|
||||||
统一响应: `GlobalResponse<T>(status:Integer, message:String, data:T)`,成功 status=0,失败 status=500。
|
|
||||||
|
|
||||||
- 列表: `data = ListItem<T>(items:Iterable<T>, total:Long)`
|
|
||||||
- 详情: `data = DetailItem<T>(item:T)`
|
|
||||||
|
|
||||||
### 查询条件
|
|
||||||
|
|
||||||
查询对象: `Query(Queryable query, List<Sortable> sort, Pageable page)`
|
|
||||||
|
|
||||||
支持: equal/notEqual/like/notLike/contain/notContain/startWith/endWith/great/less/greatEqual/lessEqual/between/notBetween/inside/notInside/nullEqual/notNullEqual/empty/notEmpty
|
|
||||||
|
|
||||||
分页: `Pageable(index, size)`,index 从 1 开始,默认 `(1, 10)`,无排序默认 `createdTime DESC`
|
|
||||||
|
|
||||||
## 开发规范
|
## 开发规范
|
||||||
|
|
||||||
|
|||||||
205
docs/database-development.md
Normal file
205
docs/database-development.md
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
# Database 模块开发指南
|
||||||
|
|
||||||
|
单表 CRUD → REST 接口快速实现框架。基于 JPA + Fenix + QueryDSL + MapStruct。
|
||||||
|
|
||||||
|
## 架构
|
||||||
|
|
||||||
|
```
|
||||||
|
Controller (REST) → Service (业务) → Repository (数据访问) → Entity (模型)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 组件结构
|
||||||
|
|
||||||
|
| 包 | 组件 | 职责 |
|
||||||
|
|---|---|---|
|
||||||
|
| entity | IdOnlyEntity, SimpleEntity | 实体基类 |
|
||||||
|
| entity | SnowflakeId, SnowflakeIdGenerator | ID 生成 |
|
||||||
|
| entity | Query, GlobalResponse, Page | 查询/响应封装 |
|
||||||
|
| repository | SimpleRepository | 统一数据访问接口 |
|
||||||
|
| service | SaveService, QueryService, RemoveService | 功能接口 |
|
||||||
|
| service | SimpleService, SimpleServiceSupport | 组合接口与实现 |
|
||||||
|
| service | QueryParser | 查询条件解析 |
|
||||||
|
| controller | SaveController, QueryController, RemoveController | REST 接口 |
|
||||||
|
| controller | SimpleController, SimpleControllerSupport | 组合接口与实现 |
|
||||||
|
| helper | DatabaseHelper, SnowflakeHelper | 工具类 |
|
||||||
|
| exception | *Exception | 异常定义 |
|
||||||
|
|
||||||
|
## 核心设计
|
||||||
|
|
||||||
|
### 实体继承
|
||||||
|
|
||||||
|
```
|
||||||
|
IdOnlyEntity (id: Long, @SnowflakeId)
|
||||||
|
↑
|
||||||
|
SimpleEntity (+ createdTime, modifiedTime)
|
||||||
|
↑
|
||||||
|
业务实体 (@Entity)
|
||||||
|
```
|
||||||
|
|
||||||
|
**实现要点**:
|
||||||
|
- `@MappedSuperclass` 标记基类
|
||||||
|
- `@SnowflakeId` 触发 `SnowflakeIdGenerator` 生成 ID
|
||||||
|
- `@CreatedDate/@LastModifiedDate` + `AuditingEntityListener` 自动填充时间
|
||||||
|
|
||||||
|
### Repository
|
||||||
|
|
||||||
|
```java
|
||||||
|
@NoRepositoryBean
|
||||||
|
public interface SimpleRepository<E> extends
|
||||||
|
FenixJpaRepository<E, Long>, // CRUD + Fenix
|
||||||
|
FenixJpaSpecificationExecutor<E>, // Specification
|
||||||
|
ListQueryByExampleExecutor<E>, // Example
|
||||||
|
ListQuerydslPredicateExecutor<E> {} // QueryDSL
|
||||||
|
```
|
||||||
|
|
||||||
|
**核心能力**:
|
||||||
|
- `saveOrUpdateByNotNullProperties()` - 部分字段更新
|
||||||
|
- Specification - 动态条件查询
|
||||||
|
- QueryDSL - 类型安全查询
|
||||||
|
|
||||||
|
### Service 接口组合
|
||||||
|
|
||||||
|
```java
|
||||||
|
SaveService<ENTITY> // save(entity), save(entities)
|
||||||
|
QueryService<ENTITY> // detail(id), list(), list(query), count()
|
||||||
|
RemoveService<ENTITY> // remove(id), remove(ids)
|
||||||
|
|
||||||
|
SimpleService<ENTITY> extends SaveService, QueryService, RemoveService
|
||||||
|
```
|
||||||
|
|
||||||
|
**SimpleServiceSupport 实现**:
|
||||||
|
- 保存:Fenix `saveOrUpdateByNotNullProperties()`
|
||||||
|
- 查询:JPA Criteria + Specification
|
||||||
|
- 删除:`deleteBatchByIds()`
|
||||||
|
- 扩展点:重写 `commonPredicates()` 添加全局过滤条件
|
||||||
|
|
||||||
|
### Controller 接口组合
|
||||||
|
|
||||||
|
```java
|
||||||
|
SaveController<SAVE_ITEM> // POST /save
|
||||||
|
QueryController<LIST_ITEM, DETAIL_ITEM> // GET/POST /list, GET /detail/{id}
|
||||||
|
RemoveController // GET /remove/{id}
|
||||||
|
|
||||||
|
SimpleController<SAVE_ITEM, LIST_ITEM, DETAIL_ITEM>
|
||||||
|
```
|
||||||
|
|
||||||
|
**SimpleControllerSupport 实现**:
|
||||||
|
- 调用 Service 方法
|
||||||
|
- 通过 Mapper 转换 DTO ↔ Entity
|
||||||
|
- 封装 GlobalResponse
|
||||||
|
- 扩展点:实现 `saveItemMapper()`, `listItemMapper()`, `detailItemMapper()`
|
||||||
|
|
||||||
|
### 查询条件
|
||||||
|
|
||||||
|
**Query 结构**:`Query(query: Queryable, sort: List<Sortable>, page: Pageable)`
|
||||||
|
|
||||||
|
**QueryParser**:抽象类定义解析接口,`JpaQueryParser` 转换为 JPA Predicate
|
||||||
|
|
||||||
|
**支持操作**:
|
||||||
|
|
||||||
|
| 类别 | 操作 |
|
||||||
|
|---|---|
|
||||||
|
| 空值 | nullEqual, notNullEqual, empty, notEmpty |
|
||||||
|
| 相等 | equal, notEqual |
|
||||||
|
| 模糊 | like, notLike, contain, notContain |
|
||||||
|
| 前后缀 | startWith, endWith, notStartWith, notEndWith |
|
||||||
|
| 比较 | great, less, greatEqual, lessEqual |
|
||||||
|
| 区间 | between, notBetween |
|
||||||
|
| 集合 | inside, notInside |
|
||||||
|
|
||||||
|
**实现**:JPA CriteriaBuilder 构建,支持多级字段路径(如 `user.name`),自动类型转换(枚举、LocalDateTime)
|
||||||
|
|
||||||
|
### 响应格式
|
||||||
|
|
||||||
|
```java
|
||||||
|
GlobalResponse<T>(status, message, data)
|
||||||
|
// status: 0 成功, 500 失败
|
||||||
|
// 列表: data = ListItem(items, total)
|
||||||
|
// 详情: data = DetailItem(item)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 技术细节
|
||||||
|
|
||||||
|
### 雪花算法
|
||||||
|
|
||||||
|
`SnowflakeHelper`:64 位 Long,1 位符号 + 41 位时间戳 + 10 位机器 ID + 12 位序列号
|
||||||
|
|
||||||
|
`SnowflakeIdGenerator`:实现 `IdentifierGenerator`,持久化时调用 `SnowflakeHelper.next()`
|
||||||
|
|
||||||
|
### 部分更新
|
||||||
|
|
||||||
|
Fenix `saveOrUpdateByNotNullProperties()`:
|
||||||
|
- 自动判断 INSERT/UPDATE
|
||||||
|
- 仅更新非 null 字段
|
||||||
|
|
||||||
|
### 命名策略
|
||||||
|
|
||||||
|
`PhysicalNamingStrategySnakeCaseImpl`:camelCase → snake_case
|
||||||
|
|
||||||
|
### 注解处理器
|
||||||
|
|
||||||
|
执行顺序:lombok → hibernate-jpamodelgen → querydsl-apt → mapstruct-processor
|
||||||
|
|
||||||
|
生成:getter/setter、JPA 元模型(_Entity)、QueryDSL Q 类(QEntity)、MapStruct Mapper
|
||||||
|
|
||||||
|
## 工具类
|
||||||
|
|
||||||
|
### DatabaseHelper
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 生成 DDL
|
||||||
|
generateDDL(entityPackages, ddlFilePath, dialect, jdbc, username, password, driver)
|
||||||
|
|
||||||
|
// 生成脚手架
|
||||||
|
generateBasicFiles(entityPackages, projectRootPackage, projectRootPath, override)
|
||||||
|
```
|
||||||
|
|
||||||
|
### SnowflakeHelper
|
||||||
|
|
||||||
|
```java
|
||||||
|
Long id = SnowflakeHelper.next();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 扩展指南
|
||||||
|
|
||||||
|
### 软删除
|
||||||
|
|
||||||
|
1. 创建 `SoftDeleteEntity extends SimpleEntity`,添加 `deleted` 字段
|
||||||
|
2. 重写 `commonPredicates()` 返回 `deleted = false` 条件
|
||||||
|
3. 覆盖 `remove()` 改为更新 `deleted` 字段
|
||||||
|
|
||||||
|
### 多租户
|
||||||
|
|
||||||
|
1. 创建 `TenantEntity extends SimpleEntity`,添加 `tenantId` 字段
|
||||||
|
2. 重写 `commonPredicates()` 添加租户过滤
|
||||||
|
3. ThreadLocal 或 Spring Security 存储当前租户
|
||||||
|
|
||||||
|
### 审计字段
|
||||||
|
|
||||||
|
在 `SimpleEntity` 添加 `createdBy`, `modifiedBy`,配合 Spring Security 获取当前用户
|
||||||
|
|
||||||
|
### 自定义查询操作符
|
||||||
|
|
||||||
|
1. `Query.Queryable` 添加字段
|
||||||
|
2. `QueryParser` 添加抽象方法
|
||||||
|
3. `JpaQueryParser` 实现转换为 Predicate
|
||||||
|
|
||||||
|
## 测试
|
||||||
|
|
||||||
|
- H2 内存数据库
|
||||||
|
- `@DataJpaTest` 测试 Repository
|
||||||
|
- `@WebMvcTest` 测试 Controller
|
||||||
|
- 测试用例:`src/test/java/.../integration/`
|
||||||
|
|
||||||
|
## 依赖
|
||||||
|
|
||||||
|
核心:spring-boot-starter-data-jpa, fenix-spring-boot-starter:4.0.0, querydsl-jpa:7.1, mapstruct:1.6.3
|
||||||
|
|
||||||
|
传递:spring-boot-service-template-common
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- 事务:写操作 `@Transactional(rollbackFor = Throwable.class)`,读操作 `@Transactional(readOnly = true)`
|
||||||
|
- 异常:不使用全局处理器,直接抛出
|
||||||
|
- 性能:批量操作使用批量方法
|
||||||
|
- 线程安全:GlobalResponse 用 record 保证不可变,SnowflakeHelper 用原子变量
|
||||||
409
docs/database-usage.md
Normal file
409
docs/database-usage.md
Normal file
@@ -0,0 +1,409 @@
|
|||||||
|
# Database 模块使用指南
|
||||||
|
|
||||||
|
单表 CRUD → REST 接口快速实现框架。
|
||||||
|
|
||||||
|
## 使用模式
|
||||||
|
|
||||||
|
### Web 应用
|
||||||
|
|
||||||
|
引入 database 模块,创建 Entity → Repository → Service → Controller,实现 REST 接口。
|
||||||
|
|
||||||
|
### 非 Web 应用
|
||||||
|
|
||||||
|
**无需引入 web 依赖**(database 模块的 `spring-boot-starter-web` scope 为 `provided`)。
|
||||||
|
|
||||||
|
仅使用 Entity → Repository → Service,直接注入 Service 使用:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@SpringBootApplication
|
||||||
|
public class Application implements CommandLineRunner {
|
||||||
|
@Autowired
|
||||||
|
private EmployeeService service;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String... args) {
|
||||||
|
Employee emp = new Employee();
|
||||||
|
emp.setName("张三");
|
||||||
|
Long id = service.save(emp);
|
||||||
|
Employee found = service.detail(id);
|
||||||
|
List<Employee> list = service.list();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
适用:批处理、定时任务、数据迁移、命令行应用、后台服务。
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 1. 添加依赖
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.lanyuanxiaoyao</groupId>
|
||||||
|
<artifactId>spring-boot-service-template-database</artifactId>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 创建实体
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Getter @Setter
|
||||||
|
@ToString(callSuper = true)
|
||||||
|
@FieldNameConstants
|
||||||
|
@Entity
|
||||||
|
@Table(name = "employee")
|
||||||
|
public class Employee extends SimpleEntity {
|
||||||
|
@Column(comment = "员工姓名", nullable = false)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(comment = "部门ID")
|
||||||
|
private Long departmentId;
|
||||||
|
|
||||||
|
@Column(comment = "邮箱")
|
||||||
|
private String email;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
继承 `SimpleEntity` 自动获得 `id`(雪花算法)、`createdTime`、`modifiedTime`(自动填充)。
|
||||||
|
|
||||||
|
### 3. 创建 Repository
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Repository
|
||||||
|
public interface EmployeeRepository extends SimpleRepository<Employee> {}
|
||||||
|
```
|
||||||
|
|
||||||
|
继承能力:CRUD、分页、Specification、QueryDSL、Example。
|
||||||
|
|
||||||
|
### 4. 创建 Service
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class EmployeeService extends SimpleServiceSupport<Employee> {
|
||||||
|
public EmployeeService(EmployeeRepository repository) {
|
||||||
|
super(repository);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
自动获得完整 CRUD 能力。
|
||||||
|
|
||||||
|
### 5. 创建 Controller(Web 应用)
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("employee")
|
||||||
|
public class EmployeeController
|
||||||
|
extends SimpleControllerSupport<Employee, EmployeeController.SaveItem, EmployeeController.ListItem, EmployeeController.DetailItem> {
|
||||||
|
|
||||||
|
private final EmployeeService service;
|
||||||
|
|
||||||
|
public EmployeeController(EmployeeService service) {
|
||||||
|
super(service);
|
||||||
|
this.service = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Function<SaveItem, Employee> saveItemMapper() {
|
||||||
|
return item -> {
|
||||||
|
Employee entity = new Employee();
|
||||||
|
entity.setId(item.id());
|
||||||
|
entity.setName(item.name());
|
||||||
|
entity.setDepartmentId(item.departmentId());
|
||||||
|
entity.setEmail(item.email());
|
||||||
|
return entity;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Function<Employee, ListItem> listItemMapper() {
|
||||||
|
return entity -> new ListItem(
|
||||||
|
entity.getId(), entity.getName(), entity.getDepartmentId(),
|
||||||
|
entity.getEmail(), entity.getCreatedTime()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Function<Employee, DetailItem> detailItemMapper() {
|
||||||
|
return entity -> new DetailItem(
|
||||||
|
entity.getId(), entity.getName(), entity.getDepartmentId(),
|
||||||
|
entity.getEmail(), entity.getCreatedTime(), entity.getModifiedTime()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public record SaveItem(Long id, String name, Long departmentId, String email) {}
|
||||||
|
public record ListItem(Long id, String name, Long departmentId, String email, LocalDateTime createdTime) {}
|
||||||
|
public record DetailItem(Long id, String name, Long departmentId, String email, LocalDateTime createdTime, LocalDateTime modifiedTime) {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
实现三个 Mapper:`saveItemMapper()`, `listItemMapper()`, `detailItemMapper()`
|
||||||
|
|
||||||
|
## 代码生成
|
||||||
|
|
||||||
|
```java
|
||||||
|
DatabaseHelper.generateBasicFiles(
|
||||||
|
"com.example.entity", // 实体包
|
||||||
|
"com.example", // 项目根包
|
||||||
|
"./src/main/java/com/example", // 源码路径
|
||||||
|
false // 是否覆盖
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
生成 Repository、Service、Controller。
|
||||||
|
|
||||||
|
## API 接口(Web 应用)
|
||||||
|
|
||||||
|
### POST /{entity}/save
|
||||||
|
|
||||||
|
保存/更新实体。
|
||||||
|
|
||||||
|
请求(新增):
|
||||||
|
```json
|
||||||
|
{"name": "张三", "departmentId": 1, "email": "zhangsan@example.com"}
|
||||||
|
```
|
||||||
|
|
||||||
|
请求(更新):
|
||||||
|
```json
|
||||||
|
{"id": 123456789, "name": "李四"}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应:
|
||||||
|
```json
|
||||||
|
{"status": 0, "message": "OK", "data": 123456789}
|
||||||
|
```
|
||||||
|
|
||||||
|
特性:不传 id 为新增,传 id 为更新(仅更新非 null 字段)。
|
||||||
|
|
||||||
|
### GET/POST /{entity}/list
|
||||||
|
|
||||||
|
GET:获取全部列表
|
||||||
|
|
||||||
|
POST:条件查询
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"query": {
|
||||||
|
"equal": {"departmentId": 1},
|
||||||
|
"like": {"name": "%张%"},
|
||||||
|
"greatEqual": {"createdTime": "2026-01-01 00:00:00"}
|
||||||
|
},
|
||||||
|
"sort": [{"column": "createdTime", "direction": "DESC"}],
|
||||||
|
"page": {"index": 1, "size": 20}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
响应:
|
||||||
|
```json
|
||||||
|
{"status": 0, "message": "OK", "data": {"items": [...], "total": 100}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### GET /{entity}/detail/{id}
|
||||||
|
|
||||||
|
响应:
|
||||||
|
```json
|
||||||
|
{"status": 0, "message": "OK", "data": {"id": 123, "name": "张三", ...}}
|
||||||
|
```
|
||||||
|
|
||||||
|
ID 不存在返回 500。
|
||||||
|
|
||||||
|
### GET /{entity}/remove/{id}
|
||||||
|
|
||||||
|
响应:
|
||||||
|
```json
|
||||||
|
{"status": 0, "message": "OK", "data": null}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 查询条件
|
||||||
|
|
||||||
|
### Query 结构
|
||||||
|
|
||||||
|
```java
|
||||||
|
Query(
|
||||||
|
query: Queryable, // 查询条件
|
||||||
|
sort: List<Sortable>, // 排序
|
||||||
|
page: Pageable // 分页
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 查询操作
|
||||||
|
|
||||||
|
| 操作 | 类型 | 示例 |
|
||||||
|
|---|---|---|
|
||||||
|
| equal | Map | `{"name": "张三"}` |
|
||||||
|
| notEqual | Map | `{"status": "DELETED"}` |
|
||||||
|
| like | Map | `{"name": "%张%"}` |
|
||||||
|
| contain | Map | `{"name": "张"}` → `%张%` |
|
||||||
|
| startWith | Map | `{"name": "张"}` → `张%` |
|
||||||
|
| endWith | Map | `{"name": "三"}` → `%三` |
|
||||||
|
| great/greatEqual | Map | `{"age": 18}` |
|
||||||
|
| less/lessEqual | Map | `{"age": 60}` |
|
||||||
|
| between | Map | `{"age": {"start": 18, "end": 60}}` |
|
||||||
|
| inside | Map | `{"id": [1, 2, 3]}` |
|
||||||
|
| notInside | Map | `{"status": ["DELETED"]}` |
|
||||||
|
| nullEqual | List | `["deletedAt"]` |
|
||||||
|
| notNullEqual | List | `["email"]` |
|
||||||
|
|
||||||
|
### 排序
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"sort": [{"column": "createdTime", "direction": "DESC"}]}
|
||||||
|
```
|
||||||
|
|
||||||
|
direction: `ASC` 升序,`DESC` 降序
|
||||||
|
|
||||||
|
### 分页
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"page": {"index": 1, "size": 20}}
|
||||||
|
```
|
||||||
|
|
||||||
|
index 从 1 开始,默认 `(1, 10)`,无排序默认 `createdTime DESC`
|
||||||
|
|
||||||
|
## 高级用法
|
||||||
|
|
||||||
|
### 扩展 Service
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Service
|
||||||
|
public class EmployeeService extends SimpleServiceSupport<Employee> {
|
||||||
|
private final EmployeeRepository repository;
|
||||||
|
|
||||||
|
public EmployeeService(EmployeeRepository repository) {
|
||||||
|
super(repository);
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义方法
|
||||||
|
public List<Employee> findByDepartmentId(Long departmentId) {
|
||||||
|
return repository.findAll(
|
||||||
|
(root, query, builder) -> builder.equal(root.get("departmentId"), departmentId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 全局过滤条件
|
||||||
|
@Override
|
||||||
|
protected Predicate commonPredicates(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
|
||||||
|
return builder.equal(root.get("deleted"), false); // 软删除过滤
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### QueryDSL 查询
|
||||||
|
|
||||||
|
```java
|
||||||
|
public List<Employee> findActiveEmployees() {
|
||||||
|
QEmployee q = QEmployee.employee;
|
||||||
|
return repository.findAll(q.status.eq("ACTIVE").and(q.deleted.isFalse()));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### MapStruct Mapper
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Mapper
|
||||||
|
public interface EmployeeMapper {
|
||||||
|
Employee toEntity(SaveItem item);
|
||||||
|
ListItem toListItem(Employee entity);
|
||||||
|
DetailItem toDetailItem(Employee entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controller 中使用
|
||||||
|
@Override
|
||||||
|
protected Function<SaveItem, Employee> saveItemMapper() {
|
||||||
|
return mapper::toEntity;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 实体设计
|
||||||
|
|
||||||
|
### 字段类型
|
||||||
|
|
||||||
|
- ID: `Long`(雪花算法)
|
||||||
|
- 时间: `LocalDateTime`
|
||||||
|
- 金额: `BigDecimal`
|
||||||
|
- 枚举: Java enum(存储为字符串)
|
||||||
|
- 布尔: `Boolean`
|
||||||
|
|
||||||
|
### 关联关系
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Entity
|
||||||
|
public class Order extends SimpleEntity {
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "customer_id")
|
||||||
|
private Customer customer;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
|
||||||
|
private List<OrderItem> items;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:谨慎使用 `@OneToMany`,可能导致 N+1 问题。
|
||||||
|
|
||||||
|
### 索引
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Entity
|
||||||
|
@Table(name = "employee", indexes = {
|
||||||
|
@Index(name = "idx_department", columnList = "department_id")
|
||||||
|
})
|
||||||
|
public class Employee extends SimpleEntity { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
## 工具类
|
||||||
|
|
||||||
|
### DatabaseHelper
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 生成 DDL
|
||||||
|
DatabaseHelper.generateDDL(
|
||||||
|
"com.example.entity", "./sql", MySQL8Dialect.class,
|
||||||
|
"jdbc:mysql://localhost:3306/test", "root", "password",
|
||||||
|
com.mysql.cj.jdbc.Driver.class
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### SnowflakeHelper
|
||||||
|
|
||||||
|
```java
|
||||||
|
Long id = SnowflakeHelper.next();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
**Q: 非 Web 应用如何使用?**
|
||||||
|
|
||||||
|
A: 不引入 web 依赖,创建 Entity → Repository → Service,直接注入 Service 使用。
|
||||||
|
|
||||||
|
**Q: 如何实现软删除?**
|
||||||
|
|
||||||
|
A: 添加 `deleted` 字段,重写 `commonPredicates()` 过滤,覆盖 `remove()` 改为更新。
|
||||||
|
|
||||||
|
**Q: 如何处理复杂查询?**
|
||||||
|
|
||||||
|
A: 使用 QueryDSL 或 Repository `@Query` 方法:
|
||||||
|
```java
|
||||||
|
@Query("SELECT e FROM Employee e WHERE e.departmentId = :deptId")
|
||||||
|
List<Employee> findByDepartment(@Param("deptId") Long deptId);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Q: 如何批量插入?**
|
||||||
|
|
||||||
|
A: `service.save(entities)` 或 `repository.saveAll(entities)`
|
||||||
|
|
||||||
|
**Q: 查询条件支持关联对象吗?**
|
||||||
|
|
||||||
|
A: 支持,使用多级路径如 `"department.name"`
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
1. DTO 设计:SaveItem 可修改字段,ListItem 列表字段,DetailItem 完整字段
|
||||||
|
2. 事务:Service 方法已加事务,无需重复
|
||||||
|
3. 性能:列表查询避免关联对象,使用投影或 DTO
|
||||||
|
4. 代码生成:初期脚手架生成,后期手动调整
|
||||||
|
|
||||||
|
## 测试用例
|
||||||
|
|
||||||
|
`src/test/java/.../integration/`
|
||||||
Reference in New Issue
Block a user