一、前言
关于这个CRUD的小案例的编写因为忙于应对学校课程与考试拖了一段时间,现在总结复习一下全部过程,加深印象。
二、关于CRUD小案例的环境搭建
1、首先创建好一个web项目。在WebContent/WEB-INF/lib下导入相应jar包,因为我是Spring和SpringMVC综合使用的,所以先导入了
commons-logging-1.2.jar、spring-aop-4.2.0.RELEASE.jar、spring-beans-4.2.0.RELEASE.jar、spring-context-4.2.0.RELEASE.jar、spring-core-4.2.0.RELEASE.jar、spring-expression-4.2.0.RELEASE.jar、spring-jdbc-4.2.0.RELEASE.jar、spring-orm-4.2.0.RELEASE.jar、spring-tx-4.2.0.RELEASE.jar、spring-web-4.2.0.RELEASE.jar、spring-webmvc-4.2.0.RELEASE.jar,这些框架包。
2、写配置。直接在项目下创建一个源目录conf,然后创建一个springmvc-servlet.xml的配置文件,一个applicationContext.xml的配置文件,把springmvc和spring的配置分开写。我的项目结构如下。
2.1 下面准备写配置文件。
首先在web项目的web.xml中添加前端控制器配置,web.xml文件一般在WEB-INF下,没有就直接找到Server项目下的web.xml拷过来,留下头部,多余的可以全删掉,其实在web项目创建时是可以勾选自动创建的。还是写配置吧。
在web.xml中添加前端控制器DispatcherServlet
springDispatcherServlet
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:springmvc-servlet.xml
1
springDispatcherServlet
/
写完这个前端控制器之后莫急,目前还只是把SpringMVC的配置文件加载了还有Spring的配置文件没有加载。这个时候需要用到
contextConfigLocation来加载spring的配置文件,不然最后运行时会找不到资源,在使用ClassPathXmlApplicationContext("applicationContext.xml")获取ioc容器时报出Exception in thread "main" java.lang.NoClassDefFoundError: org.springframework.beans.FatalBeanException异常。
org.springframework.web.context.ContextLoaderListener
contextConfigLocation
classpath:applicationContext.xml
2.2 一次性解决中文乱码问题,在web.xml中添加SpringMVC提供的Filter---->CharacterEncodingFilter
CharacterEncodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
CharacterEncodingFilter
/*
2.3 web.xml暂时配置这么多了,接下来是springmvc-servlet.xml的配置,在SpringMVC的配置文件中,按照当前的需求只需要开启包扫描和配置一个视图解析器用于响应地址的拼接就行了
2.4 由于是连接的SqlServer数据库所以我用到了外部配置文件
首先还是导入支持数据库连接的包:c3p0-0.9.5.2.jar,mchange-commons-java-0.2.12.jar,mssql-jdbc-6.4.0.jre8.jar,在c3p0的新版本中这两个都必须要,导完包后就是新建一个外部文件dbconfig.properties,具体内容为
就是配置数据库连接的一些基本信息。然后在spring的配置文件applicationContext.xml中配置数据源,同时打开包扫描,将数据连接源给spring提供的JdbcTemplate管理,这个是用来操作数据库的。
2.5 因为这个CRUD小案例使用的是Rect风格,所以还需要再web.xml中添加一个配置
HiddenHttpMethodFilter
org.springframework.web.filter.HiddenHttpMethodFilter
HiddenHttpMethodFilter
/*
2.6 数据库sql文件
--使用的数据库表创建语句
use testEmployee
create table employee(
employeid int primary key identity(1,1),
id int unique,
lastName varchar(40),
email varchar(50),
genden int
)
alter table employee add departmentid int
alter table employee
add constraint FK_Departmentid FOREIGN KEY(departmentid) REFERENCES Department(id)
create table Department(
departmentid int identity(1,1),
id int primary key,
departmentName varchar(40)
)
2.7 基础环境搭建基本结束了
三、建立包结构
3.1如图
3.2 EmployeeController.java文件是用来处理请求控制的,EmployeeDao.java是员工类的CRUD操作类,同理DepartmentDao.java是部门类的CRUD操作类,MyJdbcTemplate.java这个类很关键,是获取ioc容器中配置的数据库连接管理对象的。entity实体包中的分别是员工、部门bean。
四、具体步骤
4.1 做一个简略的index.jsp,作为整个web项目的首页,在浏览器请求index.jsp的时候显示全部员工信息。在这个地方直接使得index.jsp页面加载就转发请求/emps给后台。在index.jsp中只使用jstl标签的
4.2 先把EmployeeController.java类添加@Controller注解使得它能够被ioc扫描到。然后添加能够处理“/emps”请求的方法
@Autowired
EmployeeDao employeeDao;
@Autowired
DepartmentDao departmentDao;
/**
*
* Title: allEmployee
* Description: 处理初始页面的请求,进入员工列表页面
* @return 返回员工列表页面的地址
*/
@RequestMapping("/emps")
public String allEmployee(Model model) {
System.out.println("初始化完成,开始查询所有员工数据····");
/**查询到所有用户信息,添加到请求域中*/
List searchAllEmployee = employeeDao.searchAllEmployee();
model.addAttribute("emps", searchAllEmployee);
System.out.println("跳转到员工页面···");
return "listEmployee";
}
代码中的@RequestMapping("/emps")是标识此方法能够处理“/emps”请求。参数Model model能够将元素添加到请求域中,在jsp页面可以获取到。我显示利用employeeDao查询了所有的员工数据封装成了集合返回然后加入到请求域中,最后返回响应页面的页面名,在视图解析器处拼接好完整的地址为/WEB-INF/pages/listEployee.jsp。注意EmployeeDao.java是要添加@Repository注解保证其能被ioc容器扫描到,DepartmentDao.java也是一样。
查询所有员工的方法:
/**
*
* Title: searchAllEmployee
* Description: 查询员工所有数据,并封装成一个list集合返回
* @return 返回员工集合
*/
public List searchAllEmployee(){
String sql = "select * from employee";
//查询到的数据封装成一个list集合返回
List employeeList = MyJdbcTemplate.getInstance().query(sql,new BeanPropertyRowMapper<>(Employee.class));
System.out.println("成功返回查询的结果····");
for(Employee s:employeeList) {
/**获取部门所有数据*/
List searchAllDepartment = departmentDao.searchAllDepartment();
for(int a=0;a
因为是直接从员工表上查询的数据通过JdbcTemplate自动封装成employee对象的,所以员工对象的部门id只是id,部门属性为null,所以又查询了部门信息用作对比,把员工对象的部门赋值。后面的
Employee employee = employeeList.get(employeeList.size() - 1);
/**设置当前id为已有id,然后再增加员工时id加一*/
Employee.autoId = employee.getId();
这两句是为了实现员工添加时id的自增,检查出数据库中的员工id最后一个是多少然后加1给作为新增员工的id
好像忘了把employee类的代码贴出来了
package com.feilonkji.entity;
/**
*
* @ClassName: Employee
* @Description: TODO 员工实体类
* @author zr
* @date 2020年4月14日
* @version V1.8
*/
public class Employee {
public static int autoId;
//员工id
private Integer id;
//员工姓名
private String lastName;
//员工邮箱
private String email;
//员工性别,1表示male,0表示female
private Integer gender;
//员工所在部门id
private Integer departmentId;
//部门对象
private Department department;
public Employee() {
}
public Employee(Integer id, String lastName, String email, Integer gender, Department department) {
super();
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.department = department;
}
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 Integer getDepartmentId() {
return departmentId;
}
public void setDepartmentId(Integer departmentId) {
this.departmentId = departmentId;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
@Override
public String toString() {
return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", gender=" + gender
+ ", departmentId=" + departmentId + ", department=" + department + "]";
}
}
4.3 当员工集合放到请求域中的时候,就可以在员工展示页面获取显示了。新建listEployee.jsp
在其中利用el表达式和jstl标签遍历员工集合,当然用这个必须要导入jar包的:jstl.jar、standard.jar,这两个包需要在创建index.jsp时就要导入。
<%pageContext.setAttribute("ctp", request.getContextPath()); %>
员工列表
ID
lastName
email
gender
departmentName
EDIT
DELETE
${emp.id}
${emp.lastName}
${emp.email}
${emp.gender == 1?"男":"女"}
${emp.department.departmentName}
update
delete
添加员工
使用了
员工展示列表就完成了。
4.4 接下来添加员工,确定一下添加员工的页面,新建addEmployee.jsp页面用来作为员工信息添加页面。顺便提一句我的页面是存放在WebContent/WEB-INF/pages/下。
添加员工页面:。哈哈超级简单的页面,就一个form表单,但是也满足需求了。
因为在下拉菜单中显示部门信息,所以我做了一个过度处理。有处理方法把部门信息存放到请求域中后在转发到员工添加页面。
首先是添加员工使用了a标签发送toaddpage请求。然后在EmployeeController.java类中添加请求处理的相应方法。
/**
*
* Title: toAddPage
* Description: 处理请求,去员工添加页面
* @return
*/
@RequestMapping("/toaddpage")
public String toAddPage(Model model) {
System.out.println("开始进入员工添加页面····");
/**查询到所有的部门信息添加到请求域中*/
List searchAllDepartment = departmentDao.searchAllDepartment();
model.addAttribute("departments",searchAllDepartment);
model.addAttribute("employee",new Employee(12,"李四","[email protected]",0,new Department()));
System.out.println("进入员工添加页面····");
return "addEmployee";
}
这个toAdPage(Model model)方法就是做过度处理。先将所有部门信息添加到请求域中保证jsp页面能够获取信息,然后再响应跳转到真正的员工添加页面addEmployee.jsp。方法中的model.addAttribute("employee",new Employee(12,"李四","[email protected]",new Department())),先不用管,这个是后面使用springmvc提供的标签库配合回显数据用的测试。
之后便是到了addEmployee.jsp页面了,图示效果上面已经发过了,来看看代码。
<%--设置项目的绝对路径 --%>
<%pageContext.setAttribute("ctp",request.getContextPath());%>
员工添加
总的来讲就是一个form表单,然后就是下拉选项框了,
这些做好之后同样的在EmployeeController.java中添加对应处理方法就好了。
/**
*
* Title: addEmployee
* Description: 员工添加页面处理方法
* @param employee
* @return
*/
@RequestMapping(value="/emp",method=RequestMethod.POST)
public String addEmployee(Employee employee) {
System.out.println("添加的员工"+employee);
/**设置提交的部门id以备首页的查询处理*/
employee.setDepartmentId(employee.getDepartment().getId());
/**员工id自增*/
employee.setId(++Employee.autoId);
employeeDao.addEmployee(employee);
System.out.println("重新初始化首页···");
return "redirect:/emps";
}
关于@RequestMapping(value="/emp",method=RequestMethod.POST)注解中的后一部分method=RequestMethod.POST是干嘛用的,其实在环境搭建的时候已经申明了,将使用Rect风格的请求连接方式,其实也就是规范请求路径使得格式统一一点。将添加,修改,删除,查询的请求路径统一格式,然后用不同的请求方式来区分,添加用post请求,修改用put方式,删除用delete方式,查询用get方式。这样来区分不同的请求。在方法中添加员工时使得id自增,和最开始查询员工列表时对应。然后调用employeeDao.java的addEmployee方法添加到数据库即可,再重定向到员工列表页面。方法中的员工对象是会将页面提交的数据自动封装的。使用框架还真是方便。
addEmployee方法:
@Autowired
DepartmentDao departmentDao;
@Override
public void addEmployee(Employee employee) {
// TODO Auto-generated method stub
String sql = "insert employee(id,lastName,email,gender,departmentId) values(?,?,?,?,?)";
int update = MyJdbcTemplate.getInstance().update(sql, employee.getId(),employee.getLastName(),employee.getEmail(),employee.getGender(),employee.getDepartmentId());
System.out.println("员工保存到数据库成功···");
}
@Autowired是自动装配哈。忘记说了。至此添加员工就完成了。
4.5 接下来看修改操作。修改是直接在员工列表页面上的table中的,我也加了个a标签。
update
在上面的员工列表代码图中就有。这个请求地址是带员工id的。但是不是作为请求参数传递的,其实和员工添加处理流程差不多,我就简单略过了。
请求处理的过渡方法:
/**
*
* Title: getEmp
* Description: 员工修改回显处理方法
* @param id
* @param model
* @return
*/
@RequestMapping(value="/emp/{id}",method=RequestMethod.GET)
public String getEmp(@PathVariable("id")Integer id,Model model) {
Employee searchEmployee = employeeDao.searchEmployee(id);
model.addAttribute("updateEmployee",searchEmployee);
/**查询到所有的部门信息添加到请求域中*/
List searchAllDepartment = departmentDao.searchAllDepartment();
model.addAttribute("depts",searchAllDepartment);
System.out.println("员工修改回显数据处理完毕···");
System.out.println("准备跳转到修改页面···");
return "updateEmployee";
}
还是一样的需要过渡,但是这一次不一样了,在这里使用了@PathVariable("id")注解,用在了方法的参数上,上面的请求路径也用了这样的形式”/emp/{id}“,其实这里是能够接收请求路径上的占位符并赋值给对应的参数变量。当修改点击时,会发送一个”当前项目/emp/员工id“的请求,然后在这个方法处接收请求路径上的员工id。在这里通过员工id查询到员工的具体信息然后添加到请求域中,为什么这样做呢?或许会有这样的疑问,这里其实只是用到了springmvc提供的表单标签用作员工的数据回显,然后再响应到具体的员工修改页面。
/**
*
* Title: updateEmp
* Description: 员工修改页面请求
* @param employee
* @return
*/
@RequestMapping(value="/emp/{id}",method=RequestMethod.PUT)
public String updateEmp(@ModelAttribute("employee")Employee employee) {
System.out.println("修改后的员工信息:"+employee);
employeeDao.updata(employee);
return "redirect:/emps";
}
@Override
public boolean updata(Employee employee) {
// TODO Auto-generated method stub
String sql = "update employee set email=?,gender=?,departmentId=? where id=?";
MyJdbcTemplate.getInstance().update(sql, employee.getEmail(),employee.getGender(),employee.getDepartment().getId(),employee.getId());
System.out.println("员工修改成功···");
return false;
}
效果如下:
当我点击了这个update
这种回显的产生是怎么样的,来看一下updateEmployee.jsp页面的代码。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
Insert title here
<%pageContext.setAttribute("ctp", request.getContextPath()); %>
员工修改页面
name:
email:
gender:
男:
女:
dept:
首先还是要在jsp页面添加标签资源<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
主要作用还是
@ModelAttribute
public void myModelAttribute(@RequestParam(value="id",required=false)Integer id,Model model) {
if(id != null) {
Employee searchEmployee = employeeDao.searchEmployee(id);
model.addAttribute("employee",searchEmployee);
}
}
4.6 最后是删除了,删除操作有一个要注意的点,因为也是在员工列表页面的table表中,但是又要使用delete请求方式单纯的使用a标签是行不通过的,只能通过form表单来改请求方式,最后我还是给删除标签添加了a标签然后绑定了点击事件,禁掉了默认跳转行为。在这里添加了jquery到项目中,。然后在员工列表做出了如下操作
员工列表
ID
lastName
email
gender
departmentName
EDIT
DELETE
${emp.id}
${emp.lastName}
${emp.email}
${emp.gender == 1?"男":"女"}
${emp.department.departmentName}
update
delete
添加员工
删除绑定了一个点击事件。在外面放了一个form表单更改请求方式为DELETE,在script中获取点击的对应的请求路径赋值给表单的action,然后提交。又有一个注意的点,如果仅仅是这样操作是会报异常的找不到jquery.js文件,因为这个文件是静态资源文件,在web.xml时添加了前端控制器,拦截除了.jsp页面请求之外的一切请求,所有会找不到jquery.js文件,这个时候就需要在springmvc-servlet.xml中添加两个语句分别是:
这两条语句都必须加上。才能保证动态,静态资源都能访问。
在EmployeeController.java中添加删除的请求处理方法:
/**
*
* Title: deleEmp
* Description: 删除处理
* @param id
* @return
*/
@RequestMapping(value="/emp/{id}",method=RequestMethod.DELETE)
public String deleEmp(@PathVariable("id")Integer id) {
employeeDao.remove(id);
return "redirect:/emps";
}
@Override
public boolean remove(Integer id) {
// TODO Auto-generated method stub
String sql = "delete from employee where id=?";
MyJdbcTemplate.getInstance().update(sql,id);
return true;
}
删除后重定向到员工列表页面。至此基本功能全部完成
五、总结
这次案例对spring,springmvc的具体使用应该囊括了70%左右。在一些组合的用法上还是有许多不足,磕磕绊绊走了很多弯路,索性是完成了。这一次的复习还可以。后续还会继续学习使用。如果有不正确的地方还请大佬指正。