前言
该笔记为尚硅谷SSM实战演练丨ssm整合快速开发CRUD学习笔记
github项目地址:https://github.com/xjhqre/sgg-ssm-demo
一、SSM整合
1、项目介绍
使用SSM框架搭建出一套简单的CRUD项目示例,包括分页查询、Ajax请求、数据校验等。
2、功能点
- 分页查询
- 数据校验:JQuery前端校验+JSR-303后端校验
- Ajax请求
- REST风格的URI:GET查询、POST新增、DELETE删除、PUT修改
3、技术点
- 基础框架-SSM(Spring+SpringMVC+Mybatis)
- 数据库-MySQL
- 前端框架-Bootstrap
- 依赖管理-Maven
- 分页查询-PageHelper
- 逆向工程-Mybatis Generator
二、基础环境搭建
相关配置文件的创建请见SSM整合配置模板,这里主要写下不同的地方。
1、创建maven工程
步骤:
- 创建maven工程快速开始
- 修改pom.xml文件,打包方式为war
- 创建resources目录
- 删除不必要的类和目录
- 创建web.xml文件
2、引入依赖jar包
SpringMVC、Spring:
- spring-webmvc
Spring-Jdbc:
- spring-jdbc
Spring面向切面编程:
- spring-aspects
Mybatis:
- mybatis
mybatis整合Spring:
- mybatis-spring
数据库连接池:
- c3p0(不要用druid)
MySQL驱动:
- mysql-connector-java
其他(jstl,servlet-api,junit):
- jstl
- servlet-api 加上
<scope>provided</scope>
- junit
注意spring包的版本都要相同
pom.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>ssm-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.14</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.14</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.14</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/jstl/jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
3、引入BootStrap
步骤:
- 在webapp文件夹下创建static目录存放bootstrap文件和jQuery文件
- 创建index.jsp页面,引入jquery和bootstrap
index.jsp代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<%--引入jQuery--%>
<script type="text/javascript" src="static/js/jquery-1.12.4.min.js"></script>
<%--引入样式--%>
<link rel="stylesheet" href="static/bootstrap-3.4.1-dist/css/bootstrap.min.css">
<script src="static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>
</head>
<body>
<button type="button" class="btn btn-success">(成功)Success</button>
</body>
</html>
4、SSM整合配置
4.1、配置web.xml
步骤:
- 启动Spring容器,设置
contextConfigLocation
和ContextLoaderListener
,contextConfigLocation
设置spring配置文件的位置,在resources目录下创建applicationContext.xml文件 - 配置springMVC前端控制器dispatchServlet,拦截所有请求,需要指定springMVC配置文件的位置,若不指定,则需要在同级目录下(webapp目录下)创建一个 servlet-name + -servlet的一个xml文件。让dispatchServlet拦截所有请求:/
- 配置字符编码过滤器
CharacterEncodingFilter
,初始化参数 encoding、forceRequestEncoding、forceResponseEncoding,设置过滤所有请求 /*,一定要放在所有过滤器之前 - 使用Rest风格的URI,配置
HiddenHttpMethodFilter
,将指定的 post 转化为 delete 或者是 put 请求。过滤所有请求/*
wen.xml代码:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--1、启动Spring的容器 -->
<!-- needed for ContextLoaderListener -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--2、springmvc的前端控制器,拦截所有请求 -->
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 3、字符编码过滤器,一定要放在所有过滤器之前 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 4、使用Rest风格的URI,将页面普通的post请求转为指定的delete或者put请求 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
4.2、springMVC配置
步骤:
- 配置扫描控制器
- 配置视图解析器
- 启动Servlet默认处理器,处理springMVC不能处理的请求
- 开启MVC注解驱动
dispatcherServlet-servlet.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--SpringMVC的配置文件,包含网站跳转逻辑的控制,配置 -->
<context:component-scan base-package="com.xjhqre.crud" use-default-filters="false">
<!--只扫描控制器。 -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--配置视图解析器,方便页面返回 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--两个标准配置 -->
<!-- 将springmvc不能处理的请求交给tomcat -->
<mvc:default-servlet-handler/>
<!-- 能支持springmvc更高级的一些功能,JSR303校验,快捷的ajax...映射动态请求 -->
<mvc:annotation-driven/>
</beans>
4.3、配置spring
步骤:
- 配置数据源,创建db.properties文件
- 配置和mybatis的整合
- 指定mybatis的核心配置文件
- 指定mybatis的mapper文件
- 配置扫描器,将mybatis接口的实现加入到ioc容器中,扫描所有dao接口的实现,加入到ioc容器中
- 配置一个可以执行批量的sqlSession,加入到IOC容器里
- 事务控制的配置,导入配置好的数据源id
- 配置切入点表达式
- 配置事务增强
- 配置事务如何切入
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 不扫描Controller注解 -->
<context:component-scan base-package="com.xjhqre.crud">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<!-- Spring的配置文件,这里主要配置和业务逻辑有关的 -->
<!--=================== 数据源,事务控制,xxx ========================-->
<context:property-placeholder location="classpath:db.properties"/>
<bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="driverClass" value="${jdbc.driverClassName}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--========================== 配置和MyBatis的整合============================= -->
<!-- 创建sqlSessionFactory工厂对象 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 指定mybatis全局配置文件的位置 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 指定数据源 -->
<property name="dataSource" ref="pooledDataSource"/>
<!-- 指定mapper配置文件-->
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!-- 配置扫描器,将mybatis接口的实现加入到ioc容器中 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--扫描所有dao接口的实现,加入到ioc容器中 -->
<property name="basePackage" value="com.xjhqre.crud.dao"/>
</bean>
<!-- 配置一个可以执行批量的sqlSession -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 配置SQLSession工厂 -->
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
<!-- 配置执行者类型为批量 -->
<constructor-arg name="executorType" value="BATCH"/>
</bean>
<!-- ============================事务控制的配置 =============================-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 控制数据源 -->
<property name="dataSource" ref="pooledDataSource"/>
</bean>
<!--开启基于注解的事务,使用xml配置形式的事务(必要主要的都是使用配置式) -->
<!-- 配置事务如何切入-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 所有方法都是事务方法 -->
<tx:method name="*"/>
<!-- 以get开头的所有方法 -->
<tx:method name="get*" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 切入点表达式 -->
<aop:pointcut id="txPoint" expression="execution(* com.xjhqre.crud.service..*(..))"/>
<!-- 配置事务增强 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
</aop:config>
</beans>
5、编写mybatis核心文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 配置驼峰命名规则 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 设置日志 -->
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
</configuration>
6、创建数据库
DROP DATABASE IF EXISTS ssm;
CREATE DATABASE ssm;
USE ssm;
CREATE TABLE t_emp(
`emp_id` INT(11) PRIMARY KEY AUTO_INCREMENT,
`emp_name` VARCHAR(255) NOT NULL,
`emp_gender` CHAR(1) NOT NULL,
`emp_email` VARCHAR(255),
`dept_id` INT(11)
);
CREATE TABLE t_dept(
`dept_id` INT(11) PRIMARY KEY AUTO_INCREMENT,
`dept_name` VARCHAR(255) NOT NULL
);
7、mybatis逆向工程
使用mybatis的逆向工程生成对应的bean以及mapper
步骤:
- 导入mybatis generator core包
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.7</version>
</dependency>
-
在当前工程目录下创建 mbg.xml 文件(与pom.xml同级目录)
-
配置mgb.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3">
<!-- 设置生成时不添加注释 -->
<commentGenerator>
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- 配置数据库连接 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/ssm?useSSL=false&serverTimezone=UTC"
userId="root"
password="123456">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- 指定javaBean生成的位置 -->
<javaModelGenerator targetPackage="com.xjhqre.crud.pojo"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!--指定sql映射文件生成的位置 -->
<sqlMapGenerator targetPackage="mapper" targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- 指定dao接口生成的位置,mapper接口 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.xjhqre.crud.dao" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- table指定每个表的生成策略 -->
<table tableName="t_emp" domainObjectName="Employee"/>
<table tableName="t_dept" domainObjectName="Department"/>
</context>
</generatorConfiguration>
- 编写一个测试类运行逆向工程生成mapper和类
@Test
public void mbgTest() throws Exception {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("mbg.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
callback, warnings);
myBatisGenerator.generate(null);
}
8、修改mapper映射文件
逆向工程不能生成联合查询。我们需要在mybatis逆向工程生成的mapper映射文件基础上添加新功能,查询员工时带上他的部门信息
步骤:
- 在
EmployeeMapper
接口上新添加两个方法:
List<Employee> selectByExampleWithDept(EmployeeExample example);
Employee selectByPrimaryKeyWithDept(Integer empId);
-
在
Employee
员工类上新增属性private Department department;
,并添加 get和set方法 -
在
EmployeeMapper.xml
中编写编写 With_Dept_Column_List:
<sql id="With_Dept_Column_List">
e.emp_id, e.emp_name, e.emp_gender, e.emp_email, e.dept_id, d.dept_id, d.dept_name
</sql>
- 编写
WithDeptResultMap
返回结果封装:
<resultMap id="WithDeptResultMap" type="com.xjhqre.crud.pojo.Employee">
<id column="emp_id" jdbcType="INTEGER" property="empId" />
<result column="emp_name" jdbcType="VARCHAR" property="empName" />
<result column="emp_gender" jdbcType="CHAR" property="empGender" />
<result column="emp_email" jdbcType="VARCHAR" property="empEmail" />
<result column="dept_id" jdbcType="INTEGER" property="deptId" />
<association property="department" javaType="com.xjhqre.crud.pojo.Department">
<id column="dept_id" property="deptId"/>
<result column="dept_name" property="deptName"/>
</association>
</resultMap>
- 编写
selectByExampleWithDept
:
<select id="selectByExampleWithDept" parameterType="com.xjhqre.crud.pojo.EmployeeExample"
resultMap="WithDeptResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="With_Dept_Column_List"/>
from t_emp e left join t_dept d on e.`dept_id`=d.`dept_id`
<if test="_parameter != null">
<include refid="Example_Where_Clause"/>
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
- 编写
selectByPrimaryKeyWithDept
:
<select id="selectByPrimaryKeyWithDept" parameterType="java.lang.Integer" resultMap="WithDeptResultMap">
select
<include refid="With_Dept_Column_List"/>
from t_emp e left join t_dept d on e.`dept_id`=d.`dept_id`
where emp_id = #{empId,jdbcType=INTEGER}
</select>
9、测试dao接口方法
spring项目推荐使用spring的单元测试,可以自动注入我们需要的组件
需要导入spring-test包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.14</version>
<scope>test</scope>
</dependency>
测试代码:
在Department
类和Employee
类中添加构造函数
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class MapperTest {
@Autowired
DepartmentMapper departmentMapper;
@Autowired
EmployeeMapper employeeMapper;
@Autowired
SqlSession sqlSession;
@Test
public void test1() {
System.out.println(departmentMapper);
// 1. 测试插入部门
departmentMapper.insertSelective(new Department(null, "技术部"));
departmentMapper.insertSelective(new Department(null, "开发部"));
// 2、生成员工数据,测试员工插入
employeeMapper.insertSelective(new Employee(null, "xjhqre", "M", "xjhqre@126.com", 1));
// 3、批量插入多个员工;批量,使用可以执行批量操作的sqlSession。
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
for (int i = 0; i < 100; i++) {
String uid = UUID.randomUUID().toString().substring(0, 5) + i;
mapper.insertSelective(new Employee(null, uid, "M", uid + "@126.com", 1));
}
}
}
三、查询功能实现
步骤:
- 访问 index.jsp页面
- index.jsp 页面发送出查询员工列表请求
- EmployeeController来接收请求,查出员工数据
- 来到 list.jsp 页面进行展示
1、编写 index.jsp 页面:
发送请求为 /emps
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<jsp:forward page="/emps"></jsp:forward>
<head>
<title>Title</title>
</head>
<body>
</body>
</html>
2、编写list.jsp页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>员工列表</title>
</head>
<body>
</body>
</html>
3、编写EmployeeService类
@Service
public class EmployeeService {
@Autowired
EmployeeMapper employeeMapper;
/**
* 查询所有员工
* @return 员工信息
*/
public List<Employee> queryAllEmployees() {
return employeeMapper.selectByExampleWithDept(null);
}
}
4、引入PageHelper分页插件
- 在pom.xml文件里引入PageHelper依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.2</version>
</dependency>
- 在 mybatis 核心文件中注册插件,写在 <typeAliases>标签后面
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
5、编写EmployeeController类
@Controller
public class EmployeeController {
@Autowired
EmployeeService employeeService;
/**
* 查询员工数据
* @return 员工信息
*/
@RequestMapping("/emps")
public String queryAllEmployees(@RequestParam(value = "pn", defaultValue = "1")Integer pn, Model model) {
// 在查询之前调用分页插件,传入页码以及每页分页的大小
PageHelper.startPage(pn, 5);
// startPage后面紧跟的这个查询就是一个分页查询
List<Employee> employees = employeeService.queryAllEmployees();
// 使用pageInfo包装查询后的结果,只需要将pageInfo交给页面展示
PageInfo pageInfo = new PageInfo(employees, 5); // 传入页码显示数量
model.addAttribute("pageInfo", pageInfo);
return "list"; // 返回到 list.jsp 页面
}
}
6、虚拟MVC测试
/**
* 使用Spring测试模块提供的测试请求功能,测试curd请求的正确性
* Spring4测试的时候,需要servlet3.0的支持
*
* @author lfy
*/
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration // 表示测试的 ApplicationContext 应该是WebApplicationContext
@ContextConfiguration(locations = {"classpath:applicationContext.xml",
"/WEB-INF/dispatcherServlet-servlet.xml"})
public class MvcTest {
// 传入SpringMVC的ioc
@Autowired
WebApplicationContext context;
// 虚拟MVC请求,获取到处理结果
MockMvc mockMvc;
@Before
public void initMokcMvc() {
// 初始化mockMvc
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
@Test
public void testPage() throws Exception {
// 模拟发送get请求, 传入参数名和参数的值,拿到返回值
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/emps").param("pn", "5")).andReturn();
// 请求成功以后,请求域中会有pageInfo;我们可以取出pageInfo进行验证
MockHttpServletRequest request = result.getRequest();
PageInfo pageInfo = (PageInfo) request.getAttribute("pageInfo");
System.out.println("当前页码:" + pageInfo.getPageNum());
System.out.println("总页码:" + pageInfo.getPages());
System.out.println("总记录数:" + pageInfo.getTotal());
System.out.println("在页面需要连续显示的页码");
int[] nums = pageInfo.getNavigatepageNums();
for (int i : nums) {
System.out.print(" "+i);
}
System.out.println();
//获取员工数据
List<Employee> list = pageInfo.getList();
for (Employee employee : list) {
System.out.println("ID:"+employee.getEmpId()+"==>Name:"+employee.getEmpName());
}
}
}
7、完善list.jsp页面
引入 jsp 依赖包,不引入无法使用pageContext
的setAttribute
方法
<!-- https://mvnrepository.com/artifact/javax.servlet/jsp-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
list.jsp页面:
- 从
pageInfo
中取出取出员工信息显示 - 完善分页条
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>员工列表</title>
<%
pageContext.setAttribute("APP_PATH", request.getContextPath());
%>
<!-- web路径:
不以/开始的相对路径,找资源,以当前资源的路径为基准,经常容易出问题。
以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:3306);需要加上项目名
http://localhost:3306/crud
-->
<script type="text/javascript" src="${APP_PATH}/static/js/jquery-1.12.4.min.js"></script>
<link href="${APP_PATH}/static/bootstrap-3.4.1-dist/css/bootstrap.min.css" rel="stylesheet">
<script src="${APP_PATH}/static/bootstrap-3.4.1-dist/css/bootstrap.min.css"></script>
</head>
<body>
<%-- 搭建显示页面 --%>
<div class="container">
<%-- 标题 --%>
<div class="row">
<div class="col-md-12">
<h1>SSM_CRUD</h1>
</div>
</div>
<%-- 按钮 --%>
<div class="row">
<div class="col-md-2 col-md-offset-10">
<button class="btn btn-primary">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>添加
</button>
<button class="btn btn-danger">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>删除
</button>
</div>
</div>
<%-- 显示表格数据 --%>
<div class="row">
<div class="col-md-12">
<table class="table table-striped">
<tr>
<th>#</th>
<th>empName</th>
<th>gender</th>
<th>email</th>
<th>deptName</th>
<th>操作</th>
</tr>
<c:forEach items="${pageInfo.list}" var="emp">
<tr>
<th>${emp.empId}</th>
<th>${emp.empName}</th>
<th>${emp.empGender}</th>
<th>${emp.empEmail}</th>
<th>${emp.department.deptName}</th>
<th>
<button class="btn btn-primary">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>编辑
</button>
<button class="btn btn-danger">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>删除
</button>
</th>
</tr>
</c:forEach>
</table>
</div>
</div>
<%-- 显示分页信息 --%>
<div class="row">
<%--分页条信息--%>
<div class="col-md-6 col-md-offset-3 text-center">
<nav aria-label="Page navigation">
<ul class="pagination">
<li><a href="${APP_PATH}/emps?pn=1">首页</a></li>
<c:if test="${pageInfo.hasPreviousPage}">
<li>
<a href="${APP_PATH}/emps?pn=${pageInfo.pageNum-1}" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
</c:if>
<c:forEach items="${pageInfo.navigatepageNums}" var="num">
<c:if test="${pageInfo.pageNum == num}">
<li class="active"><a href="${APP_PATH }/emps?pn=${num}">${num}</a></li>
</c:if>
<c:if test="${pageInfo.pageNum != num}">
<li><a href="${APP_PATH }/emps?pn=${num}">${num}</a></li>
</c:if>
</c:forEach>
<c:if test="${pageInfo.hasNextPage}">
<li>
<a href="${APP_PATH}/emps?pn=${pageInfo.pageNum+1}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</c:if>
<li><a href="${APP_PATH}/emps?pn=${pageInfo.pages}">末页</a></li>
</ul>
</nav>
</div>
</div>
<div class="row">
<%--分页文字信息 --%>
<div class="col-md-6 col-md-offset-3 text-center">当前第${pageInfo.pageNum}页,总页数:${pageInfo.pages}, 总记录数:${pageInfo.total}</div>
</div>
</div>
</body>
</html>
四、改写查询功能
步骤:
- index.jsp 页面直接发送 Ajax 请求进行员工分页数据的查询
- 服务器将查出的数据,以 JSON 字符串的形式返回给浏览器
- 浏览器收到 js 字符串。可以使用 js 对 json 进行解析,使用 js 通过 dom 增删改查改变页面
- 返回JSON。实现客户端的无关性
1、创建Msg类
类中定义状态码、提示信息、返回给用户的数据、以及相关的 get、set方法,还要定义一个 add 方法,返回 Msg 类对象,用于链式调用
public class Msg {
// 状态码 100:成功 200:失败
private int code;
// 提示信息
private String msg;
// 返回给用户的数据,用map存储
private Map<String, Object> extend = new HashMap<>();
// 返回成功方法
public static Msg success() {
Msg result = new Msg();
result.setCode(100);
result.setMsg("处理成功!");
return result;
}
// 返回失败方法
public static Msg fail() {
Msg result = new Msg();
result.setCode(200);
result.setMsg("处理失败!");
return result;
}
// 用于链式添加信息
public Msg add(String key,Object value){
this.getExtend().put(key, value);
return this;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Map<String, Object> getExtend() {
return extend;
}
public void setExtend(Map<String, Object> extend) {
this.extend = extend;
}
}
2、改写处理器方法
返回 json 数据,需要导入jackson包
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
EmployeeController:
@Controller
public class EmployeeController {
@Autowired
EmployeeService employeeService;
/**
* 查询员工数据
* @return 员工信息
*/
@RequestMapping("/emps")
@ResponseBody
public Msg queryAllEmployees(@RequestParam(value = "pn", defaultValue = "1")Integer pn) {
PageHelper.startPage(pn, 5);
List<Employee> employees = employeeService.queryAllEmployees();
PageInfo pageInfo = new PageInfo(employees, 5);
return Msg.success().add("pageInfo", pageInfo);
}
}
3、修改 index.jsp
在 index.jsp 页面加载完后 发送 Ajax 请求,将返回的 JSON 数据解析。删除原来的 <c:if> 和 <c:foreach> 标签
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>员工列表</title>
<%
pageContext.setAttribute("APP_PATH", request.getContextPath());
%>
<!-- web路径:
不以/开始的相对路径,找资源,以当前资源的路径为基准,经常容易出问题。
以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:3306);需要加上项目名
http://localhost:3306/crud
-->
<script type="text/javascript" src="${APP_PATH}/static/js/jquery-1.12.4.min.js"></script>
<link href="${APP_PATH}/static/bootstrap-3.4.1-dist/css/bootstrap.min.css" rel="stylesheet">
<script src="${APP_PATH}/static/bootstrap-3.4.1-dist/css/bootstrap.min.css"></script>
</head>
<body>
<%-- 搭建显示页面 --%>
<div class="container">
<%-- 标题 --%>
<div class="row">
<div class="col-md-12">
<h1>SSM_CRUD</h1>
</div>
</div>
<%-- 按钮 --%>
<div class="row">
<div class="col-md-2 col-md-offset-10">
<button class="btn btn-primary">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>添加
</button>
<button class="btn btn-danger">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>删除
</button>
</div>
</div>
<%-- 显示表格数据 --%>
<div class="row">
<div class="col-md-12">
<table class="table table-striped" id="emps_table">
<thead>
<tr>
<th>#</th>
<th>empName</th>
<th>gender</th>
<th>email</th>
<th>deptName</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%-- 通过函数加载数据 --%>
</tbody>
</table>
</div>
</div>
<%-- 显示分页信息 --%>
<div class="row">
<%--分页条信息--%>
<div class="col-md-6 col-md-offset-3 text-center" id="page_info_area"></div>
</div>
<div class="row">
<%--分页文字信息 --%>
<div class="col-md-6 col-md-offset-3 text-center" id="page_nav_area"></div>
</div>
</div>
<script type="text/javascript">
// 初始化页面时需要调用的函数
$(function () {
// 去首页
to_page(1);
});
// 页面跳转函数
function to_page(pn) {
$.ajax({
url: "${APP_PATH}/emps",
data: "pn=" + pn,
type: "GET",
success: function (result) {
// 1.解析员工数据
build_emps_table(result);
// 2. 解析并显示分页信息
build_page_info(result);
// 3. 解析显示分页条码
build_page_nav(result);
}
});
}
function build_emps_table(result) {
// 清空表格信息
$("#emps_table tbody").empty()
// 获取员工列表
var emps = result.extend.pageInfo.list;
// 遍历员工列表,构建表格
$.each(emps, function (index, item) {
var empIdTd = $("<td></td>").append(item.empId);
var empNameTd = $("<td></td>").append(item.empName)
var genderTd = $("<td></td>").append(item.empGender == "M" ? "男" : "女")
var emailTd = $("<td></td>").append(item.empEmail)
var deptNameTd = $("<td></td>").append(item.department.deptName)
// 注入按钮数据
var editBtn = $("<button></button>").addClass("btn btn-primary btn-sm")
.append($("<span></span>").addClass("glyphicon glyphicon-pencil"))
.append("编辑");
var delBtn = $("<button></button>").addClass("btn btn-danger btn-sm")
.append($("<span></span>").addClass("glyphicon glyphicon-trash"))
.append("删除");
var btnTd = $("<td></td>").append(editBtn).append(" ").append(delBtn);
$("<tr></tr>").append(empIdTd)
.append(empNameTd)
.append(genderTd)
.append(emailTd)
.append(deptNameTd)
.append(btnTd)
.appendTo("#emps_table tbody");
});
}
// 解析显示分页信息
function build_page_info(result) {
// 清空原来的分页信息
$("#page_info_area").empty()
$("#page_info_area").append("当前页:" +
result.extend.pageInfo.pageNum + ",总页数:" +
result.extend.pageInfo.pages + ",总记录数:" +
result.extend.pageInfo.total)
}
// 解析显示分页条
function build_page_nav(result) {
// 清空原来的分页码
$("#page_nav_area").empty();
// 构建元素
var ul = $("<ul></ul>").addClass("pagination")
var firstPageLi = $("<li></li>").append($("<a></a>").append("首页").attr("href", "#"));
var prePageLi = $("<li></li>").append($("<a></a>").append("«").attr("href", "#"));
var nextPageLi = $("<li></li>").append($("<a></a>").append("»").attr("href", "#"));
var lastPageLi = $("<li></li>").append($("<a></a>").append("末页").attr("href", "#"));
// 添加首页、末页、上一页、下一页禁用 和跳转功能
if (result.extend.pageInfo.hasPreviousPage == false) {
firstPageLi.addClass("disabled")
prePageLi.addClass("disabled")
} else {
firstPageLi.click(function () {
to_page(1)
})
prePageLi.click(function () {
to_page(result.extend.pageInfo.pageNum-1)
})
}
if (result.extend.pageInfo.hasNextPage == false) {
nextPageLi.addClass("disabled");
lastPageLi.addClass("disabled");
} else {
lastPageLi.click(function () {
to_page(result.extend.pageInfo.pages);
});
nextPageLi.click(function () {
to_page(result.extend.pageInfo.pageNum+1);
});
}
// 添加首页和前一页
ul.append(firstPageLi).append(prePageLi)
// 构建条码
$.each(result.extend.pageInfo.navigatepageNums, function (index, num) {
var numLi = $("<li></li>").append($("<a></a>").append(num).attr("href", "#"));
// 设置当前页码高亮显示
if (result.extend.pageInfo.pageNum == num) {
numLi.addClass("active")
}
// 添加按钮跳转事件
numLi.click(function () {
to_page(num)
})
ul.append(numLi)
});
// 添加末页和后一页
ul.append(nextPageLi).append(lastPageLi)
var navEle = $("<nav></nav>").append(ul)
navEle.appendTo("#page_nav_area");
}
</script>
</body>
</html>
五、编写添加功能
步骤:
- 在index.jsp页面点击“新增”按钮
- 弹出添加模态框
- 从数据库中查询出所有部门名称显示在下拉列表中
- 用户输入数据
- 校验用户名、邮箱格式是否正确、用户名是否重复
- 点击“保存”按钮添加数据
1、编写查询所有部门的业务逻辑
1.1、编写DepartmentService
@Service
public class DepartmentService {
@Autowired
DepartmentMapper departmentMapper;
public List<Department> quireAllDepartments() {
return departmentMapper.selectByExample(null);
}
}
1.2、编写DepartmentController
@Controller
public class DepartmentController {
@Autowired
DepartmentService departmentService;
@RequestMapping("/depts")
@ResponseBody
public Msg quireAllDepartments() {
List<Department> departments = departmentService.quireAllDepartments();
return Msg.success().add("depts", departments);
}
}
2、编写保存员工代码
规定URI:
- /emp/{id} GET查询员工
- /emp POST保存员工
- /emp/{id} PUT修改员工
- /emp/{id} DELETE删除员工
2.1、编写Service层方法
public void saveEmp(Employee employee) {
employeeMapper.insertSelective(employee);
}
2.2、编写controller层方法
@RequestMapping(value = "/emp", method = RequestMethod.POST)
@ResponseBody
public Msg saveEmp(Employee employee) {
employeeService.saveEmp(employee);
return Msg.success();
}
3、设置分页助手
在添加用户后跳转到最后一页,传入跳转的页数为总记录数,需要设置分页助手参数合理化。否则会跳转到其他非法地址
在 mybatis 核心配置文件中配置
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 分页参数合理化 -->
<property name="reasonable" value="true"/>
</plugin>
</plugins>
reasonable:分页合理化参数,默认值为 false
,当该参数为 true
时,pageNum<=0
时会查询第一页,pageNum
(超过总数时),会查询最后一页,默认 false
时,直接根据参数进行查询
4、前端校验
- 用户名格式校验
- 邮箱格式校验
- 邮箱是否重复校验
4.1、检查邮箱是否已存在
因为员工姓名可能存在重复的情况,所以检查邮箱是否已存在
当输入完邮箱后失去焦点,页面发送Ajax请求查询邮箱是否重复,重复则在页面显示“邮箱地址重复”
发送表单中含有中文,使用POST请求,因为GET请求不校验中文
编写Service层方法
/**
* 检查邮箱是否重复
* @param empEmail 页面传来的邮箱
* @return 返回该邮箱是否可用
*/
public boolean checkForDuplicateEmails(String empEmail) {
EmployeeExample example = new EmployeeExample();
EmployeeExample.Criteria criteria = example.createCriteria();
criteria.andEmpEmailEqualTo(empEmail);
long count = employeeMapper.countByExample(example);
return count == 0;
}
编写Controller层方法
/**
* 检查邮箱是否可用
* @param empEmail 网页传来的邮箱
* @return json数据
*/
@RequestMapping("/checkEmail")
@ResponseBody
public Msg checkForDuplicateEmails(@RequestParam("empEmail")String empEmail) {
boolean b = employeeService.checkForDuplicateEmails(empEmail);
if(b){
return Msg.success();
}else {
return Msg.fail();
}
}
5、后端校验
重要数据使用 JSR303 进行校验
5.1、导入相关jar包
需要导入 Hibernate-Validator
:
JSR303数据校验支持:tomcat7及以上的服务器,tomcat7以下的服务器:el表达式。额外给服务器的lib包中替换新的标准的el
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.0.Final</version>
</dependency>
5.2、修改Employee类
给empName和empEmail属性添加校验注解
@Pattern(regexp = "(^[a-zA-Z0-9_-]{3,16}$)|(^[\\u2E80-\\u9FFF]{2,5})",
message = "用户名格式错误")
private String empName;
@Pattern(regexp = "^[a-z\\d]+(\\.[a-z\\d]+)*@([\\da-z](-[\\da-z])?)+(\\.{1,2}[a-z]+)+$",
message = "邮箱格式错误")
private String empEmail;
5.3、修改添加员工控制器
@Valid:指定要校验的数据
BindingResult:封装校验的结果
打印中文乱码则在tomcat中的虚拟机选项配置:-Dfile.encoding=UTF-8
@RequestMapping(value = "/emp", method = RequestMethod.POST)
@ResponseBody
public Msg saveEmp(@Valid Employee employee, BindingResult result) {
if (result.hasErrors()) {
Map<String, Object> map = new HashMap<>();
List<FieldError> errors = result.getFieldErrors();
for (FieldError error : errors) {
System.out.println("错误的字段名:" + error.getField());
System.out.println("错误信息:" + error.getDefaultMessage());
map.put(error.getField(), error.getDefaultMessage());
}
return Msg.fail().add("errorFields", map);
} else {
employeeService.saveEmp(employee);
return Msg.success();
}
}
5.4、修改保存按钮事件函数
// 模态框保存按钮点击事件
$("#saveEmpBtn").click(function () {
// 校验表单数据
if (!isTheUsernameCorrect || !isTheEmailAddressCorrect) {
return false
}
// 发送保存请求
$.ajax({
url: "${APP_PATH}/emp",
type: "POST",
data: $("#empAddModal form").serialize(),
success: function (result) {
if(result.code == 100) {
// 员工保存成功
// 1. 关闭模态框
$("#empAddModal").modal('hide')
// 2. 跳转到最后一页显示
to_page(totalRecord)
} else {
// 显示失败信息
if(undefined != result.extend.errorFields.empEmail){
validate_add_from_information_show("#empEmail_add_input", "error", "邮箱格式不正确")
}
if(undefined != result.extend.errorFields.empName){
validate_add_from_information_show("#empName_add_input", "error", "员工名称格式不正确")
}
}
}
})
})
5.5、完整jsp页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>员工列表</title>
<%
pageContext.setAttribute("APP_PATH", request.getContextPath());
%>
<!-- web路径:
不以/开始的相对路径,找资源,以当前资源的路径为基准,经常容易出问题。
以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:3306);需要加上项目名
http://localhost:3306/crud
-->
<script type="text/javascript" src="${APP_PATH}/static/js/jquery-1.12.4.min.js"></script>
<script src="${APP_PATH }/static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>
<link href="${APP_PATH}/static/bootstrap-3.4.1-dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<%-- 添加员工模态框 --%>
<div class="modal fade" id="empAddModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span>
</button>
<h4 class="modal-title" id="myModalLabel">添加员工</h4>
</div>
<div class="modal-body">
<%-- 表单 --%>
<form class="form-horizontal">
<div class="form-group">
<label for="empName_add_input" class="col-sm-2 control-label">empName</label>
<div class="col-sm-10">
<input name="empName" type="text" class="form-control" id="empName_add_input"
placeholder="empName">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label for="empEmail_add_input" class="col-sm-2 control-label">Email</label>
<div class="col-sm-10">
<input name="empEmail" type="text" class="form-control" id="empEmail_add_input"
placeholder="empEmail">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">empGender</label>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="empGender" id="empGender_add_input" value="M" checked="checked"> 男
</label>
<label class="radio-inline">
<input type="radio" name="empGender" id="empGender_add_input2" value="F"> 女
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">deptId</label>
<div class="col-sm-4">
<select class="form-control" name="deptId" id="deptId_add_select">
</select>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" id="saveEmpBtn">保存</button>
</div>
</div>
</div>
</div>
<%-- 搭建显示页面 --%>
<div class="container">
<%-- 标题 --%>
<div class="row">
<div class="col-md-12">
<h1>SSM_CRUD</h1>
</div>
</div>
<%-- 按钮 --%>
<div class="row">
<div class="col-md-2 col-md-offset-10">
<button class="btn btn-primary" id="emp_add_modal_btn">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>添加
</button>
<button class="btn btn-danger">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>删除
</button>
</div>
</div>
<%-- 显示表格数据 --%>
<div class="row">
<div class="col-md-12">
<table class="table table-striped" id="emps_table">
<thead>
<tr>
<th>#</th>
<th>empName</th>
<th>gender</th>
<th>email</th>
<th>deptName</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%-- 通过函数加载数据 --%>
</tbody>
</table>
</div>
</div>
<%-- 显示分页信息 --%>
<div class="row">
<%--分页条信息--%>
<div class="col-md-6 col-md-offset-3 text-center" id="page_info_area"></div>
</div>
<div class="row">
<%--分页文字信息 --%>
<div class="col-md-6 col-md-offset-3 text-center" id="page_nav_area"></div>
</div>
</div>
<script type="text/javascript">
/* ================================================ 全局变量 ================================================ */
var totalRecord; // 用于跳转到末页
var isTheUsernameCorrect = false // 用户名是否正确
var isTheEmailAddressCorrect = false // 邮箱是否正确
/* ================================================ 函数 ================================================ */
// 初始化页面时需要调用的函数
$(function () {
// 去首页
to_page(1);
});
// 页面跳转函数
function to_page(pn) {
$.ajax({
url: "${APP_PATH}/emps",
data: "pn=" + pn,
type: "GET",
success: function (result) {
// 1.解析员工数据
build_emps_table(result);
// 2. 解析并显示分页信息
build_page_info(result);
// 3. 解析显示分页条码
build_page_nav(result);
}
});
}
// 解析表格数据函数
function build_emps_table(result) {
// 清空表格信息
$("#emps_table tbody").empty()
// 获取员工列表
var emps = result.extend.pageInfo.list;
// 遍历员工列表,构建表格
$.each(emps, function (index, item) {
var empIdTd = $("<td></td>").append(item.empId);
var empNameTd = $("<td></td>").append(item.empName)
var genderTd = $("<td></td>").append(item.empGender == "M" ? "男" : "女")
var emailTd = $("<td></td>").append(item.empEmail)
var deptNameTd = $("<td></td>").append(item.department.deptName)
// 注入按钮数据
var editBtn = $("<button></button>").addClass("btn btn-primary btn-sm")
.append($("<span></span>").addClass("glyphicon glyphicon-pencil"))
.append("编辑");
var delBtn = $("<button></button>").addClass("btn btn-danger btn-sm")
.append($("<span></span>").addClass("glyphicon glyphicon-trash"))
.append("删除");
var btnTd = $("<td></td>").append(editBtn).append(" ").append(delBtn);
$("<tr></tr>").append(empIdTd)
.append(empNameTd)
.append(genderTd)
.append(emailTd)
.append(deptNameTd)
.append(btnTd)
.appendTo("#emps_table tbody");
});
}
// 解析显示分页信息函数
function build_page_info(result) {
// 清空原来的分页信息
$("#page_info_area").empty()
$("#page_info_area").append("当前页:" +
result.extend.pageInfo.pageNum + ",总页数:" +
result.extend.pageInfo.pages + ",总记录数:" +
result.extend.pageInfo.total)
totalRecord = result.extend.pageInfo.total
}
// 解析显示分页条函数
function build_page_nav(result) {
// 清空原来的分页码
$("#page_nav_area").empty();
// 构建元素
var ul = $("<ul></ul>").addClass("pagination")
var firstPageLi = $("<li></li>").append($("<a></a>").append("首页").attr("href", "#"));
var prePageLi = $("<li></li>").append($("<a></a>").append("«").attr("href", "#"));
var nextPageLi = $("<li></li>").append($("<a></a>").append("»").attr("href", "#"));
var lastPageLi = $("<li></li>").append($("<a></a>").append("末页").attr("href", "#"));
// 添加首页、末页、上一页、下一页禁用 和跳转功能
if (result.extend.pageInfo.hasPreviousPage == false) {
firstPageLi.addClass("disabled")
prePageLi.addClass("disabled")
} else {
firstPageLi.click(function () {
to_page(1)
})
prePageLi.click(function () {
to_page(result.extend.pageInfo.pageNum - 1)
})
}
if (result.extend.pageInfo.hasNextPage == false) {
nextPageLi.addClass("disabled");
lastPageLi.addClass("disabled");
} else {
lastPageLi.click(function () {
to_page(result.extend.pageInfo.pages);
});
nextPageLi.click(function () {
to_page(result.extend.pageInfo.pageNum + 1);
});
}
// 添加首页和前一页
ul.append(firstPageLi).append(prePageLi)
// 构建条码
$.each(result.extend.pageInfo.navigatepageNums, function (index, num) {
var numLi = $("<li></li>").append($("<a></a>").append(num).attr("href", "#"));
// 设置当前页码高亮显示
if (result.extend.pageInfo.pageNum == num) {
numLi.addClass("active")
}
// 添加按钮跳转事件
numLi.click(function () {
to_page(num)
})
ul.append(numLi)
});
// 添加末页和后一页
ul.append(nextPageLi).append(lastPageLi)
var navEle = $("<nav></nav>").append(ul)
navEle.appendTo("#page_nav_area");
}
// 查询所有部门信息请求函数
function getDepts() {
$.ajax({
url: "${APP_PATH}/depts",
type: "GET",
success: function (result) {
console.log(result)
$.each(result.extend.depts, function () {
var optionElement = $("<option></option>").append(this.deptName).attr("value", this.deptId)
optionElement.appendTo("#deptId_add_select")
})
}
})
}
// 添加员工表单数据校验信息显示函数
function validate_add_from_information_show(ele, status, msg) {
// 清楚当前元素的校验状态
$(ele).parent().removeClass("has-success has-error")
$(ele).next("span").text("")
if("success" == status){
$(ele).parent().addClass("has-success")
$(ele).next("span").text(msg)
} else if ("error" == status) {
$(ele).parent().addClass("has-error")
$(ele).next("span").text(msg)
}
}
// 重置添加员工表单数据和样式
function reset_form(ele) {
// 清空表单数据
$(ele + " form")[0].reset()
// 清空表单样式
$(ele + " form").find("*").removeClass("has-success has-error")
$(ele + " form").find(".help-block").text("")
// 清空下拉列表
$(ele + " select").empty()
}
/* ============================================== 按钮绑定事件区 ============================================== */
// 添加员工按钮事件
$("#emp_add_modal_btn").click(function () {
// 表单重置数据
reset_form("#empAddModal")
// 发送Ajax请求, 查出部门信息,显示在下拉列表中
getDepts()
$("#empAddModal").modal({
backdrop: "static"
})
})
// 模态框保存按钮点击事件
$("#saveEmpBtn").click(function () {
// 校验表单数据
if (!isTheUsernameCorrect || !isTheEmailAddressCorrect) {
return false
}
// 发送保存请求
$.ajax({
url: "${APP_PATH}/emp",
type: "POST",
data: $("#empAddModal form").serialize(),
success: function (result) {
if(result.code == 100) {
// 员工保存成功
// 1. 关闭模态框
$("#empAddModal").modal('hide')
// 2. 跳转到最后一页显示
to_page(totalRecord)
} else {
// 显示失败信息
if(undefined != result.extend.errorFields.empEmail){
validate_add_from_information_show("#empEmail_add_input", "error", "邮箱格式不正确")
}
if(undefined != result.extend.errorFields.empName){
validate_add_from_information_show("#empName_add_input", "error", "员工名称格式不正确")
}
}
}
})
})
// 用户名栏失去焦点时进行格式检查
$("#empName_add_input").blur(function () {
var empName = $("#empName_add_input").val();
var regName = /(^[a-zA-Z0-9_-]{3,16}$)|(^[\u2E80-\u9FFF]{2,5})/ // 允许英文和中文
if(!regName.test(empName)){
validate_add_from_information_show("#empName_add_input", "error", "员工名称格式不正确")
} else {
validate_add_from_information_show("#empName_add_input", "success", "")
isTheUsernameCorrect = true
}
})
// 邮箱栏失去焦点时进行格式检查和重复检查
$("#empEmail_add_input").blur(function () {
var empEmail = $("#empEmail_add_input").val()
var regEmail = /^[a-z\d]+(\.[a-z\d]+)*@([\da-z](-[\da-z])?)+(\.{1,2}[a-z]+)+$/
if(!regEmail.test(empEmail)){
validate_add_from_information_show("#empEmail_add_input", "error", "邮箱格式不正确")
}
// 发送Ajax请求判断邮箱是否重复
$.ajax({
url:"${APP_PATH}/checkEmail",
data:"empEmail=" + empEmail,
type:"GET", // get请求不检验中文,如果发送对象中含中文则用POST
success:function (result) {
console.log(result)
if(result.code == 100 && regEmail.test(empEmail)) {
validate_add_from_information_show("#empEmail_add_input", "success", "")
isTheEmailAddressCorrect = true
} else if (result.code == 200) {
validate_add_from_information_show("#empEmail_add_input", "error", "邮箱已存在")
}
}
})
})
</script>
</body>
</html>
六、修改员工信息功能
步骤:
- 点击编辑
- 弹出用户修改的模态框(显示用户信息)
- 点击更新,完成用户修改
1、编写修改模态框
<%-- 修改员工模态框 --%>
<div class="modal fade" id="empUpdateModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span>
</button>
<h4 class="modal-title" id="updateModalLabel">修改员工</h4>
</div>
<div class="modal-body">
<%-- 表单 --%>
<form class="form-horizontal">
<div class="form-group">
<label for="empName_add_input" class="col-sm-2 control-label">empName</label>
<div class="col-sm-10">
<input name="empName" type="text" class="form-control" id="empName_update_input"
placeholder="empName">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label for="empEmail_add_input" class="col-sm-2 control-label">Email</label>
<div class="col-sm-10">
<input name="empEmail" type="text" class="form-control" id="empEmail_update_input"
placeholder="empEmail">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">empGender</label>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="empGender" id="empGender_update_input" value="M" checked="checked"> 男
</label>
<label class="radio-inline">
<input type="radio" name="empGender" id="empGender_update_input2" value="F"> 女
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">deptId</label>
<div class="col-sm-4">
<select class="form-control" name="deptId" id="deptId_update_select">
</select>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" id="updateEmpBtn">更新</button>
</div>
</div>
</div>
</div>
2、添加按钮样式
给员工列表的修改和删除按钮添加样式:edit_btn、del_btn
// 注入按钮数据
var editBtn = $("<button></button>").addClass("btn btn-primary btn-sm edit_btn")
.append($("<span></span>").addClass("glyphicon glyphicon-pencil"))
.append("编辑");
var delBtn = $("<button></button>").addClass("btn btn-danger btn-sm del_btn")
.append($("<span></span>").addClass("glyphicon glyphicon-trash"))
.append("删除");
3、修改重置表单函数
新增下拉列表的重置
// 重置添加员工表单数据和样式
function reset_form(ele) {
// 清空表单数据
$(ele + " form")[0].reset()
// 清空表单样式
$(ele + " form").find("*").removeClass("has-success has-error")
$(ele + " form").find(".help-block").text("")
// 清空下拉列表
$(ele + " select").empty()
}
4、编辑按钮事件
// 为修改员工按钮添加事件
// 因为是先绑定事件后在生成的按钮,所以不能用click
// 早先版本的jQuery可以使用live方法
// 这里使用 on 方法绑定
$(document).on("click", ".edit_btn", function () {
// 1. 查询部门信息显示
getDepts("#deptId_update_select")
// 2. 回显员工信息
// 3. 跳出模态框
$("#empUpdateModal").modal({
backdrop: "static"
})
})
5、回显用户数据
5.1、编写控制器方法
/**
* 根据id查询员工信息
* @param id 页面传入的id
* @return 员工对象的JSON数据
*/
@RequestMapping(value = "/emp/{id}", method = RequestMethod.GET)
@ResponseBody
public Msg getEmpById(@PathVariable("id")Integer id) {
Employee emp = employeeService.getEmpById(id);
return Msg.success().add("emp", emp);
}
5.2、编写Service层方法
public Employee getEmpById(Integer id) {
return employeeMapper.selectByPrimaryKey(id);
}
5.3、网页查询员工信息函数
// 根据id查询员工信息函数
function getEmpById(id) {
$.ajax({
url:"${APP_PATH}/emp/" + id,
type:"GET",
success:function (result) {
console.log(result)
}
})
}
5.4、为修改员工按钮添加属性
在定义editBtn按钮之后添加empId属性,用于修改员工时传递id值
// 添加自定义属性,用于修改员工时传递id值
editBtn.attr("empId_for_edit", item.empId)
5.5、回显员工信息函数
这里传入的是部门id,而回显的是部门名称的原因:
- val() 方法返回或设置被选元素的值。元素的值是通过
value
属性设置的。该方法大多用于input
元素。 - 查询部门信息时,value属性定义的是id,显示的是部门名,页面根据传入的id值选择了相应的部门名
// 根据id查询员工信息函数
function getEmpById(id) {
$.ajax({
url:"${APP_PATH}/emp/" + id,
type:"GET",
success:function (result) {
var empData = result.extend.empty;
$("#empName_update_input").val(empData.empName);
$("#empEmail_update_input").val(empData.empEmail);
$("#empUpdateModal input[name=empGender]").val(empData.empGender);
$("#empUpdateModal select").val(empData.deptId);
}
})
}
5.6、完善修改按钮事件
// 为修改员工按钮添加事件
// 因为是先绑定事件后在生成的按钮,所以不能用click
// 早先版本的jQuery可以使用live方法
// 这里使用 on 方法绑定
$(document).on("click", ".edit_btn", function () {
// 1. 查询部门信息显示
getDepts("#deptId_update_select")
// 2. 回显员工信息
getEmpById($(this).attr("empId_for_edit"))
// 3. 跳出模态框
$("#empUpdateModal").modal({
backdrop: "static"
})
})
6、更新按钮事件
步骤:
- 绑定更新按钮事件
- 前端校验用户名和邮箱格式
- 将编辑按钮的id属性传递给更新按钮
- 前端发送Ajax请求更新员工数据
- 编写更新员工控制器
- 编写更新员工Service方法
6.1、控制器方法
注意事项
@RequestMapping(value = "/emp/{empId}", method = RequestMethod.PUT)
上述代码里的 {empId} ,大括号中的名称必须和对象类的属性名相同
如果直接使用ajax=PUT的请求
存在问题:
- 请求体中有数据,但是Employee对象封装属性失败
原因:
- Tomcat将请求体中的数据封装为一个map,SpringMVC封装POJO对象的时候,底层使用request.getParameter(“empName”) 从map中取值
- Tomcat不会封装请求体中的数据为map,只有POST请求才会封装成map
request和connector原码:
org.apache.catalina.connector.Request--parseParameters()
(3111);
protected String parseBodyMethods = "POST";
if( !getConnector().isParseBodyMethod(getMethod()) ) {
success = true;
return;
}
/**
* 根据id更新员工信息
* @param employee 页面传来的员工数据
* @param result 校验结果
* @return 校验信息
*/
@RequestMapping(value = "/emp/{empId}", method = RequestMethod.PUT)
@ResponseBody
public Msg updateEmpById(@Valid Employee employee, BindingResult result) {
if (result.hasErrors()) {
Map<String, Object> map = new HashMap<>();
List<FieldError> errors = result.getFieldErrors();
for (FieldError error : errors) {
System.out.println("错误的字段名:" + error.getField());
System.out.println("错误信息:" + error.getDefaultMessage());
map.put(error.getField(), error.getDefaultMessage());
}
return Msg.fail().add("errorFields", map);
} else {
employeeService.updateEmpById(employee);
return Msg.success();
}
}
6.2、Service层方法
/**
* 根据id更新员工数据
* @param employee 页面传来的员工数据
*/
public void updateEmpById(Employee employee) {
employeeMapper.updateByPrimaryKey(employee);
}
6.3、修改编辑按钮事件
将编辑按钮的id属性传递给更新按钮
// 为修改员工按钮添加事件
// 因为是先绑定事件后在生成的按钮,所以不能用click
// 早先版本的jQuery可以使用live方法
// 这里使用 on 方法绑定
$(document).on("click", ".edit_btn", function () {
// 1. 查询部门信息显示
getDepts("#deptId_update_select")
// 2. 回显员工信息
getEmpById($(this).attr("empId_for_edit"))
// 3. 传递id給更新按钮
$("#updateEmpBtn").attr("empId_for_edit", $(this).attr("empId_for_edit"))
// 4. 跳出模态框
$("#empUpdateModal").modal({
backdrop: "static"
})
})
6.4、使用PUT发送请求
在web.xml设置 HttpPutFormContentFilter
作用:
- 将请求体中的数据解析包装成一个map
- request重新被包装,
request.getParameter()
被重写,就会从自己封装的map中获取数据
<filter>
<filter-name>HttpPutFormContentFilter</filter-name>
<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpPutFormContentFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
七、删除员工
1、控制器方法
/**
* 根据id删除对应的员工数据
* @return 删除是否成功
*/
@RequestMapping(value = "/emp/{id}", method = RequestMethod.DELETE)
@ResponseBody
public Msg deleteEmp(@PathVariable("id")String id) {
String[] ids = id.split("-");
List<Integer> idList = new ArrayList<>();
for (String s : ids) {
idList.add(Integer.valueOf(s));
}
if (idList.size() == 1) {
employeeService.deleteEmpById(idList.get(0));
} else {
employeeService.deleteBatch(idList);
}
return Msg.success();
}
2、Srevice方法
删除单个员工
/**
* 根据id删除员工
* @param id 页面传入的id
*/
public void deleteEmpById(Integer id) {
employeeMapper.deleteByPrimaryKey(id);
}
删除多个员工
/**
* 删除对应列表里id的员工
* @param idList id列表
*/
public void deleteBatch(List<Integer> idList) {
EmployeeExample employeeExample = new EmployeeExample();
EmployeeExample.Criteria criteria = employeeExample.createCriteria();
criteria.andEmpIdIn(idList);
employeeMapper.deleteByExample(employeeExample);
}
3. 添加选择按钮
全选按钮:
<tr>
<th>
<input type="checkbox" id="check_all"/>
</th>
<th>#</th>
<th>empName</th>
<th>gender</th>
<th>email</th>
<th>deptName</th>
<th>操作</th>
</tr>
选项按钮:
$.each(emps, function (index, item) {
var checkBoxTd = $("<td><input type='checkbox' class='check_item'/></td>")
var empIdTd = $("<td></td>").append(item.empId);
var empNameTd = $("<td></td>").append(item.empName)
var genderTd = $("<td></td>").append(item.empGender == "M" ? "男" : "女")
var emailTd = $("<td></td>").append(item.empEmail)
var deptNameTd = $("<td></td>").append(item.department.deptName)
// 注入按钮数据
var editBtn = $("<button></button>").addClass("btn btn-primary btn-sm edit_btn")
.append($("<span></span>").addClass("glyphicon glyphicon-pencil"))
.append("编辑");
// 添加自定义属性,用于修改员工时传递id值
editBtn.attr("empId_for_edit", item.empId)
var delBtn = $("<button></button>").addClass("btn btn-danger btn-sm del_btn")
.append($("<span></span>").addClass("glyphicon glyphicon-trash"))
.append("删除");
var btnTd = $("<td></td>").append(editBtn).append(" ").append(delBtn);
$("<tr></tr>").append(checkBoxTd)
.append(empIdTd)
.append(empNameTd)
.append(genderTd)
.append(emailTd)
.append(deptNameTd)
.append(btnTd)
.appendTo("#emps_table tbody");
});
4、绑定选择按钮事件
全选按钮绑定事件
// 全选按钮绑定事件
$("#check_all").click(function () {
// attr获取checked属性时undefined
// dom原生的属性需要用prop获取,attr获取自定义的属性
$(".check_item").prop("checked", $(this).prop("checked"))
})
选项按钮绑定事件
// 选项按钮绑定事件,当所有按钮被选中时,同时选中全选按钮
$(document).on("click", ".check_item", function () {
var flag = $(".check_item:checked").length === $(".check_item").length
$("#check_all").prop("checked", flag)
})
4、删除单个员工按钮事件
// 单个删除按钮事件
$(document).on("click", ".del_btn", function (){
var empName = $(this).parents("tr").find("td:eq(2)").text()
var empId = $(this).parents("tr").find("td:eq(1)").text()
if(confirm("确认删除【" + empName + "】吗?")) {
// 确认则发送Ajax请求
$.ajax({
url:"${APP_PATH}/emp/" + empId,
type:"DELETE",
success:function (result) {
console.log(result)
// 刷新页面
to_page(currentPage)
}
})
}
})
5、删除全部员工按钮事件
添加删除所有员工按钮id
<button class="btn btn-danger" id="delete_all_emp_btn">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>删除
</button>
绑定按钮事件
// 删除全部选中员工按钮事件
$("#delete_all_emp_btn").click(function () {
var empIds = ""
alert("dqwdfa")
$.each($(".check_item:checked"), function () {
empIds += $(this).parents("tr").find("td:eq(1)").text() + "-"
})
// 删除最后一个 "-"
empIds.substring(0, empIds.length-1)
if(confirm("确认删除所有员工吗?")) {
$.ajax({
url:"${APP_PATH}/emp/" + empIds,
type:"DELETE",
success:function (result) {
console.log(result)
to_page(currentPage)
// 重置全选按钮为未选中状态
$("#check_all").prop("checked", false)
}
})
}
})
九、最终index页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>员工列表</title>
<%
pageContext.setAttribute("APP_PATH", request.getContextPath());
%>
<!-- web路径:
不以/开始的相对路径,找资源,以当前资源的路径为基准,经常容易出问题。
以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:3306);需要加上项目名
http://localhost:3306/crud
-->
<script type="text/javascript" src="${APP_PATH}/static/js/jquery-1.12.4.min.js"></script>
<script src="${APP_PATH }/static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>
<link href="${APP_PATH}/static/bootstrap-3.4.1-dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<%-- 添加员工模态框 --%>
<div class="modal fade" id="empAddModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span>
</button>
<h4 class="modal-title" id="myModalLabel">添加员工</h4>
</div>
<div class="modal-body">
<%-- 表单 --%>
<form class="form-horizontal">
<div class="form-group">
<label for="empName_add_input" class="col-sm-2 control-label">empName</label>
<div class="col-sm-10">
<input name="empName" type="text" class="form-control" id="empName_add_input"
placeholder="empName">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label for="empEmail_add_input" class="col-sm-2 control-label">Email</label>
<div class="col-sm-10">
<input name="empEmail" type="text" class="form-control" id="empEmail_add_input"
placeholder="empEmail">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">empGender</label>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="empGender" id="empGender_add_input" value="M" checked="checked"> 男
</label>
<label class="radio-inline">
<input type="radio" name="empGender" id="empGender_add_input2" value="F"> 女
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">deptId</label>
<div class="col-sm-4">
<select class="form-control" name="deptId" id="deptId_add_select">
</select>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" id="saveEmpBtn">保存</button>
</div>
</div>
</div>
</div>
<%-- 修改员工模态框 --%>
<div class="modal fade" id="empUpdateModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span>
</button>
<h4 class="modal-title" id="updateModalLabel">修改员工</h4>
</div>
<div class="modal-body">
<%-- 表单 --%>
<form class="form-horizontal">
<div class="form-group">
<label for="empName_add_input" class="col-sm-2 control-label">empName</label>
<div class="col-sm-10">
<input name="empName" type="text" class="form-control" id="empName_update_input"
placeholder="empName">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label for="empEmail_add_input" class="col-sm-2 control-label">Email</label>
<div class="col-sm-10">
<input name="empEmail" type="text" class="form-control" id="empEmail_update_input"
placeholder="empEmail">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">empGender</label>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="empGender" id="empGender_update_input" value="M" checked="checked"> 男
</label>
<label class="radio-inline">
<input type="radio" name="empGender" id="empGender_update_input2" value="F"> 女
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">deptId</label>
<div class="col-sm-4">
<select class="form-control" name="deptId" id="deptId_update_select">
</select>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" id="updateEmpBtn">更新</button>
</div>
</div>
</div>
</div>
<%-- 搭建显示页面 --%>
<div class="container">
<%-- 标题 --%>
<div class="row">
<div class="col-md-12">
<h1>SSM_CRUD</h1>
</div>
</div>
<%-- 按钮 --%>
<div class="row">
<div class="col-md-2 col-md-offset-10">
<button class="btn btn-primary" id="emp_add_modal_btn">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>添加
</button>
<button class="btn btn-danger" id="delete_all_emp_btn">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>删除
</button>
</div>
</div>
<%-- 显示表格数据 --%>
<div class="row">
<div class="col-md-12">
<table class="table table-striped" id="emps_table">
<thead>
<tr>
<th>
<input type="checkbox" id="check_all"/>
</th>
<th>#</th>
<th>empName</th>
<th>gender</th>
<th>email</th>
<th>deptName</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%-- 通过函数加载数据 --%>
</tbody>
</table>
</div>
</div>
<%-- 显示分页信息 --%>
<div class="row">
<%--分页条信息--%>
<div class="col-md-6 col-md-offset-3 text-center" id="page_info_area"></div>
</div>
<div class="row">
<%--分页文字信息 --%>
<div class="col-md-6 col-md-offset-3 text-center" id="page_nav_area"></div>
</div>
</div>
<script type="text/javascript">
/* ================================================ 全局变量 ================================================ */
var totalRecord; // 用于跳转到末页
var currentPage // 用于完成请求后刷新页面
/* ================================================ 函数 ================================================ */
// 初始化页面时需要调用的函数
$(function () {
// 去首页
to_page(1);
});
// 页面跳转函数
function to_page(pn) {
$.ajax({
url: "${APP_PATH}/emps",
data: "pn=" + pn,
type: "GET",
success: function (result) {
// 1.解析员工数据
build_emps_table(result);
// 2. 解析并显示分页信息
build_page_info(result);
// 3. 解析显示分页条码
build_page_nav(result);
}
});
}
// 解析表格数据函数
function build_emps_table(result) {
// 清空表格信息
$("#emps_table tbody").empty()
// 获取员工列表
var emps = result.extend.pageInfo.list;
// 遍历员工列表,构建表格
$.each(emps, function (index, item) {
var checkBoxTd = $("<td><input type='checkbox' class='check_item'/></td>")
var empIdTd = $("<td></td>").append(item.empId);
var empNameTd = $("<td></td>").append(item.empName)
var genderTd = $("<td></td>").append(item.empGender == "M" ? "男" : "女")
var emailTd = $("<td></td>").append(item.empEmail)
var deptNameTd = $("<td></td>").append(item.department.deptName)
// 注入按钮数据
var editBtn = $("<button></button>").addClass("btn btn-primary btn-sm edit_btn")
.append($("<span></span>").addClass("glyphicon glyphicon-pencil"))
.append("编辑");
// 添加自定义属性,用于修改员工时传递id值
editBtn.attr("empId_for_edit", item.empId)
var delBtn = $("<button></button>").addClass("btn btn-danger btn-sm del_btn")
.append($("<span></span>").addClass("glyphicon glyphicon-trash"))
.append("删除");
var btnTd = $("<td></td>").append(editBtn).append(" ").append(delBtn);
$("<tr></tr>").append(checkBoxTd)
.append(empIdTd)
.append(empNameTd)
.append(genderTd)
.append(emailTd)
.append(deptNameTd)
.append(btnTd)
.appendTo("#emps_table tbody");
});
}
// 解析显示分页信息函数
function build_page_info(result) {
// 清空原来的分页信息
$("#page_info_area").empty()
$("#page_info_area").append("当前页:" +
result.extend.pageInfo.pageNum + ",总页数:" +
result.extend.pageInfo.pages + ",总记录数:" +
result.extend.pageInfo.total)
totalRecord = result.extend.pageInfo.total
currentPage = result.extend.pageInfo.pageNum
}
// 解析显示分页条函数
function build_page_nav(result) {
// 清空原来的分页码
$("#page_nav_area").empty();
// 构建元素
var ul = $("<ul></ul>").addClass("pagination")
var firstPageLi = $("<li></li>").append($("<a></a>").append("首页").attr("href", "#"));
var prePageLi = $("<li></li>").append($("<a></a>").append("«").attr("href", "#"));
var nextPageLi = $("<li></li>").append($("<a></a>").append("»").attr("href", "#"));
var lastPageLi = $("<li></li>").append($("<a></a>").append("末页").attr("href", "#"));
// 添加首页、末页、上一页、下一页禁用 和跳转功能
if (result.extend.pageInfo.hasPreviousPage == false) {
firstPageLi.addClass("disabled")
prePageLi.addClass("disabled")
} else {
firstPageLi.click(function () {
to_page(1)
})
prePageLi.click(function () {
to_page(result.extend.pageInfo.pageNum - 1)
})
}
if (result.extend.pageInfo.hasNextPage == false) {
nextPageLi.addClass("disabled");
lastPageLi.addClass("disabled");
} else {
lastPageLi.click(function () {
to_page(result.extend.pageInfo.pages);
});
nextPageLi.click(function () {
to_page(result.extend.pageInfo.pageNum + 1);
});
}
// 添加首页和前一页
ul.append(firstPageLi).append(prePageLi)
// 构建条码
$.each(result.extend.pageInfo.navigatepageNums, function (index, num) {
var numLi = $("<li></li>").append($("<a></a>").append(num).attr("href", "#"));
// 设置当前页码高亮显示
if (result.extend.pageInfo.pageNum == num) {
numLi.addClass("active")
}
// 添加按钮跳转事件
numLi.click(function () {
to_page(num)
})
ul.append(numLi)
});
// 添加末页和后一页
ul.append(nextPageLi).append(lastPageLi)
var navEle = $("<nav></nav>").append(ul)
navEle.appendTo("#page_nav_area");
}
// 查询所有部门信息请求函数
function getDepts(ele) {
$.ajax({
url: "${APP_PATH}/depts",
type: "GET",
success: function (result) {
console.log(result)
$.each(result.extend.depts, function () {
var optionElement = $("<option></option>").append(this.deptName).attr("value", this.deptId)
optionElement.appendTo(ele)
})
}
})
}
// 添加员工表单数据校验信息显示函数
function validate_add_from_information_show(ele, status, msg) {
// 清楚当前元素的校验状态
$(ele).parent().removeClass("has-success has-error")
$(ele).next("span").text("")
if("success" == status){
$(ele).parent().addClass("has-success")
$(ele).next("span").text(msg)
} else if ("error" == status) {
$(ele).parent().addClass("has-error")
$(ele).next("span").text(msg)
}
}
// 重置添加员工表单数据和样式
function reset_form(ele) {
// 清空表单数据
$(ele + " form")[0].reset()
// 清空表单样式
$(ele + " form").find("*").removeClass("has-success has-error")
$(ele + " form").find(".help-block").text("")
// 清空下拉列表
$(ele + " select").empty()
}
// 根据id查询员工信息函数
function getEmpById(id) {
$.ajax({
url:"${APP_PATH}/emp/" + id,
type:"GET",
success:function (result) {
var empData = result.extend.emp;
console.log(result)
$("#empName_update_input").val(empData.empName);
$("#empEmail_update_input").val(empData.empEmail);
$("#empUpdateModal input[name=empGender]").val(empData.empGender);
$("#empUpdateModal select").val(empData.deptId);
}
})
}
// 校验用户名函数
function verifyUsername(ele) {
var empName = $(ele).val();
var regName = /(^[a-zA-Z0-9_-]{3,16}$)|(^[\u2E80-\u9FFF]{2,5})/ // 允许英文和中文
if(!regName.test(empName)){
validate_add_from_information_show(ele, "error", "员工名称格式不正确")
return false
} else {
validate_add_from_information_show(ele, "success", "")
return true
}
}
// 校验邮箱格式函数
function checkMailbox(ele) {
var empEmail = $(ele).val()
var regEmail = /^[a-z\d]+(\.[a-z\d]+)*@([\da-z](-[\da-z])?)+(\.{1,2}[a-z]+)+$/
if(!regEmail.test(empEmail)){
validate_add_from_information_show(ele, "error", "邮箱格式不正确")
return false
}
var isTheEmailAddressCorrect = false
// 发送Ajax请求判断邮箱是否重复
$.ajax({
url:"${APP_PATH}/checkEmail",
data:"empEmail=" + empEmail,
type:"GET", // get请求不检验中文,如果发送对象中含中文则用POST
async:false, // 设置Ajax为同步
success:function (result) {
console.log(result)
if(result.code === 100 && regEmail.test(empEmail)) {
validate_add_from_information_show(ele, "success", "")
isTheEmailAddressCorrect = true
} else if (result.code === 200) {
validate_add_from_information_show(ele, "error", "邮箱已存在")
isTheEmailAddressCorrect = false
}
}
})
return isTheEmailAddressCorrect;
}
/* ============================================== 按钮绑定事件区 ============================================== */
// 添加员工按钮事件
$("#emp_add_modal_btn").click(function () {
// 表单重置数据
reset_form("#empAddModal")
// 发送Ajax请求, 查出部门信息,显示在下拉列表中
getDepts("#deptId_add_select")
$("#empAddModal").modal({
backdrop: "static"
})
})
// 模态框保存按钮点击事件
$("#saveEmpBtn").click(function () {
// 校验表单数据
if (!verifyUsername("#empName_add_input") || !checkMailbox("#empEmail_add_input")) {
return false
}
// 发送保存请求
$.ajax({
url: "${APP_PATH}/emp",
type: "POST",
data: $("#empAddModal form").serialize(),
success: function (result) {
console.log(result)
if(result.code === 100) {
// 员工保存成功
// 1. 关闭模态框
$("#empAddModal").modal('hide')
// 2. 跳转到最后一页显示
to_page(totalRecord)
} else {
// 显示失败信息
if(undefined !== result.extend.errorFields.empEmail){
validate_add_from_information_show("#empEmail_add_input", "error", "邮箱格式不正确")
}
if(undefined !== result.extend.errorFields.empName){
validate_add_from_information_show("#empName_add_input", "error", "员工名称格式不正确")
}
}
}
})
})
// 为修改员工按钮添加事件
// 因为是先绑定事件后在生成的按钮,所以不能用click
// 早先版本的jQuery可以使用live方法
// 这里使用 on 方法绑定
$(document).on("click", ".edit_btn", function () {
// 1. 查询部门信息显示
getDepts("#deptId_update_select")
// 2. 回显员工信息
getEmpById($(this).attr("empId_for_edit"))
// 3. 传递id給更新按钮
$("#updateEmpBtn").attr("empId_for_edit", $(this).attr("empId_for_edit"))
// 4. 跳出模态框
$("#empUpdateModal").modal({
backdrop: "static"
})
})
// 绑定更新按钮单击事件
$("#updateEmpBtn").click(function () {
// 1. 校验表单数据
if (!verifyUsername("#empName_update_input") || !checkMailbox("#empEmail_update_input")) {
return false
}
// 2. 发送更新的Ajax请求
$.ajax({
url:"${APP_PATH}/emp/" + $(this).attr("empId_for_edit"),
type:"PUT",
data:$("#empUpdateModal form").serialize(),
success:function (result) {
console.log(result)
if(result.code === 100) {
// 员工更新成功
// 1. 关闭模态框
$("#empUpdateModal").modal('hide')
// 2. 刷新页面
to_page(currentPage)
} else {
// 显示失败信息
if(undefined !== result.extend.errorFields.empEmail){
validate_add_from_information_show("#empEmail_update_input", "error", "邮箱格式不正确")
}
if(undefined !== result.extend.errorFields.empName){
validate_add_from_information_show("#empName_update_input", "error", "员工名称格式不正确")
}
}
}
})
})
// 单个删除按钮事件
$(document).on("click", ".del_btn", function (){
var empName = $(this).parents("tr").find("td:eq(2)").text()
var empId = $(this).parents("tr").find("td:eq(1)").text()
if(confirm("确认删除【" + empName + "】吗?")) {
// 确认则发送Ajax请求
$.ajax({
url:"${APP_PATH}/emp/" + empId,
type:"DELETE",
success:function (result) {
console.log(result)
// 刷新页面
to_page(currentPage)
}
})
}
})
// 全选按钮绑定事件
$("#check_all").click(function () {
// attr获取checked属性时undefined
// dom原生的属性需要用prop获取,attr获取自定义的属性
$(".check_item").prop("checked", $(this).prop("checked"))
})
// 选项按钮绑定事件,当所有按钮被选中时,同时选中全选按钮
$(document).on("click", ".check_item", function () {
var flag = $(".check_item:checked").length === $(".check_item").length
$("#check_all").prop("checked", flag)
})
// 删除全部选中员工按钮事件
$("#delete_all_emp_btn").click(function () {
var empIds = ""
$.each($(".check_item:checked"), function () {
empIds += $(this).parents("tr").find("td:eq(1)").text() + "-"
})
// 删除最后一个 "-"
empIds.substring(0, empIds.length-1)
if(confirm("确认删除所有员工吗?")) {
$.ajax({
url:"${APP_PATH}/emp/" + empIds,
type:"DELETE",
success:function (result) {
console.log(result)
to_page(currentPage)
// 重置全选按钮为未选中状态
$("#check_all").prop("checked", false)
}
})
}
})
</script>
</body>
</html>