SpringBoot:员工管理系统(基本结构)

SpringBoot_staff

写一个简单的员工管理系统,来体验 Spring Boot 开发的整个流程。

1、项目结构

  1. 创建 Spring Boot Web 项目:springboot_04_staff

  2. 导入依赖

    • 数据库JDBCMySQL 连接、Druid 数据源、MyBatis
    • 模板引擎Thymeleaf
  3. 完善包结构

    • pojo
    • dao
    • service
    • controller
    • config
    • constant
    • utils
  4. 导入静态资源

    • static 目录:存放资源文件,如 CSSJS...
    • templates 目录:存放 HTML 页面

    image-20211018235047805

2、数据库表、实体类

2.1、注意

2.1.1、编程规范

  1. ORM原则
  2. 命名风格
    • 数据库:小写字母,下划线命名;
    • 实体类:大驼峰命名;
  3. 实体类
    • 不需要逻辑主键,需要创建时间和更新时间;
    • 必须使用包装数据类型;
    • 根据需要添加constructortoString()getter/setter,可以全部添加

2.1.2、注意事项

  1. 多对一关系:员工对部门

  2. 员工实体中对部门的表示

    • 数据库- employee:部门ID——department_id
    • 实体类- Employee:部门——Department
  3. 映射问题

    • 命名风格映射;
    • department_idDepartment 的映射。
  4. 关于日期时间的属性说明

    • 在数据库,日期使用 date类型,时间使用 timestamp类型;
    • 在后端,日期和时间均使用 java.util.Date类型
    • 为了便于管理,通过后台代码完成(Service层)有关日期时间的操作,而不是直接在数据库端操作。

2.2、数据库springboot_staff

数据库表必备三字段阿里巴巴规范

  • id逻辑主键。bigint,无符号自增;
  • create_time创建时间。timestamp,非空(新增记录时通过后端代码生成)
  • update_time更新时间。timestamp,默认为空(更新记录时通过后端代码生成)

主键

  • 逻辑主键
    • 无实际含义,用于唯一标识数据库表的每个记录。
    • 是表的主键PK,即 id;
    • 仅在数据库中体现,实体类不需要该属性;
  • 业务主键
    • 有实际含义,用于标识不同的业务实体;
    • 是我们理解上的主键,如用户的 user_id,游戏的 game_id等。

部门表department

  • department_id业务主键,部门ID

  • name:部门名

    image-20211127005111615

员工表employee

部门和员工是一对多的关系

  • employee_id业务主键,员工ID

  • name:员工名

  • email:邮箱

  • gender:性别

  • birthday:生日

  • department_id在员工数据库表中,用 ID表示部门

    image-20211127010553757

2.3、实体类

部门类Department

public class Department {
    /**
     * 创建时间
     */
    private Date createTime;
    /**
     * 更新时间
     */
    private Date updateTime;
    /**
     * 部门ID
     */
    private String departmentId;
    /**
     * 部门名
     */
    private String name;
}

员工类Employee

  • 在员工实体类中,用实体类表示部门
public class Employee {
    /**
     * 创建时间
     */
    private Date createTime;
    /**
     * 更新时间
     */
    private Date updateTime;
    /**
     * 员工ID
     */
    private String employeeId;
    /**
     * 员工名
     */
    private String name;
    /**
     * 邮箱
     */
    private String email;
    /**
     * 性别
     */
    private Integer gender;
    /**
     * 生日
     */
    private Date birthday;
   
    /**
     * 部门
     */
    private Department department;
}

3、工具类

3.1、工具类

  • 返回当前时间戳
public class TimeUtils {
    /**
     * 返回当前时间戳
     *
     * @return 当前时间戳
     */
    public static Date getCurrentTime() {
        return new Date(System.currentTimeMillis());
    }
}

3.2、常量类

  • 当前时间
  • 员工登录Session
  • 有需要可以添加枚举类:xxxEnum
