SpringBoot(十)RestfulCRUD

我们先创建一个SpringBoot的 web 项目,并导入thymeleaflombok 坐标。

1、环境搭建

首先我们先将静态资源及页面拷入到项目中。

在这里插入图片描述
然后我们编写 pojo、dao类

这里我们没有连接数据库 ,Dao层的数据是使用Map临时伪造的;等我们后面讲到数据库集成的时候在回来修改此项目。

在这里插入图片描述
Department类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {

    private Integer id;
    private String departmentName;

}

Employee 类

@Data
@NoArgsConstructor
public class Employee {

    private Integer id;
    private String lastName;

    private String email;
    //1 male, 0 female
    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();// 默认创建日期
    }
}

DepartmentDao 类

@Repository
public class DepartmentDao {

    // 模拟数据库中的数据
    private static Map<Integer,Department> departments = null;

    static {
        departments = new HashMap<Integer, Department>();

        departments.put(101,new Department(101,"科研部"));
        departments.put(102,new Department(102,"市场部"));
        departments.put(103,new Department(103,"教育部"));
        departments.put(104,new Department(104,"运营部"));
        departments.put(105,new Department(105,"后勤部"));
    }

    // 获得所有部门信息
    public Collection<Department> getDepartments(){
        return departments.values();
    }

    //根据 id 获得部
    public Department getDepartmentById(Integer id){
        return  departments.get(id);
    }

}

EmployeeDao 类

@Repository
public class EmployeeDao {

    // 模拟数据库中的数据
    private static Map<Integer,Employee> employees = null;
    // 员工有所属的部门
    @Autowired
    private DepartmentDao departmentDao;

    static {
        employees = new HashMap<Integer, Employee>();

        employees.put(1001, new Employee(1001, "E-AA", "[email protected]", 1, new Department(101, "后勤部")));
        employees.put(1002, new Employee(1002, "E-BB", "[email protected]", 1, new Department(102, "市场部")));
        employees.put(1003, new Employee(1003, "E-CC", "[email protected]", 0, new Department(103, "教育部")));
        employees.put(1004, new Employee(1004, "E-DD", "[email protected]", 0, new Department(104, "运营部")));
        employees.put(1005, new Employee(1005, "E-EE", "[email protected]", 1, new Department(105, "科研部")));
    }

    // 主键自增
    private static Integer initId = 1006;
    // 添加一个员工
    public void save(Employee employee){
        if(employee.getId() == null){
            employee.setId(initId++);
        }

        employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
        employees.put(employee.getId(), employee);
    }
    // 查询全部员工
    public Collection<Employee> getAll(){
        return employees.values();
    }

    // 通过id 查询员工
    public Employee get(Integer id){
        return employees.get(id);
    }

    // 根据id 删除员工
    public void delete(Integer id){
        employees.remove(id);
    }
}

2、默认访问首页

因为 页面我们放在了 templates 文件夹中,需要用Controller 跳转访问;

这我们使用通用的配置类实现;

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");

    }
}

效果图:
在这里插入图片描述
这里我们只是看到了页面,并没有显示出样式;此时我们需要释放静态资源。

3、释放静态资源

  1. 我们需要先导入thymeleaf 坐标
  2. 在application.properties 文件中加入 关闭模板引擎缓存
    spring.thymeleaf.cache=false
  3. 在 html 页面需要引入头文件 xmlns:th="http://www.thymeleaf.org"
  4. 要在每一个 css、js、img等位置使用 thymeleaf 语法显示,如:
    <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">

在这里插入图片描述

4、国际化

详细步骤说明:https://blog.csdn.net/weixin_45606067/article/details/108098673

5、实现登录功能

1、在index.html 中更改请求,并增加每个输入框的 name 属性

在这里插入图片描述

2、编写Controller

@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) && "123456".equals(password)){
            session.setAttribute("loginUser",username);
            return "redirect:/main.html";
        }else{
            model.addAttribute("msg","用户或者密码错误");
            return "index";
        }

    }

}

注意:这里我们要在 MyMvcConfig 类中的 addViewControllers 方法中加入 main 的跳转。

3、运行测试发现,我们登录失败之后页面没有对应的信息提示,不够人性化;此时我们想在页面上回显我们放在session的错误信息。

这里我们查看 thymeleaf 官网文档说明:#19

在这里插入图片描述

  • #strings : utility methods for String objects:
