Skip to content

05:数据层:数据库、Entity、Mapper、Provider

1. 先理解“表”和“Entity”

数据库表保存数据,Entity 是这张表在 Java 中的表达。

Demo 表:

text
demo_customer

Demo Entity:

text
entity/DemoCustomer.java

字段映射例子:

表字段Java 字段含义
customer_namecustomerName客户名称
contact_personcontactPerson联系人
created_atcreatedAt创建时间

map-underscore-to-camel-case: true 使 MyBatis 能自动完成 customer_name -> customerName 映射。

2. Demo 数据库脚本如何组织

Demo 有三类 SQL 文件:

文件作用
sql/hc-my-demo.sql手工创建数据库、表和种子数据
schema.sql应用启动时保证表存在
data.sql应用启动时保证测试数据存在

生产项目通常不会在应用启动时自动插入演示数据。正式项目的表变更需要遵循团队的数据库变更流程,确认环境和迁移工具后再执行。

3. 设计一张表的最小原则

创建表前先回答:

  1. 这张表的一行到底代表什么?
  2. 主键是什么?
  3. 哪些字段必填?
  4. 哪些字段需要索引?
  5. 删除是物理删除还是逻辑删除?
  6. 它和其他表是什么关系?

例如,你下一步可以创建客户联系人表:

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;
  • 日期字段优先用 LocalDateLocalDateTime,不要随意混用 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. 本章练习

  1. demo_customer_contact 建表 SQL 加入 schema.sql 和完整初始化脚本。
  2. 新建 DemoCustomerContact Entity。
  3. 新建 DemoCustomerContactMapper
  4. 写一个临时测试:按 customerId=1 查询联系人列表。
  5. 检查 SQL:联系人表必须有 customer_id 索引。

验收:mvn test 通过,MySQL 中能看到两张表。

Lucking