尚硅谷ssm整合实战项目笔记

文章目录

前言

该笔记为尚硅谷SSM实战演练丨ssm整合快速开发CRUD学习笔记

github项目地址:https://github.com/xjhqre/sgg-ssm-demo

一、SSM整合

1、项目介绍

使用SSM框架搭建出一套简单的CRUD项目示例,包括分页查询、Ajax请求、数据校验等。

img

img

2、功能点

  1. 分页查询
  2. 数据校验:JQuery前端校验+JSR-303后端校验
  3. Ajax请求
  4. REST风格的URI:GET查询、POST新增、DELETE删除、PUT修改

3、技术点

  • 基础框架-SSM(Spring+SpringMVC+Mybatis)
  • 数据库-MySQL
  • 前端框架-Bootstrap
  • 依赖管理-Maven
  • 分页查询-PageHelper
  • 逆向工程-Mybatis Generator

二、基础环境搭建

相关配置文件的创建请见SSM整合配置模板,这里主要写下不同的地方。

1、创建maven工程

步骤:

  1. 创建maven工程快速开始
  2. 修改pom.xml文件,打包方式为war
  3. 创建resources目录
  4. 删除不必要的类和目录
  5. 创建web.xml文件

image-20220209154453205

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

步骤:

  1. 在webapp文件夹下创建static目录存放bootstrap文件和jQuery文件
  2. 创建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

步骤:

  1. 启动Spring容器,设置 contextConfigLocationContextLoaderListenercontextConfigLocation设置spring配置文件的位置,在resources目录下创建applicationContext.xml文件
  2. 配置springMVC前端控制器dispatchServlet,拦截所有请求,需要指定springMVC配置文件的位置,若不指定,则需要在同级目录下(webapp目录下)创建一个 servlet-name + -servlet的一个xml文件。让dispatchServlet拦截所有请求:/
  3. 配置字符编码过滤器 CharacterEncodingFilter,初始化参数 encoding、forceRequestEncoding、forceResponseEncoding,设置过滤所有请求 /*,一定要放在所有过滤器之前
  4. 使用Rest风格的URI,配置 HiddenHttpMethodFilter,将指定的 post 转化为 delete 或者是 put 请求。过滤所有请求/*

image-20220209163754148

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配置

步骤:

  1. 配置扫描控制器
  2. 配置视图解析器
  3. 启动Servlet默认处理器,处理springMVC不能处理的请求
  4. 开启MVC注解驱动

image-20220209164633621

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

步骤:

  1. 配置数据源,创建db.properties文件
  2. 配置和mybatis的整合
    1. 指定mybatis的核心配置文件
    2. 指定mybatis的mapper文件
    3. 配置扫描器,将mybatis接口的实现加入到ioc容器中,扫描所有dao接口的实现,加入到ioc容器中
    4. 配置一个可以执行批量的sqlSession,加入到IOC容器里
  3. 事务控制的配置,导入配置好的数据源id
    1. 配置切入点表达式
    2. 配置事务增强
    3. 配置事务如何切入

image-20220209175403205

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

步骤:

  1. 导入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>
  1. 在当前工程目录下创建 mbg.xml 文件(与pom.xml同级目录)

  2. 配置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&amp;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>
  1. 编写一个测试类运行逆向工程生成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映射文件基础上添加新功能,查询员工时带上他的部门信息

步骤:

  1. EmployeeMapper接口上新添加两个方法:
List<Employee> selectByExampleWithDept(EmployeeExample example);

Employee selectByPrimaryKeyWithDept(Integer empId);
  1. Employee员工类上新增属性private Department department;,并添加 get和set方法

  2. 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>
  1. 编写 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>
  1. 编写 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>
  1. 编写 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));
        }
    }
}

三、查询功能实现

步骤:

  1. 访问 index.jsp页面
  2. index.jsp 页面发送出查询员工列表请求
  3. EmployeeController来接收请求,查出员工数据
  4. 来到 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分页插件

  1. 在pom.xml文件里引入PageHelper依赖
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.2</version>
</dependency>
  1. 在 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 依赖包,不引入无法使用pageContextsetAttribute方法

<!-- 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页面:

  1. pageInfo 中取出取出员工信息显示
  2. 完善分页条
<%@ 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">&laquo;</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">&raquo;</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>

四、改写查询功能

步骤:

  1. index.jsp 页面直接发送 Ajax 请求进行员工分页数据的查询
  2. 服务器将查出的数据,以 JSON 字符串的形式返回给浏览器
  3. 浏览器收到 js 字符串。可以使用 js 对 json 进行解析,使用 js 通过 dom 增删改查改变页面
  4. 返回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("&laquo;").attr("href", "#"));
        var nextPageLi = $("<li></li>").append($("<a></a>").append("&raquo;").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>

五、编写添加功能

步骤:

  1. 在index.jsp页面点击“新增”按钮
  2. 弹出添加模态框
  3. 从数据库中查询出所有部门名称显示在下拉列表中
  4. 用户输入数据
  5. 校验用户名、邮箱格式是否正确、用户名是否重复
  6. 点击“保存”按钮添加数据

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">&times;</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("&laquo;").attr("href", "#"));
        var nextPageLi = $("<li></li>").append($("<a></a>").append("&raquo;").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. 点击编辑
  2. 弹出用户修改的模态框(显示用户信息)
  3. 点击更新,完成用户修改

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">&times;</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,而回显的是部门名称的原因:

  1. val() 方法返回或设置被选元素的值。元素的值是通过 value 属性设置的。该方法大多用于 input 元素。
  2. 查询部门信息时,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、更新按钮事件

步骤:

  1. 绑定更新按钮事件
  2. 前端校验用户名和邮箱格式
  3. 将编辑按钮的id属性传递给更新按钮
  4. 前端发送Ajax请求更新员工数据
  5. 编写更新员工控制器
  6. 编写更新员工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

作用:

  1. 将请求体中的数据解析包装成一个map
  2. 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">&times;</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">&times;</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("&laquo;").attr("href", "#"));
        var nextPageLi = $("<li></li>").append($("<a></a>").append("&raquo;").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>
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值