目录
一、项目大体框架
- 后端:Spring Boot
- 前端:HTML
- 数据库:Mysql
二、创建项目
项目采用jdk8编译,并用maven对项目进行管理
Spring Boot版本我这里选择2.7.6,编译环境是jdk17的小伙伴可以选择3.0的,勾选Spring Web和Thymelafe依赖
这里简单介绍一下Thymelafe:
Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下三个极吸引人的特点:
- Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 Thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
- Thymeleaf 开箱即用的特性。它提供标准和 Spring 标准两种方言,可以直接套用模板实现 JSTL、 OGNL表达式效果,避免每天套模板、改 Jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。
- Thymeleaf 提供 Spring 标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
更多关于Thymelafe的语法大家都可以去看这篇文章
查看创建完的项目结构图
三、编写代码
- 首先创建Entity层用来放置相关的实体类
- 部门信息实体类,并创建相对应的构造方法和get、set方法
public class Department {
private Integer id;
private String departmentName;
Department(){
}
public Department(Integer id, String departmentName) {
this.id = id;
this.departmentName = departmentName;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDepartmentName() {
return departmentName;
}
public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}
}
- 员工信息实体类,并创建相对应的构造方法和get、set方法
PS:我们可以使用Generate快速的创建构造方法以及get和set方法
public class employee {
private Integer id;
private String lastName;
private String email;
private Integer gender;
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();
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
}
创建完的项目结构图如下所示
- 创建两个实体类的数据持久层也就是Dao层
@Repository
public class departmentDao {
//创建一个HashMap对象departments,并通过泛型来限制key,value数据类型
private static final Map<Integer, Department> departments = new HashMap<>();
//设置数据
static {
//value的值是创建的一个Department对象
departments.put(101, new Department(101, "D-AA"));
departments.put(102, new Department(102, "D-BB"));
departments.put(103, new Department(103, "D-CC"));
departments.put(104, new Department(104, "D-DD"));
departments.put(105, new Department(105, "D-EE"));
}
//定义一个泛型方法返回department对象中的所有值
public Collection<Department> getDepartments() {
return departments.values();
}
//通过id获取对应的部门ID和部门名称
public Department getDepartment(Integer id) {
return departments.get(id);
}
}
@Repository
public class employeeDao {
private static final Map<Integer, employee> employees = new HashMap<>();
private final departmentDao departmentDao;
//以构造器的方式自动引入departDao
@Autowired
public employeeDao(departmentDao departmentDao) {
this.departmentDao = departmentDao;
}
//设置数据
static {
employees.put(1001, new employee(1001, "E-AA", "aa@163.com", 1, new Department(101, "D-AA")));
employees.put(1002, new employee(1002, "E-BB", "bb@163.com", 1, new Department(102, "D-BB")));
employees.put(1003, new employee(1003, "E-CC", "cc@163.com", 0, new Department(103, "D-CC")));
employees.put(1004, new employee(1004, "E-DD", "dd@163.com", 0, new Department(104, "D-DD")));
employees.put(1005, new employee(1005, "E-EE", "ee@163.com", 1, new Department(105, "D-EE")));
}
//通过获取employees集合中所有的值来获取所有员工信息
public Collection<employee> getAll() {
return employees.values();
}
//把传入参数作为key值,通过get方法来获取对应的value也就是获取指定的员工信息
public employee get(Integer id) {
return employees.get(id);
}
//把传入参数作为key值,调用remove方法来删除key对应的value(员工信息)
public void delete(Integer id) {
employees.remove(id);
}
private static Integer initId = 1006;
//通过传入一个employee类型的参数来保存员工信息
public void save(employee employee) {
//如果在传入employee参数时,没有设置key值,那么就自动设置key的值,key的值从集合中最后一个key的值开始算起
if (employee.getId() == null) {
//每调用一次,对key的值进行自增
employee.setId(initId++);
}
//通过该方法传入的employee对象 我们可以调用他自身的getDepartment方法来获取来获取Department对象,然后通过调用实体类Department中的getId方法得到部门id
//然后把得到的部门id传入departmentDao类中的getDepartment方法里,就可以把传入的id作为key来获取对应的value也就是部门信息
//最后通过employee的setDepartment方法来设置对应的部门信息
employee.setDepartment(departmentDao.getDepartment(employee.getDepartment().getId()));
//通过key,value保存信息
employees.put(employee.getId(), employee);
}
}
PS:
@Repository用在持久层的接口上,这个注解是将接口的一个实现类交给Spring管理。
该注解的作用不只是将类识别为Bean,同时它还能将所标注的类中抛出的数据访问异常封装为 Spring 的数据访问异常类型。
创建完的项目结构图如下所示
- 创建Controller层来实现业务逻辑的控制
@Controller
public class employeeController {
private final employeeDao employeeDao;
private final departmentDao departmentDao;
@Autowired
public employeeController(employeeDao employeeDao,departmentDao departmentDao){
this.employeeDao = employeeDao;
this.departmentDao = departmentDao;
}
//查询所有信息并返回结果
@GetMapping("/emps")
public String list(Model model) {
//通过getAll方法把获取到的所有值(员工信息),赋值给employee集合
Collection<employee> employees = employeeDao.getAll();
//model中的addAttribute方法是以K,V的形式从后端往前端传数据(可以传对象、List等)前端可以通过el表达式${}获取到值
model.addAttribute("emps", employees);
// 返回指定页面(因为thymeleaf的模板默认路径是resources的templates,所以我们的访问路径从templates下开始写)
return "emp/list";
}
//删除员工信息的实现是通过获取员工id,并把员工id参数传入employeeDao的delete方法
@GetMapping("/delete/{id}")
//@PathVariable的作用是获取请求路径中指定参数的值
public String deleteEmploy(@PathVariable("id") Integer id) {
employeeDao.delete(id);
//redirect的作用是重定向,当删除完员工的信息后再重新访问一次员工信息页面
return "redirect:/emps";
}
//员工添加
@GetMapping("/emp")
public String toAddPage(Model model) {
Collection<Department> departments = departmentDao.getDepartments();
//把所有的部门信息返回给前端以供对部门信息的修改和员工添加信息时部门的选择
model.addAttribute("depts", departments);
return "emp/add";
}
//员工编辑
@GetMapping("/emp/{id}")
public String editPage(@PathVariable("id") Integer id, Model model) {
//employee对象的所有值都是通过employDao中的get方法获取到,而get方法传入的值来自于前端发起请求中url的参数
employee employee = employeeDao.get(id);
model.addAttribute("emp", employee);
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("depts", departments);
return "emp/add";
}
//保存添加的员工
@PostMapping("/emp")
//把前端表单传回来的值,以对应的方式赋值给employee的属性
public String addEmp(employee employee) {
//通过save方法把传入回来的employee对象进行保存
employeeDao.save(employee);
return "redirect:/emps";
}
}
PS:
@Controller 是标记在Controller 类(包和类名之间)上面的。用于指示Spring类的实例是一个控制器。Controller接口的实现类只能处理一个单一请求动作,而@Controller注解的控制器可以支持同时处理多个请求动作,更加灵活。
@GetMapping注解用于处理HTTP GET请求,并将请求映射到具体的处理方法中
@PostMapping注解用于处理HTTP Post请求,并将请求映射到具体的处理方法中
- 创建员工信息详情和员工编辑/添加的前端页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>员工信息页</title>
</head>
<body>
<h2><a th:href="@{/emp}">员工添加</a></h2>
<div>
<table>
<!-- 设置表头 -->
<thead>
<tr>
<th th:width="137">ID</th>
<th th:width="137">姓名</th>
<th th:width="137">邮箱</th>
<th th:width="137">性别</th>
<th th:width="137">部门</th>
<th th:width="137">出生日期</th>
<th th:width="137">操作</th>
</tr>
</thead>
<!-- 下面警告的原因是IDEA除了对语法检验之外,还会对表达式进行检验。而IDEA检测到html是不支持El表达式的,
所以才会有“Cannot resolve”的提示 -->
<!-- 虽然html不可以使用此表达式,但我们的项目中集成了thymeleaf,所以运行项目虽然有警告但也是可以读取到值的-->
<!-- 我们可以直接让IDEA忽略这个警告也可以使用如下语句来消除警告,缺点是需要一个一个写,顾不可采-->
<!-- /*@thymesVar id="emps" type=""*/ -->
<tr th:each="emp:${emps}" th:align="center">
<!-- th:each作用是遍历 此处我们对后端传来的emps这个key进行遍历得到其中的值,并把得到的值赋值给emp -->
<td th:text="${emp.id}"></td>
<td th:text="${emp.lastName}"></td>
<td th:text="${emp.email}"></td>
<td th:text="${emp.gender}==0?'女':'男'"></td>
<td th:text="${emp.department.departmentName}"></td>
<!-- 使用thymeleaf内置工具对象dates的format方法对日期进行格式化输出 -->
<td th:text="${#dates.format(emp.birth,'yyyy-MM-dd HH:mm')}"></td>
<td>
<!-- 把获取到的id拼接成为访问的url的一部分 -->
<a th:href="@{/emp/} + ${emp.id}">编辑</a>
<a th:href="@{/delemp/} + ${emp.id}">删除</a>
</td>
</tr>
</table>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>员工添加页面</title>
</head>
<body>
<!--该页面实现将员工的信息以表单的方式通过post请求传入后端-->
<form th:action="@{/emp}" method="post">
<!-- 如果当前的emp不为空我们进行对当前信息的更新操作,如果没有我们执行添加 -->
<input type="hidden" name="id" th:if="${emp!=null}" th:value="${emp.id}">
<div>
<label>LastName</label>
<input name="lastName" type="text" placeholder="张三" th:value="${emp!=null}?${emp.lastName}">
</div>
<div>
<label>email</label>
<input name="email" type="email" placeholder="张三@qq.com" th:value="${emp!=null}?${emp.email}">
</div>
<div>
<label>gender</label><br>
<div>
<!-- 根据单选钮的选中情况来判断性别是男还是女 -->
<label>男</label>
<input name="gender" type="radio" value="1" th:checked="${emp!=null}?${emp.gender==1}">
</div>
<div>
<label>女</label>
<input name="gender" type="radio" value="0" th:checked="${emp!=null}?${emp.gender==0}">
</div>
</div>
<div>
<label>department</label>
<select name="department.id">
<option th:each="dept:${depts}" th:selected="${emp!=null}?${dept.id == emp.department.id}"
th:value="${dept.id}" th:text="${dept.departmentName}">部门
</option>
</select>
</div>
<div>
<label>Birth</label>
<input name="birth" type="text" placeholder="2022/07/15"
th:value="${emp!=null}?${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}">
</div>
<!-- 判断emp是否为空的情况来决定按钮的值是添加还是修改-->
<button type="submit" th:text="${emp!=null}?'修改':'添加'">添加</button>
</form>
</body>
</html>
创建完的项目结构图如下所示
- 查看项目运行效果
在此基础上我们可以创建一个登录页面,实现登录和注册的功能,而登录成功才可对员工信息进行增删改查
- 先创建一个实体类用来存放账号和密码
public class user {
String user;
String passwd;
public user() {
}
public user(String user, String passwd) {
this.user = user;
this.passwd = passwd;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
}
- 再创建一个Map层,用来存放一些和Mysql数据库的交互操作,在此之前我们需要在pom文件中引入jdbc数据库驱动和Mybatis
- 配置数据库驱动(xxxx.properties文件)
创建完的项目结构图如下所示
- 简单创建一个数据库表用来存放账号和密码信息
- 在Map层中定义一个接口,接口中的方法主要用来实现一些sql语句
@Mapper
public interface loginMap {
@Select("select * from 表名 where user=#{user} and passwd=#{passwd}")
user login(String user, String passwd);
@Insert("insert into 表名 values(#{user},#{passwd})")
void insertUser(user user);
}
PS:使用@Mapper的作用,以及@Select和@Insert的作用
- 使用@Mapper将loginMap接口交给Spring进行管理
- 不用写Mapper映射文件(XML)
- 为这个loginMap接口生成一个实现类,让别的类进行引用
- @Select注解的目的是为了取代xml中的select标签,只作用于方法上面。@Insert同理
- 创建一个登录和注册界面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<form th:action="@{/login}" method="post">
用户名:<label>
<input name="username" type="text">
</label>
密码:<label>
<input name="passwd" type="password">
</label>
<input type="submit" value="登录">
</form>
<!-- p标签中的值用来反馈登录结果 -->
<p th:text="${msg}"></p>
<a th:href="@{/register}">注册</a>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<form th:action="@{/register}" method="post">
用户名:<label>
<input name="username" type="text">
</label>
密码:<label>
<input name="passwd" type="password">
</label>
<input type="submit" value="注册">
</form>
</body>
</html>
在Controller层中创建一个loginController用来处理登录相关业务
@Controller
public class loginController {
private final LoginMapper loginMapper;
@Autowired
public loginController(LoginMapper loginMapper){
this.loginMapper = loginMapper;
}
//访问登录页面
@GetMapping({"/login", "/"})
public String login() {
return "login";
}
@PostMapping("/login")
//@RequestParam:将请求参数绑定到你控制器的方法参数上(把登录页面输入的用户名和密码的值取出来,并赋值给方法参数)
public String login1(@RequestParam("username") String user, @RequestParam("passwd") String passwd, Model model) {
//loginMapper中的login方法通过传入的用户名和密码去数据库中进行比对,并把结果赋值给user对象
user u = loginMapper.login(user,passwd);
//如果user为空则表示数据库中没有该账号,并将结果反馈到前端页面
if (u == null) {
model.addAttribute("msg", "信息有误");
return "login";
//如果数据校对成功则重定向到员工信息页面
} else {
return "redirect:/emps";
}
}
}
- 在Controller层中创建一个registerController用来处理登录相关业务
@Controller
public class registerController {
private final LoginMapper loginMapper;
@Autowired
public registerController(LoginMapper loginMapper) {
this.loginMapper = loginMapper;
}
@GetMapping("/register")
public String register() {
return "register";
}
@PostMapping("/register")
public String register1(@RequestParam("username") String username, @RequestParam("passwd") String passwd) {
//因为insertUser方法接收的参数是user类型,所有我们需要new一个user对象,并把从前端获取到的账号和密码转入到user的构造方法中
loginMapper.insertUser(new user(username, passwd));
return "redirect:/login";
}
}
创建完的项目结构图如下所示
查看项目运行效果,并测试注册功能和登录功能
我们可以看到注册的账号已同步写入到数据库中
登录成功~