🔰 参考视频 🔰
狂神说Java Springboot,在其基础上进行了修改。
尚硅谷雷神SpringBoot2零基础入门springboot全套完整版(spring boot2),拦截器、文件上传。
🔰 项目仓库 🔰
https://gitee.com/zqcliudaliuda/person-management
主要功能:
- 简单的前端管理页面
- 员工信息的增删改查。
一、创建工程
1.1 创建springboot工程
步骤一:新建项目
步骤二:选择以下依赖
- Lombok
- Spring Web
- Thymeleaf
- Spring Data JDBC
- MySQL Driver
- MyBatis Framework
1.2 idea设置
1.2.1 File encoding编码
1.2.2 Lombok插件
1.3 整合Druid和MyBatis
步骤一:导入依赖
再导入druid和mybatis与springboot整合依赖到pom.xml
内:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
最终的pom.xml配置如下:https://gitee.com/zqcliudaliuda/person-management/blob/master/pom.xml
步骤二:编写配置文件
在src/main/resources
项目目录下,创建application.yml
,用于配置mysql数据库、druid和mybatis。
根据自己的设置,至少需要修改以下配置:
- 数据库:username、password、url
- mybatis:type-aliases-package
spring:
datasource:
# 一、mysql数据库的配置
username: root
password: 123456
url: jdbc:mysql://localhost:3306/pm?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
# 二、整合druid
druid:
# 1.连接池配置
# 初始化连接池的连接数量 大小,最小,最大
initialSize: 5
minIdle: 5
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 是否缓存preparedStatement,也就是PSCache,官方建议MySQL下建议关闭(个人建议如果想用SQL防火墙,建议打开)
poolPreparedStatements: true
# 2.基础监控配置
web-stat-filter:
enabled: true
url-pattern: /*
#设置不统计哪些URL
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
session-stat-enable: true
session-stat-max-count: 100
# 3.
stat-view-servlet:
enabled: true
url-pattern: /druid/*
reset-enable: true
#设置监控页面的登录名和密码
login-username: admin
login-password: 123456
# 可访问
allow: 127.0.0.1
# 不可访问
#deny: 192.168.1.100
# 四、配置日期格式
mvc:
format:
date: yyyy-MM-dd
# 三、整合mybatis
mybatis:
# pojo所在目录
type-aliases-package: com.zqc.personmanagement.pojo
# mapper所在目录
mapper-locations: classpath:mapper/*.xml
1.4 测试Druid
浏览器输入:http://localhost:8080/druid
弹出登录框,输入设置好的账号及密码(在application.yml
内)
登陆成功后出现一下页面,说明配置成功。
二、数据库
1.1 创建数据库
CREATE DATABASE pm;
DROP TABLE IF EXISTS t_department;
CREATE TABLE t_department(
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(20)
);
DROP TABLE IF EXISTS t_employee;
CREATE TABLE t_employee(
`id` INT PRIMARY KEY AUTO_INCREMENT,
`lastname` VARCHAR(20),
`email` VARCHAR(100),
`gender` INT(1),
`department` INT,
`birth` DATE,
FOREIGN KEY(department) REFERENCES t_department(`id`)
);
1.2 插入数据
INSERT INTO pm.t_department(`id`,`name`) VALUES (101, '部门1');
INSERT INTO pm.t_department(`id`,`name`) VALUES (102, '部门2');
INSERT INTO pm.t_department(`id`,`name`) VALUES (103, '部门3');
INSERT INTO pm.t_department(`id`,`name`) VALUES (104, '部门4');
INSERT INTO pm.t_department(`id`,`name`) VALUES (105, '部门5');
INSERT INTO pm.t_employee(id, lastname, email, gender, department, birth) VALUES (1001,'员工1','1001@qq.com',0,101,'1997-01-01');
INSERT INTO pm.t_employee(id, lastname, email, gender, department, birth) VALUES (1002,'员工2','1002@qq.com',1,102,'1997-01-02');
INSERT INTO pm.t_employee(id, lastname, email, gender, department, birth) VALUES (1003,'员工3','1003@qq.com',0,103,'1997-01-03');
INSERT INTO pm.t_employee(id, lastname, email, gender, department, birth) VALUES (1004,'员工4','1004@qq.com',1,104,'1997-01-04');
INSERT INTO pm.t_employee(id, lastname, email, gender, department, birth) VALUES (1005,'员工5','1005@qq.com',0,105,'1997-01-05');
三、实体类pojo
POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称。即有无参构造函数,每个字段都有getter和setter的java类。
分别创建部门类和员工类,属性与数据库中的属性一一对应。
3.1 Department
src/main/java/com/zqc/personmanagement/pojo/Department.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
// 部门表
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
private Integer id;
private String name;
}
3.2 Employee
src/main/java/com/zqc/personmanagement/pojo/Employee.java
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
// 员工表
@Data
@NoArgsConstructor
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender; // 0:women
private Department department;
private Date birth;
public Employee(Integer id, String lastName, String email, Integer gender, Department department) {
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.department = department;
this.birth = new Date();
}
}
四、Mapper
定义接口,对部门的数据进行增删改查。包括:
- 查找全部的部门或员工
- 根据id查找部门或员工
- 增加部门或员工
- 更新部门或员工
- 根据id删除部门或员工
4.1 DepartmentMapper接口
src/main/java/com/zqc/personmanagement/mapper/DepartmentMapper.java
import com.zqc.personmanagement.pojo.Department;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
// 注解表示了这是一个mybatis的mapper类
@Mapper
@Repository
public interface DepartmentMapper {
List<Department> queryDepartmentList();
Department queryDepartmentById(int id);
int addDepartment(Department department);
int updateDepartment(Department department);
int deleteDepartmentById(int id);
}
4.2 EmployeeMapper接口
src/main/java/com/zqc/personmanagement/mapper/EmployeeMapper.java
定义接口,对员工的数据进行增删改查。
import com.zqc.personmanagement.pojo.Employee;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface EmployeeMapper {
List<Employee> queryEmployeeList();
Employee queryEmployeeById(int id);
int addEmployee(Employee Employee);
int updateEmployee(Employee Employee);
int deleteEmployeeById(int id);
}
4.3 DepartmentMapper.xml
与DepartmentMapper
接口相对应,通过sql实现增删改查。
src/main/resources/mapper/DepartmentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zqc.personmanagement.mapper.DepartmentMapper">
<select id="queryDepartmentList" resultType="Department">
select * from t_department
</select>
<select id="queryDepartmentById" resultType="Department">
select * from t_department where id = #{id}
</select>
<insert id="addDepartment" parameterType="Department">
insert into t_department (id, name) values (#{id},#{name})
</insert>
<update id="updateDepartment" parameterType="Department">
update t_department set name=#{name} where id=#{id}
</update>
<delete id="deleteDepartmentById" parameterType="int">
delete from t_department where id=#{id}
</delete>
</mapper>
4.4 EmployeeMapper.xml
与EmployeeMapper
接口相对应,通过sql实现增删改查。
需要注意的是,在Employee的类中的Department也是一个类,但在数据库的t_employee表中department代表的是部门编号。所以在写sql语句时要传入department.id
。
与DepartmentMapper相比,EmployeeMapper更复杂,其中包含嵌套查询处理,在查询员工信息时,需要查询其部门信息。
src/main/resources/mapper/EmployeeMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zqc.personmanagement.mapper.EmployeeMapper">
<!-- 嵌套查询处理-->
<resultMap id="EmployeeDepartment" type="Employee">
<result property="id" column="id"/>
<result property="lastName" column="lastName"/>
<result property="email" column="email"/>
<result property="gender" column="gender"/>
<result property="birth" column="birth"/>
<association property="department" column="department" javaType="Department" select="getDepartment"/>
</resultMap>
<select id="getDepartment" resultType="Department">
select * from t_department where id=#{id}
</select>
<select id="queryEmployeeList" resultMap="EmployeeDepartment">
select * from t_employee
</select>
<select id="queryEmployeeById" resultMap="EmployeeDepartment">
select * from t_employee where id = #{id}
</select>
<insert id="addEmployee" parameterType="Employee">
insert into t_employee (lastname,email,gender,department,birth) values (#{lastName},#{email},#{gender},#{department.id},#{birth})
</insert>
<update id="updateEmployee" parameterType="Employee">
update t_employee set lastname=#{lastName},email=#{email},gender=#{gender},department=#{department.id},birth=#{birth} where id=#{id}
</update>
<delete id="deleteEmployeeById" parameterType="int">
delete from t_employee where id=#{id}
</delete>
</mapper>
在mapper中会遇到idea的警告信息,可参考改内容解决:
【Idea】关于数据库和mybatis的一些设置,及idea报错解决
4.5 测试DepartmentMapper
创建测试文件,分别对DepartmentMapper中的五种方法进行测试。可根据输出和数据库的变化判断是否测试成功,以此找到bug,对代码进行调试。
src/test/java/com/zqc/personmanagement/DepartmentMapperTest.java
import com.zqc.personmanagement.mapper.DepartmentMapper;
import com.zqc.personmanagement.pojo.Department;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class DepartmentMapperTest {
@Autowired
private DepartmentMapper departmentMapper;
/**
* department测试
*/
@Test
void testQueryDepartmentList() {
List<Department> departments = departmentMapper.queryDepartmentList();
for (Department department : departments) {
System.out.println(department);
}
}
@Test
void testQueryDepartmentById() {
Department department = departmentMapper.queryDepartmentById(101);
System.out.println(department);
}
@Test
void testAddDepartment() {
int x = departmentMapper.addDepartment(new Department(111, "部门x"));
System.out.println(x);
}
@Test
void testUpdateDepartment() {
int y = departmentMapper.updateDepartment(new Department(111, "部门y"));
System.out.println(y);
}
@Test
void testDeleteDepartmentById() {
int i = departmentMapper.deleteDepartmentById(111);
System.out.println(i);
}
}
4.5 测试EmployeetMapper
创建测试文件,分别对EmployeeMapper中的五种方法进行测试。可根据输出和数据库的变化判断是否测试成功,以此找到bug,对代码进行调试。
src/test/java/com/zqc/personmanagement/EmployeeMapperTest.java
import com.zqc.personmanagement.mapper.DepartmentMapper;
import com.zqc.personmanagement.mapper.EmployeeMapper;
import com.zqc.personmanagement.pojo.Employee;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class EmployeeMapperTest {
@Autowired
EmployeeMapper employeeMapper;
@Autowired
DepartmentMapper departmentMapper;
@Test
void testQueryEmployeeList() {
List<Employee> employeeList = employeeMapper.queryEmployeeList();
System.out.println(employeeList);
}
@Test
void testQueryEmployeeById() {
Employee employee = employeeMapper.queryEmployeeById(1001);
System.out.println(employee);
}
@Test
void testAddEmployee() {
int x = employeeMapper.addEmployee(new Employee(null, "员工x", "xxx@qq.com", 1, departmentMapper.queryDepartmentById(101)));
System.out.println(x);
}
@Test
void testUpdateEmployee() {
int y = employeeMapper.updateEmployee(new Employee(1007, "员工y", "yyy@qq.com", 1, departmentMapper.queryDepartmentById(101)));
System.out.println(y);
}
@Test
void testDeleteEmployeeById() {
int i = employeeMapper.deleteEmployeeById(1007);
System.out.println(i);
}
}
五、前端页面
静态资源和网页模板内容较多,内容均上传到Gitee,可在上面查看或下载。
静态文件
网页模板
网页使用了thymeleaf模板,并通过Controller进行控制(内容已经写好了),将在后面介绍。
主体内容介绍如下:
5.1 登录页面
localhost:8080进入登录页面,输入账号密码,并点击登录。
5.2 dashboard页面
登录后进来的页面,没啥实际用处。
5.3 list员工列表页面
可在该页面点击添加员工添加新的员工,也可以编辑删除已有员工。
5.4 add添加员工页面
按照要求输入所有信息后,可添加新的员工到数据库。
5.5 commom页面
红色框选部分均在common页面下进行设置。
六、配置Config
对首页、主页进行配置。
src/main/java/com/zqc/personmanagement/config/IndexViewConfig.java
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class IndexViewConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
registry.addViewController("/main.html").setViewName("dashboard");
}
}
如果找不到首页,检查target目录下是否生成index.html。若未生成该文件,可使用maven中的clean功能后重新启动。
七、Controller
通常情况下应采用三层架构:dao(mapper)、service、controller。
该项目省去了service,通过controller直接调用service。
7.1 LoginController
设置登录和和注销。
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpSession;
@Controller
public class LoginController {
@RequestMapping("/user/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password,
Model model, HttpSession session) {
//具体业务
if (!StringUtils.isEmpty(username) && "admin".equals(password)) {
session.setAttribute("loginUser", username);
return "redirect:/main.html";
} else {
model.addAttribute("msg", "用户名或密码错误!");
return "index";
}
}
@RequestMapping("/user/logout")
public String logout(HttpSession session) {
session.invalidate();
return "redirect:/index.html";
}
}
7.2 EmployeeController
获取浏览器的请求,并执行相对应的命令。
import com.zqc.personmanagement.mapper.DepartmentMapper;
import com.zqc.personmanagement.mapper.EmployeeMapper;
import com.zqc.personmanagement.pojo.Department;
import com.zqc.personmanagement.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.Collection;
@Controller
public class EmployeeController {
@Autowired
EmployeeMapper employeeMapper;
@Autowired
DepartmentMapper departmentMapper;
@RequestMapping("/emps")
public String List(Model model) {
Collection<Employee> employees = employeeMapper.queryEmployeeList();
model.addAttribute("emps", employees);
return "emp/list";
}
@GetMapping("/emp")
public String toAddpage(Model model) {
// 查出所有部门的信息
Collection<Department> departments = departmentMapper.queryDepartmentList();
model.addAttribute("departments", departments);
return "emp/add";
}
@PostMapping("/emp")
public String addEmp(Employee employee) {
// 添加操作
employeeMapper.addEmployee(employee); // 调用底层业务保存员工
return "redirect:/emps";
}
// 员工修改页面
@GetMapping("/emp/{id}")
public String toUpdateEmp(@PathVariable("id") Integer id, Model model) {
// 查出原来的数据
Employee employee = employeeMapper.queryEmployeeById(id);
model.addAttribute("emp", employee);
// 查出所有部门的信息
Collection<Department> departments = departmentMapper.queryDepartmentList();
model.addAttribute("departments", departments);
return "emp/update";
}
@PostMapping("/updateEmp")
public String updateEmp(Employee employee) {
employeeMapper.updateEmployee(employee);
return "redirect:/emps";
}
@GetMapping("/removeEmp/{id}")
public String removeEmp(@PathVariable("id") Integer id) {
employeeMapper.deleteEmployeeById(id);
return "redirect:/emps";
}
}
八、登录拦截
8.1 MVC配置
src/main/java/com/zqc/personmanagement/config/AdminWebConfig.java
import com.zqc.personmanagement.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 1 编写一个拦截器实现HandlerInterceptor接口
* 2 拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
* 3 指定拦截规则(如果拦截所有,静态资源也会被拦截)
*/
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") // 所有请求都被拦截
.excludePathPatterns("/", "/user/login","/css/**","/js/**","/img/**"); // 放行的请求
}
}
8.1 handler配置
preHandle
在目标方法执行之前完成操作。
src/main/java/com/zqc/personmanagement/interceptor/LoginInterceptor.java
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* 登录检查
* 1 配置好拦截器,要拦截哪些请求
* 2 把这些配置放在容器中
*/
public class LoginInterceptor implements HandlerInterceptor {
// 目标方法执行之前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 登录检查逻辑
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("loginUser");
System.out.println(loginUser);
if (loginUser != null) {
// 放行
return true;
}
// 拦截住,未登录,跳转到登录页
session.setAttribute("msg", "请先登录!");
request.getRequestDispatcher("/").forward(request, response);
return false;
}
}