2、后端项目初始化与数据库设计

好,咱们正式开始动手了。这一章我会带你从零搭建Spring Boot项目,顺便把MySQL装好、配好,再把OA系统的核心表结构设计出来。最后,用JPA逆向生成实体类——这一步能省不少体力活。

我个人习惯是先搭好项目骨架,再设计数据库。为什么呢?因为Spring Boot项目本身不依赖数据库也能跑,先确认项目没问题,再回头搞数据库,排查问题会清晰很多。

2.1 Spring Boot项目搭建

我用的是Spring Initializr,说白了就是start.spring.io这个网站。你打开它,选好Java版本、Spring Boot版本,然后勾上几个依赖就行。

我建议你选这些依赖:

  • Spring Web —— 做REST API必备
  • Spring Data JPA —— 操作数据库的利器
  • MySQL Driver —— 连MySQL用的
  • Lombok —— 少写getter/setter,代码清爽

生成后下载下来,用IDEA打开。嗯,这里要注意:如果你用的是IDEA社区版,记得装Lombok插件,不然会报错。

核心配置:application.yml

我个人习惯用yml格式,比properties更直观。下面是我常用的配置模板:

server:
  port: 8080

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/oa_system?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf-8
    username: root
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver

  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        format_sql: true
        dialect: org.hibernate.dialect.MySQL8Dialect

这里有个坑,我曾经遇到过:ddl-auto: update 在开发阶段很方便,但生产环境千万别用。它会自动改表结构,万一误操作,数据就没了。生产环境建议用 validate 或者手动管理。

2.2 MySQL安装与配置

MySQL的安装我就不一步步截图了,网上教程一大堆。我只说几个关键点:

  • 版本选择:我建议用MySQL 8.0+,性能更好,支持窗口函数
  • 字符集:建库时一定要用 utf8mb4,别问为什么,你想想看,万一用户名字里带个emoji,用utf8就存不进去了
  • 时区:设置成 Asia/Shanghai,不然时间差8小时,排查起来很头疼

建库语句很简单:

CREATE DATABASE oa_system 
  DEFAULT CHARACTER SET utf8mb4 
  DEFAULT COLLATE utf8mb4_unicode_ci;

小技巧:我习惯在MySQL的my.cnf里加上 sql_mode=STRICT_TRANS_TABLES,这样插入非法数据时会直接报错,而不是静默截断。调试阶段能省不少事。

2.3 OA系统数据库表结构设计

好,重头戏来了。OA系统的核心是权限管理,说白了就是「谁、能干什么、在哪个部门」。所以我们需要四张核心表:

  • 用户表(sys_user) —— 存登录账号、密码、基本信息
  • 部门表(sys_dept) —— 存组织架构
  • 角色表(sys_role) —— 存角色定义,比如管理员、普通员工
  • 权限表(sys_permission) —— 存具体的操作权限,比如「新增用户」、「删除公告」

再加上关联表:用户-角色、角色-权限。这样设计灵活,也符合RBAC(基于角色的访问控制)模型。

下面是我设计的表结构:

2.3.1 用户表(sys_user)

CREATE TABLE sys_user (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  username VARCHAR(50) NOT NULL UNIQUE,
  password VARCHAR(100) NOT NULL,
  real_name VARCHAR(50),
  email VARCHAR(100),
  phone VARCHAR(20),
  dept_id BIGINT,
  status TINYINT DEFAULT 1 COMMENT '1:启用 0:禁用',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

这里我特意加了 dept_id 字段,直接关联部门表。为什么不做成关联表?因为用户和部门是「多对一」关系,一个用户只能属于一个部门,直接加外键字段更简单。

2.3.2 部门表(sys_dept)

CREATE TABLE sys_dept (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  parent_id BIGINT DEFAULT 0,
  sort_order INT DEFAULT 0,
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);

部门表用了 parent_id 实现树形结构。嗯,这里要注意:如果部门层级很深,递归查询会慢。我建议最多支持3级,再深就用缓存或者冗余路径字段。

2.3.3 角色表(sys_role)

CREATE TABLE sys_role (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  code VARCHAR(50) NOT NULL UNIQUE,
  description VARCHAR(200),
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);

code 字段是角色的唯一标识,比如 ROLE_ADMINROLE_USER。我建议用这个字段做权限判断,而不是用id,因为id在数据库迁移时可能会变。

2.3.4 权限表(sys_permission)

CREATE TABLE sys_permission (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  permission VARCHAR(100) NOT NULL UNIQUE,
  type TINYINT COMMENT '1:菜单 2:按钮 3:API',
  parent_id BIGINT DEFAULT 0,
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);

permission 字段是权限标识,比如 user:createuser:delete。我习惯用冒号分隔资源+操作,这样一看就懂。

2.3.5 关联表

CREATE TABLE sys_user_role (
  user_id BIGINT NOT NULL,
  role_id BIGINT NOT NULL,
  PRIMARY KEY (user_id, role_id)
);

CREATE TABLE sys_role_permission (
  role_id BIGINT NOT NULL,
  permission_id BIGINT NOT NULL,
  PRIMARY KEY (role_id, permission_id)
);

避坑指南:我曾经在项目里把关联表的主键设成自增id,结果查询时多了一次索引查找。后来改成联合主键,性能提升明显。记住:关联表不需要自增id,直接用两个外键做联合主键就行。

2.4 JPA逆向生成实体类

表建好了,接下来用JPA自动生成实体类。Spring Data JPA支持从数据库表逆向生成,但说实话,IDEA自带的逆向工具更好用。

操作步骤很简单:

  1. 在IDEA中打开 Persistence 面板(View → Tool Windows → Persistence)
  2. 右键项目 → Generate Persistence Mapping → By Database Schema
  3. 选择数据源,勾选要生成的表
  4. 设置包名,比如 com.oa.entity
  5. 点击OK,实体类就自动生成了

生成后,你会看到类似这样的代码:

@Entity
@Table(name = "sys_user")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SysUser {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, unique = true)
    private String username;
    
    @Column(nullable = false)
    private String password;
    
    @Column(name = "real_name")
    private String realName;
    
    private String email;
    private String phone;
    
    @ManyToOne
    @JoinColumn(name = "dept_id")
    private SysDept dept;
    
    private Integer status;
    
    @Column(name = "create_time")
    private LocalDateTime createTime;
    
    @Column(name = "update_time")
    private LocalDateTime updateTime;
}

注意看,dept_id 字段被自动映射成了 @ManyToOne 关联。这就是JPA的便利之处——你不需要手动写关联查询,直接通过对象导航就能拿到部门信息。

个人经验:逆向生成后,我建议手动检查一下关联关系。比如 @ManyToOne 默认是懒加载(LAZY),但如果你在事务外访问关联对象,会报LazyInitializationException。我一般会在需要的地方加 @Transactional,或者改成 FetchType.EAGER——不过要小心性能问题。

另外,记得给实体类加上 @Data(Lombok的注解),这样getter、setter、toString、equals、hashCode全都有了。代码量直接减半。

好,到这一步,后端项目骨架搭好了,数据库表也设计好了,实体类也生成了。下一章我们会开始写具体的业务逻辑——用户登录、权限校验这些。你想想看,有了这些基础,后面是不是就顺理成章了?