Appearance
05:数据层:数据库、Entity、Mapper、Provider
1. 先理解“表”和“Entity”
数据库表保存数据,Entity 是这张表在 Java 中的表达。
Demo 表:
text
demo_customerDemo Entity:
text
entity/DemoCustomer.java字段映射例子:
| 表字段 | Java 字段 | 含义 |
|---|---|---|
customer_name | customerName | 客户名称 |
contact_person | contactPerson | 联系人 |
created_at | createdAt | 创建时间 |
map-underscore-to-camel-case: true 使 MyBatis 能自动完成 customer_name -> customerName 映射。
2. Demo 数据库脚本如何组织
Demo 有三类 SQL 文件:
| 文件 | 作用 |
|---|---|
sql/hc-my-demo.sql | 手工创建数据库、表和种子数据 |
schema.sql | 应用启动时保证表存在 |
data.sql | 应用启动时保证测试数据存在 |
生产项目通常不会在应用启动时自动插入演示数据。正式项目的表变更需要遵循团队的数据库变更流程,确认环境和迁移工具后再执行。
3. 设计一张表的最小原则
创建表前先回答:
- 这张表的一行到底代表什么?
- 主键是什么?
- 哪些字段必填?
- 哪些字段需要索引?
- 删除是物理删除还是逻辑删除?
- 它和其他表是什么关系?
例如,你下一步可以创建客户联系人表:
sql
CREATE TABLE demo_customer_contact (
id BIGINT NOT NULL AUTO_INCREMENT,
customer_id BIGINT NOT NULL,
contact_name VARCHAR(64) NOT NULL,
mobile VARCHAR(32) NOT NULL,
position_name VARCHAR(64),
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_customer_contact_customer_id (customer_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;customer_id 指向客户主表 id。这是一对多关系:一个客户可以有多个联系人。
4. Entity 的写法
联系人表对应:
java
@Data
@TableName("demo_customer_contact")
public class DemoCustomerContact {
@TableId(type = IdType.AUTO)
private Long id;
private Long customerId;
private String contactName;
private String mobile;
private String positionName;
private LocalDateTime createdAt;
}注意:
@TableName明确表名;@TableId指明主键及生成策略;- Java 里使用驼峰命名;
- 不要把 Form、VO、业务方法塞进 Entity;
- 日期字段优先用
LocalDate、LocalDateTime,不要随意混用Date。
正式 CRM 的 Entity 大多继承内部 BaseModel。它会统一提供 id、创建人、创建时间、删除标志等基础字段;Provider 查询也会自动增加未删除条件。Demo 先不用复制这套基础设施,但要理解正式表可能是逻辑删除而不是物理删除。
5. Mapper:只负责数据读写
MyBatis-Plus 的最小 Mapper:
java
public interface DemoCustomerContactMapper
extends BaseMapper<DemoCustomerContact> {
}BaseMapper 已包含常用方法:
| 方法 | 含义 |
|---|---|
selectById(id) | 按 id 查询 |
selectList(wrapper) | 按条件查多条 |
selectCount(wrapper) | 统计数量 |
insert(entity) | 新增 |
updateById(entity) | 按 id 修改 |
deleteById(id) | 按 id 删除 |
初学时优先使用这些方法。它们足以完成单表 CRUD。
6. 查询条件:LambdaQueryWrapper
不要手写字符串字段名:
java
// 不推荐:字段拼错只能运行时发现
wrapper.eq("customer_id", customerId);优先使用 Lambda:
java
LambdaQueryWrapper<DemoCustomerContact> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DemoCustomerContact::getCustomerId, customerId)
.orderByDesc(DemoCustomerContact::getId);
List<DemoCustomerContact> contacts = mapper.selectList(wrapper);这样重命名 Java 字段时,IDE 更容易发现错误。
7. Provider:什么时候才需要
正式 CRM 的 CustomerInfoProvider 用于:
- 根据
QueryModel动态生成筛选 SQL; - 客户主表与工商、品牌等表联表;
- 支持自定义表头、数据权限、复杂条件;
- 用 MyBatis-Plus 的
IPage分页。
Provider 的原则:只生成 SQL,不做业务决策。
在 Demo 中,先把筛选条件写在 Service 的 LambdaQueryWrapper。当你能解释每一个条件的 SQL 含义后,再练习将分页查询搬到 provider/DemoCustomerProvider。
不要为了“目录像正式项目”而过早增加 Provider;这只会让调试难度变高。
8. 本章练习
- 将
demo_customer_contact建表 SQL 加入schema.sql和完整初始化脚本。 - 新建
DemoCustomerContactEntity。 - 新建
DemoCustomerContactMapper。 - 写一个临时测试:按
customerId=1查询联系人列表。 - 检查 SQL:联系人表必须有
customer_id索引。
验收:mvn test 通过,MySQL 中能看到两张表。