public class CommonConstant {
    /**
     * 当前时间
     */
    public static final Date CURRENT_TIME = MyDateUtils.getCurrentTime();
}

public class EmployeeConstant {
    /**
     * 员工登录Session
     */
    public static final String LOGIN_EMPLOYEE = "loginEmployee";
}

4、整合MyBatis

即整合了 MyBatis后的 DAO层10.3、整合 MyBatis

4.1、导入依赖

<!-- JDBC -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- MySQL连接 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<!-- Druid数据源 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.8</version>
</dependency>
<!-- SpringBoot整合MyBatis -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.0</version>
</dependency>
<!-- log4j日志 -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

4.2、数据库配置文件

  • JDBC 数据库配置
  • Druid 数据源
  • MyBatis

application.yaml

spring:
  datasource:
    username: root
    password: 密码
    url: jdbc:mysql://主机地址/数据库名?useUnicode=true&characterEncoding=utf8&useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver
    
    # Druid数据源
    type: com.alibaba.druid.pool.DruidDataSource
    # Druid数据源配置
    initialSize: 5
    maxActive: 20
    minIdle: 5
    maxWait: 60000
    poolPreparedStatements: true
    maxPoolPreparedStatementPerConnectionSize: 20
    validationQuery: SELECT 1 FROM DUAL
    testOnBorrow: false
    testOnReturn: false
    testWhileIdle: true
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    # filters:stat监控统计、log4j日志记录(需要导入log4j依赖)、wall防御sql注入
    filters: stat,wall,log4j
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
    
# 整合MyBatis配置
mybatis:
  type-aliases-package: indi.jaywee.pojo
  # Mapper.xml放在resources目录下与mapper同名的包下
  mapper-locations: classpath:indi/jaywee/mapper/*.xml
  # 自动映射:数据库下划线命名<-->实体类驼峰命名
  configuration:
    map-underscore-to-camel-case: true

5、DAO、service层

5.1、Mapper

  1. Mapper 类上方添加@Mapper注解;
  2. 插入语句:需要添加 create_time,该属性由 service层为对象设置,DAO层负责编写相关的SQL语句即可;
  3. 更新语句
    • 以实体类作为参数,对象中存放业务主键需要修改的新属性值(具体见EmployeeMapper);
    • 需要修改 update_time,该属性由 service层为对象设置,DAO层负责编写相关的SQL语句即可。
  4. 先编写基本的CRUD,后面再根据需求来添加方法;

DepartmentMapper

@Mapper
public interface DepartmentMapper {
    /**
     * 根据ID查询部门
     *
     * @param departmentId 部门ID
     * @return 查询部门
     */
    Department getDepartment(String departmentId);

    /**
     * 查询所有部门
     *
     * @return 部门列表
     */
    List<Department> listDepartments();
}

EmployeeMapper

更新语句:改名

  • 以Employee实体类作为参数
    • 对象中存放业务主键employeeId和新名称name;
    • 具体实现:只需要临时创建一个员工,为该员工设置employeeId和name两个属性即可;
  • update_time由 service层为上述临时员工设置,DAO层负责编写相关的SQL语句即可。
/**
 * 添加员工
 *
 * @param employee 待添加员工
 * @return 受影响行数
 */
int insertEmployee(Employee employee);

/**
 * 删除员工
 *
 * @param employeeId 员工ID
 * @return 受影响行数
 */
int deleteEmployee(String employeeId);

/**
 * 更新员工:改名
 *
 * @param employee 待更新员工:主要用到员工ID和新名称
 * @return 受影响行数
 */
int updateEmployee(Employee employee);

/**
 * 根据ID查询员工
 *
 * @param employeeId 员工ID
 * @return 待查询员工
 */
Employee getEmployee(String employeeId);

/**
 * 查询所有员工
 *
 * @return 员工列表
 */
List<Employee> listEmployees();