/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Strings
* ======================================================================
*/
/*
* Null-safe toString()
*/
${#strings.toString(obj)} // also array*, list* and set*

/*
* Check whether a String is empty (or null). Performs a trim() operation before check
* Also works with arrays, lists or sets
*/
${#strings.isEmpty(name)}
${#strings.arrayIsEmpty(nameArr)}
${#strings.listIsEmpty(nameList)}
${#strings.setIsEmpty(nameSet)}

/*
* Perform an 'isEmpty()' check on a string and return it if false, defaulting to
* another specified string if true.
* Also works with arrays, lists or sets
*/
${#strings.defaultString(text,default)}
${#strings.arrayDefaultString(textArr,default)}
${#strings.listDefaultString(textList,default)}
${#strings.setDefaultString(textSet,default)}

/*
* Check whether a fragment is contained in a String
* Also works with arrays, lists or sets
*/
${#strings.contains(name,'ez')} // also array*, list* and set*
${#strings.containsIgnoreCase(name,'ez')} // also array*, list* and set*

/*
* Check whether a String starts or ends with a fragment
* Also works with arrays, lists or sets
*/
${#strings.startsWith(name,'Don')} // also array*, list* and set*
${#strings.endsWith(name,endingFragment)} // also array*, list* and set*

/*
* Substring-related operations
* Also works with arrays, lists or sets
*/
${#strings.indexOf(name,frag)} // also array*, list* and set*
${#strings.substring(name,3,5)} // also array*, list* and set*
${#strings.substringAfter(name,prefix)} // also array*, list* and set*
${#strings.substringBefore(name,suffix)} // also array*, list* and set*
${#strings.replace(name,'las','ler')} // also array*, list* and set*

/*
* Append and prepend
* Also works with arrays, lists or sets
*/
${#strings.prepend(str,prefix)} // also array*, list* and set*
${#strings.append(str,suffix)} // also array*, list* and set*

/*
* Change case
* Also works with arrays, lists or sets
*/
${#strings.toUpperCase(name)} // also array*, list* and set*
${#strings.toLowerCase(name)} // also array*, list* and set*

/*
* Split and join
*/
${#strings.arrayJoin(namesArray,',')}
${#strings.listJoin(namesList,',')}
${#strings.setJoin(namesSet,',')}
${#strings.arraySplit(namesStr,',')} // returns String[]
${#strings.listSplit(namesStr,',')} // returns List<String>
${#strings.setSplit(namesStr,',')} // returns Set<String>

/*
* Trim
* Also works with arrays, lists or sets
*/
${#strings.trim(str)} // also array*, list* and set*

/*
* Compute length
* Also works with arrays, lists or sets
*/
${#strings.length(str)} // also array*, list* and set*

/*
* Abbreviate text making it have a maximum size of n. If text is bigger, it
* will be clipped and finished in "..."
* Also works with arrays, lists or sets
*/
${#strings.abbreviate(str,10)} // also array*, list* and set*

/*
* Convert the first character to upper-case (and vice-versa)
*/
${#strings.capitalize(str)} // also array*, list* and set*
${#strings.unCapitalize(str)} // also array*, list* and set*

/*
* Convert the first character of every word to upper-case
*/
${#strings.capitalizeWords(str)} // also array*, list* and set*
${#strings.capitalizeWords(str,delimiters)} // also array*, list* and set*

/*
* Escape the string
*/
${#strings.escapeXml(str)} // also array*, list* and set*
${#strings.escapeJava(str)} // also array*, list* and set*
${#strings.escapeJavaScript(str)} // also array*, list* and set*
${#strings.escapeJavaScript(str)} // also array*, list* and set*
${#strings.unescapeJava(str)} // also array*, list* and set*
${#strings.unescapeJavaScript(str)} // also array*, list* and set*

/*
* Null-safe comparison and concatenation
*/
${#strings.equals(first, second)}
${#strings.equalsIgnoreCase(first, second)}
${#strings.concat(values...)}
${#strings.concatReplaceNulls(nullValue, values...)}

/*
* Random
*/
${#strings.randomAlphanumeric(count)}

通过查看文档第15行在登录页面的适合位置填上以下代码:

<!--如果msg 值为空,则不显示消息-->
<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>

测试我们重新启动服务再次运行就 OK 了!

6、登录拦截器

我们现在不登录,直接访问http://localhost:8080/main.html地址也可以进入,这样是不够安全的。

这里我们编写登录拦截器类。

public class LoginHandlerInterceptor implements HandlerInterceptor {

    // 登拦截器
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 登录成功得到用户session
        Object loginUser = request.getSession().getAttribute("loginUser");
        if (loginUser ==null){
            request.setAttribute("msg","没有权限,请先登录!");
            request.getRequestDispatcher("/index.html").forward(request,response);
            return false;
        }else{
            return true;
        }
    }
}    

并且我们要将自定义的拦截器注入到 Ioc容器中。

// 注入登录拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new LoginHandlerInterceptor())
            .addPathPatterns("/**").
            excludePathPatterns("/index.html","/","/user/login","/css/**","/js/**","/img/**");
}

注意:除了登录请求、登录成功后、静态资源等,其他的请求都需要被拦截。如果不过滤资源的话,会造成死循环。

此时我们测试即可。

7、展示员工列表

1、RestfulCRUD

CRUD满足Rest风格

URI: /资源名称/资源标识 HTTP请求方式区分对资源CRUD操作

普通CRUD(uri来区分操作) RestfulCRUD
查询 getEmp emp—GET
添加 addEmp?xxx emp—POST
修改 updateEmp?id=xxx&xxx=xx emp/{id}—PUT
删除 deleteEmp?id=1 emp/{id}—DELETE

实验的请求架构

实验功能 请求URI 请求方式
查询所有员工 emps GET
查询某个员工(来到修改页面) emp/1 GET
来到添加页面 emp GET
添加员工 emp POST
来到修改页面(查出员工进行信息回显) emp/1 GET
修改员工 emp PUT
删除员工 emp/1 DELETE

2、抽取公共页面

我们把顶部导航栏、侧边栏每个页面都有的、可以复用的,抽取一个公共的供其他页面使用的。

  1. 需要在抽取代码的位置加上 th:fragment="topbar"

在这里插入图片描述
2. 在需要的页面引入抽取公共的资源即可<div th:replace="~{commons/commons::topbar}"></div>

在这里插入图片描述
这里我们可以用th:replace 或者th:insert都可以实现代码的复用。

3、导航栏高亮

这里我们点击侧边栏的某个功能时,它既会跳转又会高亮我们所点击的。
在这里插入图片描述
如果要传递参数,可以直接使用 ()传参,接收判断即可。

在这里插入图片描述
这里我们使用 thymeleaf 提供的三元运算符。

在这里插入图片描述

4、实现页面展示信息

<table class="table table-striped table-sm">
<thead>
	<tr>
		<th>id</th>
		<th>lastName</th>
		<th>email</th>
		<th>gender</th>
		<th>department</th>
		<th>birth</th>
		<th>操作</th>
	</tr>
</thead>
<tbody>
	<tr th:each="emp:${emps}">
		<td th:text="${emp.getId()}"></td>
		<td th:text="${emp.getLastName()}"></td>
		<td>[[${emp.getEmail()}]]</td>
		<td th:text="${emp.getGender()==0?'':''}"></td>
		<td th:text="${emp.department.getDepartmentName()}"></td>
		<td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td>
		<td>
			<button class="btn btn-sm btn-primary">编辑</button>
			<button class="btn btn-sm btn-danger">删除</button>
		</td>
	</tr>
</tbody>
</table>

效果图:
在这里插入图片描述

8、增加员工信息

1、准备添加页面

首先我们要在展示页list.html 中增加 添加员工按钮

<h2>
	<a class="btn btn-sm btn-success" th:href="@{/emp}">添加员工</a>
</h2>

跳转后的添加页面。

<form th:action="@{/emp}" method="post">
	<div class="form-group">
		<label>LastName</label>
		<input type="text" class="form-control" name="lastName" placeholder="StarSea99">
	</div>
	<div class="form-group">
		<label>Email</label>
		<input type="email" class="form-control" name="email" placeholder="[email protected]">
	</div>
	<div class="form-group">
		<label>Gender</label><br/>
		<div class="form-check form-check-inline">
			<input class="form-check-label" type="radio" name="gender" value="1">
			<label class="form-check-label"></label>
		</div>
		<div class="form-check form-check-inline">
			<input class="form-check-label" type="radio" name="gender" value="0">
			<label class="form-check-label"></label>
		</div>
	</div>
	<div class="form-group">
		<label>Department</label>
		<!--我们在controller中接收的是一个Employee,所以我们需要提交的是其中的一个属性 -->
		<select class="form-control" name="department.id">
			<option th:each="dept:${departments}" th:text="${dept.getDepartmentName()}"
					th:value="${dept.getId()}"></option>
		</select>
	</div>
	<div class="form-group">
		<label>Birth</label>
		<input type="text" name="birth" class="form-control" placeholder="1990-01-01">
	</div>
	<button type="submit" class="btn btn-primary">添加</button>
</form>

2、实现页面跳转及部门信息的回显

@GetMapping("/emp")
public String toAddEmp(Model model){
    // 查出所有部门信息
    Collection<Department> departments = departmentDao.getDepartments();
    model.addAttribute("departments",departments);
    return "emp/add";
}

3、实现Controller类

@PostMapping("/emp")
public String addEmp(Model model,Employee employee){
    // 添加员工
    employeeDao.save(employee);
    return "redirect:/employee/list";
}

4、启动服务测试即可

9、修改员工信息

1、准备修改页面

首先我们要在展示页list.html 中增加 编辑按钮

<td>
	<a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.getId()}">编辑</a>
</td>

跳转后的修改页面。

<form th:action="@{/updateEmp}" method="post">
	<input type="hidden" name="id" th:value="${emp.getId()}">
	<div class="form-group">
		<label>LastName</label>
		<input type="text" class="form-control" th:value="${emp.getLastName()}" name="lastName" placeholder="StarSea99">
	</div>
	<div class="form-group">
		<label>Email</label>
		<input type="email" class="form-control" th:value="${emp.getEmail()}" name="email" placeholder="[email protected]">
	</div>
	<div class="form-group">
		<label>Gender</label><br/>
		<div class="form-check form-check-inline">
			<input class="form-check-label" th:checked="${emp.getGender()==1}" type="radio" name="gender" value="1">
			<label class="form-check-label"></label>
		</div>
		<div class="form-check form-check-inline">
			<input class="form-check-label" th:checked="${emp.getGender()==0}" type="radio" name="gender" value="0">
			<label class="form-check-label"></label>
		</div>
	</div>
	<div class="form-group">
		<label>Department</label>
		<!--我们在controller中接收的是一个Employee,所以我们需要提交的是其中的一个属性 -->
		<select class="form-control" name="department.id">
			<option th:each="dept:${departments}" th:selected="${dept.getId()==emp.department.getId()}" th:text="${dept.getDepartmentName()}"
					th:value="${dept.getId()}"></option>
		</select>
	</div>
	<div class="form-group">
		<label>Birth</label>
		<input type="text" name="birth" th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm')}" class="form-control" placeholder="1990-01-01">
	</div>
	<button type="submit" class="btn btn-primary">更新</button>
</form>

2、跳转更新页面,并回显页面信息

// 去员工的修改页面
@GetMapping("/emp/{id}")
public String toUpdate(@PathVariable("id") Integer id ,Model model){
    // 查询原来数据
    Employee employee = employeeDao.get(id);
    model.addAttribute("emp",employee);
    // 查出所有部门信息
    Collection<Department> departments = departmentDao.getDepartments();
    model.addAttribute("departments",departments);
    return "emp/update";
}

3、编写Controller 类

//更新员工
@PostMapping("/updateEmp")
public String updateEmp(Employee employee){
    employeeDao.save(employee);
    return "redirect:/employee/list";
}

4、启动服务测试即可

10、删除员工以404处理

删除功能

1、首先我们要在展示页list.html 中增加 删除按钮

<td>
	<a class="btn btn-sm btn-danger" th:href="@{/delemp/}+${emp.getId()}">删除</a>
</td>

2、编写Controller类

// 删除员工
@GetMapping("/delemp/{id}")
public String deleteEmp(@PathVariable("id") Integer id){
    employeeDao.delete(id);
    return "redirect:/employee/list";
}

3、启动服务测试即可

404处理

我们只需要在templates 下创建error文件夹,并将页面放到里面。

注意:命名为404.html,SpringBoot会自动识别,不需要我们有多余的配置即可。

11、退出登录

1、修改退出按钮的请求地址

<li class="nav-item text-nowrap">
   <a class="nav-link" th:href="@{/user/logout}">退出</a>
</li>

2、编写Controller 类

// 退出功能
@RequestMapping("/user/logout")
 public String logout(HttpSession session){
     session.invalidate();
     return "redirect:/index.html";
 }

3、启动服务测试即可

以上就是 RestfulCRUD 的全部功能!!!


如果有收获!!! 希望老铁们来个三连,点赞、收藏、转发。
创作不易,别忘点个赞,可以让更多的人看到这篇文章,顺便鼓励我写出更好的博客

猜你喜欢

转载自blog.csdn.net/weixin_45606067/article/details/108097279