SpringBoot_staff
寫一個簡單的員工管理系統,來體驗 Spring Boot 開發的整個流程。
1、項目結構
-
創建 Spring Boot Web 項目:
springboot_04_staff
-
導入依賴
- 數據庫:JDBC、MySQL 連接、Druid 數據源、MyBatis
- 模板引擎:Thymeleaf
-
完善包結構
- pojo
- dao
- service
- controller
- config
- constant
- utils
-
導入靜態資源
- static 目錄:存放資源文件,如 CSS、JS...
- templates 目錄:存放 HTML 頁面
2、數據庫表、實體類
2.1、注意
2.1.1、編程規范
- ORM原則
- 命名風格
- 數據庫:小寫字母,下划線命名;
- 實體類:大駝峰命名;
- 實體類
- 不需要邏輯主鍵,需要創建時間和更新時間;
- 必須使用包裝數據類型;
- 根據需要添加
constructor
、toString()
、getter/setter
,可以全部添加
2.1.2、注意事項
-
多對一關系:員工對部門
-
員工實體中對部門的表示
- 數據庫- employee 表:部門ID——department_id;
- 實體類- Employee 類:部門——Department;
-
映射問題
- 命名風格映射;
- department_id 和 Department 的映射。
-
關於日期時間的屬性說明
- 在數據庫,日期使用 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
:部門名
員工表employee
部門和員工是一對多的關系
-
employee_id
:業務主鍵,員工ID -
name
:員工名 -
email
:郵箱 -
gender
:性別 -
birthday
:生日 -
department_id
:在員工數據庫表中,用 ID表示部門
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
- Mapper 類上方添加
@Mapper
注解; - 插入語句:需要添加 create_time,該屬性由 service層為對象設置,DAO層負責編寫相關的SQL語句即可;
- 更新語句
- 以實體類作為參數,對象中存放業務主鍵和需要修改的新屬性值(具體見EmployeeMapper);
- 需要修改 update_time,該屬性由 service層為對象設置,DAO層負責編寫相關的SQL語句即可。
- 先編寫基本的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(!)
- 綁定對應接口:
namespace="xxx"
; - 命名映射配置:下划線命名和駝峰命名的自動映射;
- SQL語句注意
- 區分命名風格:數據庫字段用下划線,實體類屬性用駝峰命名;
- 區分主鍵:邏輯主鍵id無實際含義,業務主鍵有業務邏輯含義;
- 不要使用
select *
,要將需要查詢的字段全寫出來(即使是所有字段);
- 難點: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(!)
- insert
- 需要插入 create_time,不需要插入 update_time;
- 要求實體類Employee 具有 getDepartmentId() 方法,否則無法獲取並插入departmentId;
- select
- 使用 resultMap 處理結果集映射
- 使用 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
- 接口類:
- 不需要添加注解;
- 方法通常和相應的Mapper接口方法相同;
- 接口實現類:
- 使用
@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、模板引擎
- 導入 Thymeleaf 的 starter:
spring-boot-starter-thymeleaf
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
6.2、首頁:視圖控制器
- 默認啟動 Spring Boot 項目時,在沒有設置首頁的情況下,打開的是錯誤頁面 Whitelabel Error Page;
- 由於使用 Thymeleaf 模板引擎,只能映射 template 目錄下的頁面,因此需要顯式地設置首頁的視圖控制器
:創建自定義 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");
}
}
6.3、錯誤頁面:error
- 在 template 目錄下新建 error 目錄
- 創建錯誤頁面:404、500