9.2 KiB
Database 模块使用指南
单表 CRUD → REST 接口快速实现框架。
使用模式
Web 应用
引入 database 模块,创建 Entity → Repository → Service → Controller,实现 REST 接口。
非 Web 应用
无需引入 web 依赖(database 模块的 spring-boot-starter-web scope 为 provided)。
仅使用 Entity → Repository → Service,直接注入 Service 使用:
@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. 添加依赖
<dependency>
<groupId>com.lanyuanxiaoyao</groupId>
<artifactId>spring-boot-service-template-database</artifactId>
</dependency>
2. 创建实体
@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
@Repository
public interface EmployeeRepository extends SimpleRepository<Employee> {}
继承能力:CRUD、分页、Specification、QueryDSL、Example。
4. 创建 Service
@Slf4j
@Service
public class EmployeeService extends SimpleServiceSupport<Employee> {
public EmployeeService(EmployeeRepository repository) {
super(repository);
}
}
自动获得完整 CRUD 能力。
5. 创建 Controller(Web 应用)
@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()
代码生成
DatabaseHelper.generateBasicFiles(
"com.example.entity", // 实体包
"com.example", // 项目根包
"./src/main/java/com/example", // 源码路径
false // 是否覆盖
);
生成 Repository、Service、Controller。
API 接口(Web 应用)
POST /{entity}/save
保存/更新实体。
请求(新增):
{"name": "张三", "departmentId": 1, "email": "zhangsan@example.com"}
请求(更新):
{"id": 123456789, "name": "李四"}
响应:
{"status": 0, "message": "OK", "data": 123456789}
特性:不传 id 为新增,传 id 为更新(仅更新非 null 字段)。
GET/POST /{entity}/list
GET:获取全部列表
POST:条件查询
{
"query": {
"equal": {"departmentId": 1},
"like": {"name": "%张%"},
"greatEqual": {"createdTime": "2026-01-01 00:00:00"}
},
"sort": [{"column": "createdTime", "direction": "DESC"}],
"page": {"index": 1, "size": 20}
}
响应:
{"status": 0, "message": "OK", "data": {"items": [...], "total": 100}}
GET /{entity}/detail/{id}
响应:
{"status": 0, "message": "OK", "data": {"id": 123, "name": "张三", ...}}
ID 不存在返回 500。
GET /{entity}/remove/{id}
响应:
{"status": 0, "message": "OK", "data": null}
查询条件
Query 结构
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"] |
排序
{"sort": [{"column": "createdTime", "direction": "DESC"}]}
direction: ASC 升序,DESC 降序
分页
{"page": {"index": 1, "size": 20}}
index 从 1 开始,默认 (1, 10),无排序默认 createdTime DESC
高级用法
扩展 Service
@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 查询
public List<Employee> findActiveEmployees() {
QEmployee q = QEmployee.employee;
return repository.findAll(q.status.eq("ACTIVE").and(q.deleted.isFalse()));
}
MapStruct Mapper
@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
关联关系
@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 问题。
索引
@Entity
@Table(name = "employee", indexes = {
@Index(name = "idx_department", columnList = "department_id")
})
public class Employee extends SimpleEntity { ... }
工具类
DatabaseHelper
// 生成 DDL
DatabaseHelper.generateDDL(
"com.example.entity", "./sql", MySQL8Dialect.class,
"jdbc:mysql://localhost:3306/test", "root", "password",
com.mysql.cj.jdbc.Driver.class
);
SnowflakeHelper
Long id = SnowflakeHelper.next();
常见问题
Q: 非 Web 应用如何使用?
A: 不引入 web 依赖,创建 Entity → Repository → Service,直接注入 Service 使用。
Q: 如何实现软删除?
A: 添加 deleted 字段,重写 commonPredicates() 过滤,覆盖 remove() 改为更新。
Q: 如何处理复杂查询?
A: 使用 QueryDSL 或 Repository @Query 方法:
@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"
最佳实践
- DTO 设计:SaveItem 可修改字段,ListItem 列表字段,DetailItem 完整字段
- 事务:Service 方法已加事务,无需重复
- 性能:列表查询避免关联对象,使用投影或 DTO
- 代码生成:初期脚手架生成,后期手动调整
测试用例
src/test/java/.../integration/