5.2、Mapper.xml(!)

  1. 绑定对应接口namespace="xxx"
  2. 命名映射配置下划线命名和驼峰命名的自动映射
  3. SQL语句注意
    1. 区分命名风格:数据库字段用下划线,实体类属性用驼峰命名;
    2. 区分主键:逻辑主键id无实际含义,业务主键有业务逻辑含义;
    3. 不要使用select *,要将需要查询的字段全写出来(即使是所有字段);
  4. 难点Employee 的部门字段的映射

DepartmentMapper.xml

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 接口绑定 -->
<mapper namespace="indi.jaywee.mapper.DepartmentMapper">
    <!-- 根据ID查询部门 -->
    <select id="getDepartment" resultType="department">
        SELECT create_time,
               update_time,
               department_id,
               name
        FROM springboot_staff.department
        WHERE department_id = #{departmentId}
    </select>
    <!-- 查询所有部门 -->
    <select id="listDepartments" resultType="department">
        SELECT create_time,
               update_time,
               department_id,
               name
        FROM springboot_staff.department
    </select>
</mapper>

EmployeeMapper.xml(!)

  1. insert
    • 需要插入 create_time,不需要插入 update_time;
    • 要求实体类Employee 具有 getDepartmentId() 方法,否则无法获取并插入departmentId;
  2. select
    1. 使用 resultMap 处理结果集映射
    2. 使用 association + 子查询,来映射部门字段(有关说明见MyBatis的多对一和一对多
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 接口绑定 -->
<mapper namespace="indi.jaywee.mapper.EmployeeMapper">

    <!-- 添加员工 -->
    <insert id="insertEmployee">
        INSERT INTO springboot_staff.employee(create_time, employee_id,
                                              name, email, gender, birthday, department_id)
        VALUES (#{createTime}, #{employeeId},
                #{name}, #{email}, #{gender}, #{birthday}, #{departmentId})
    </insert>

    <!-- 更新员工:改名 -->
    <update id="updateEmployee">
        UPDATE springboot_staff.employee
        SET update_time = #{updateTime},
            name        = #{name}
        WHERE employee_id = #{employeeId}
    </update>

    <!-- 删除员工 -->
    <delete id="deleteEmployee">
        DELETE
        FROM springboot_staff.employee
        WHERE employee_id = #{employeeId}
    </delete>

    <!-- 查询员工 -->
    <select id="getEmployee" resultMap="employeeMap">
        SELECT create_time,
               update_time,
               employee_id,
               name,
               email,
               gender,
               birthday,
               department_id
        FROM springboot_staff.employee
        WHERE employee_id = #{employeeId}
    </select>

    <!-- 查询所有员工-->
    <select id="listEmployees" resultMap="employeeMap">
        SELECT create_time,
               update_time,
               employee_id,
               name,
               email,
               gender,
               birthday,
               department_id
        FROM springboot_staff.employee
    </select>

    <!-- 结果映射:数据库department_id与实体类Department -->
    <resultMap id="employeeMap" type="employee">
        <association property="department" column="department_id" javaType="Department" select="getDepartment"/>
    </resultMap>
    <select id="getDepartment" resultType="department">
        SELECT create_time, update_time, name, department_id
        FROM springboot_staff.department
        WHERE department_id = #{department_id}
    </select>
</mapper>

5.3、service

  1. 接口类
    • 不需要添加注解;
    • 方法通常和相应的Mapper接口方法相同;
  2. 接口实现类
    • 使用@Service注解,将Service实现类注册到容器中;
    • 注入相应的Mapper接口类,用于调用DAO层方法;
    • 处理时间属性
      • 新增:设置 create_time属性;
      • 更新:设置 update_time属性;

DepartmentService

  • 不需要添加注解;
  • 接口方法:同DepartmenMapper。
public interface DepartmentService {
    /**
     * 根据ID查询部门
     *
     * @param departmentId 部门ID
     * @return 查询部门
     */
    Department getDepartment(String departmentId);

    /**
     * 查询所有部门
     *
     * @return 部门列表
     */
    List<Department> listDepartments();

EmployeeService

  • 不需要添加注解;
  • 接口方法:同DepartmenMapper。
/**
 * 添加员工
 *
 * @param employee 待添加员工
 * @return 受影响行数
 */
int insertEmployee(Employee employee);

/**
 * 删除员工
 *
 * @param employeeId 员工ID
 * @return 受影响行数
 */
int deleteEmployee(String employeeId);

/**
 * 更新员工:改名
 *
 * @param employee 待更新员工:主要用到员工ID和新名称
 * @return 受影响行数
 */
int updateEmployee(Employee employee);

/**
 * 根据ID查询员工
 *
 * @param employeeId 员工ID
 * @return 待查询员工
 */
Employee getEmployee(String employeeId);

/**
 * 查询所有员工
 *
 * @return 员工列表
 */
List<Employee> listEmployees();

5.4、serviceImpl

DepartmentServiceImpl

  • 使用@Service注解,将Service实现类注册到容器中
  • 注入DepartmentMapper,用于调用DAO层方法;
@Service
public class DepartmentServiceImpl implements DepartmentService {
    /**
     * Service层调用DAO层方法
     */
    @Resource
    DepartmentMapper departmentMapper;


    @Override
    public Department getDepartment(String departmentId) {
        return departmentMapper.getDepartment(departmentId);
    }

    @Override
    public List<Department> listDepartments() {
        return departmentMapper.listDepartments();
    }
}

EmployeeServiceImpl

  • 使用@Service注解,将Service实现类注册到容器中;
  • 注入EmployeeMapper,用于调用DAO层方法;
  • 处理时间属性
    • 新增:设置 create_time属性;
    • 更新:设置 update_time属性;
@Service
public class EmployeeServiceImpl implements EmployeeService {
    /**
     * Service层调用DAO层方法
     */
    @Resource
    EmployeeMapper employeeMapper;

    @Override
    public int insertEmployee(Employee employee) {
        employee.setCreateTime(CommonConstant.CURRENT_TIME);

        return employeeMapper.insertEmployee(employee);
    }

    @Override
    public int deleteEmployee(String employeeId) {
        return employeeMapper.deleteEmployee(employeeId);
    }

    @Override
    public int updateEmployee(Employee employee) {
        employee.setUpdateTime(CommonConstant.CURRENT_TIME);
        
        return employeeMapper.updateEmployee(employee);
    }

    @Override
    public Employee getEmployee(String employeeId) {
        return employeeMapper.getEmployee(employeeId);
    }

    @Override
    public List<Employee> listEmployees() {
        return employeeMapper.listEmployees();
    }

}

编写完 Service层后,创建相应的 ServiceTest 类,测试是否能正常运行

6、首页和错误页面

6.1、模板引擎

  • 导入 Thymeleafstarterspring-boot-starter-thymeleaf
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

6.2、首页:视图控制器

SpringBoot 8.3、欢迎页面

  • 默认启动 Spring Boot 项目时,在没有设置首页的情况下,打开的是错误页面 Whitelabel Error Page
  • 由于使用 Thymeleaf 模板引擎,只能映射 template 目录下的页面,因此需要显式地设置首页的视图控制器

自定义SpringMVC

:创建自定义 SpringMVC 配置类

  • 实现WebMvcConfigurer接口的@Configuration
  • 添加视图控制器:访问//index.html,会映射到 template 目录下的index.html
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 欢迎页面
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
    }
}

image-20211022151033594

6.3、错误页面:error

  1. template 目录下新建 error 目录
  2. 创建错误页面:404、500

image-20211111153556527

posted @ 2021-10-22 14:58  Jaywee  阅读(1355)  评论(0编辑  收藏  举报

👇