自学尚硅谷java视频-整合SSM框架实现简单CRUD操作(IDEA版)

文章目录


1.主要功能点:

分页

数据校验(前端Jquery+后端JSR303)

ajax异步请求

Rest风格URL;使用http协议请求方式,表示对资源的操作
GET(查询)
POST(添加)
PUT(修改)
DELETE(删除)

2.技术点:

JDK:1.8

基础框架:Spring+SpringMVC+mybatis

前端页面框架:bootstrap

服务器:tomcat8

数据库:Mysql

数据库连接池:druid

依赖管理:Maven

mybatis分页插件:pagehelper


一、基础环境搭建

1. 1.创建maven的web工程

在这里插入图片描述

1. 2 引入项目依赖的jar包

在pom.xml中的properties配置中统一管理相关版本

<spring.version>5.2.6.RELEASE</spring.version>
    <mybatis.version>3.4.6</mybatis.version>
    <mysql.version>5.1.46</mysql.version>

spring与springmvc

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
  <version>${spring.version}</version>
</dependency>

mybaits及与spring整合包

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>${mybatis.version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.2</version>
    </dependency>

数据库连接池以及数据库驱动

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.1.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>${mysql.version}</version>
</dependency>

其他(jstl,jsp,servlet-api,junit,AOP相应操作)

    <!-- 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>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.2</version>
      <scope>provided</scope>
    </dependency>
     <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/cglib/cglib -->
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>2.2.2</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
   

1.3 引入bootstrap前端框架

在这里插入图片描述
在jsp页面中引入:

<%--    引入jquery--%>
    <link href="./static/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet">
    <script type="text/javascript" src="/static/js/jquery-1.7.2.js"></script>
<%--    引入样式--%>
    <script src="./static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>

1.4 编写整合的配置文件

1.4.1 web.xml

  <!--  启动Spring的容器-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
<!--  解决中文乱码的过滤器,一定要放在所有过滤器之前-->
  <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>
  </filter>
<!--  使用Rest风格的URL,将页面普通的POST请求转为指定的delete或者put请求-->
  <filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>//*</url-pattern>
  </filter-mapping>
<filter-mapping>
  <filter-name>hiddenHttpMethodFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
<!--  配置springMVC的前端控制器-->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--    配置初始参数,服务器一启动就加载springmvc的配置-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

1.4.2 springmvc.xml(包含网站页面的跳转逻辑控制配置)

<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
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.js" use-default-filters="false">
<!--        只扫描控制器-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

<!--    配置视图解析器,方便页面返回解析-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

<!--    开启mvc注解扫描以及servlet-handler-->
<!--    将springmvc不能处理的请求交给tomcat-->
    <mvc:default-servlet-handler></mvc:default-servlet-handler>
<!--    支持mvc更高级的一些功能,JSR303校验,ajax请求以及映射动态请求-->
    <mvc:annotation-driven></mvc:annotation-driven>
</beans>

1.4.3 application.xml(Spring相关配置)

创建德鲁伊连接池的配置文件:
在这里插入图片描述
配置spring文件:

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
        ">

<!--    Spring的配置文件,主要配置和业务逻辑相关的-->
<context:component-scan base-package="com.js">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--引入数据源-->
    <context:property-placeholder location="classpath:dbdruid.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driver" value="${driver}"></property>
    <property name="url" value="${url}"></property>
    <property name="username" value="${username}"></property>
    <property name="password" value="${password}"></property>
</bean>

<!--    与mybatis整合,可以生成sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="configLocation" value="mybatis-config"></property>
    <property name="dataSource" ref="dataSource"></property>
<!--    指定mybatis的mapper文件位置-->
    <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
</bean>
<!--    配置扫描器,将mybatis接口的实现加入到IOC容器中-->
    <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--        扫描所有DAO接口的实现,加入IOC容器-->
        <property name="basePackage" value="com.js.dao"></property>
    </bean>

<!--    事务管理控制配置,创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--        指定需要控制的数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
<!--    开启事务注解驱动-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>

1.4.4 mybatis的基本配置文件(参考官方文档)

二、创建基础bean,DAO(mapper)增删改查逻辑及测试具体步骤

2.1创建所需要的表(mysql)

CREATE DATABASE ssm
USE smm

CREATE TABLE emp(
emp_id INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
emp_name VARCHAR(200) NOT NULL,
gender CHAR(1),
email VARCHAR(200)
)
CREATE TABLE dept(
dept_id INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
dept_name VARCHAR(200) NOT NULL
)
#添加列
ALTER TABLE emp
ADD d_id INT(11)
#添加外键约束
ALTER TABLE emp
ADD CONSTRAINT fk_emp_dept FOREIGN KEY(d_id) REFERENCES dept(dept_id)

2.2使用mybatis逆向工程生成对应的bean及mapper(也可自己手动根据表进行书写相应bean及mapper)

2.2.1 引入mybatis Generator core.jar包

<!-- 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>

2.2.2 书写相应的xml配置文件,参考mybatis Generator官方文档

<?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">
        <!--        数据库连接配置-->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/ssm"
                        userId="root"
                        password="123456">
        </jdbcConnection>

        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>
        <!--指定javabean生成的位置-->
        <javaModelGenerator
                targetPackage="com.js.bean"
                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接口生成的位置-->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.js.dao"
                             targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>
        <!--指定每个表的生成策略-->
        <table tableName="emp" domainObjectName="Employee"></table>
        <table tableName="dept" domainObjectName="Department"></table>
    </context>
</generatorConfiguration>

2.2.3 利用官方java代码运行generator

public class MBGTest {
    public static void main(String[] args) throws IOException, XMLParserException, InvalidConfigurationException, SQLException, InterruptedException {
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        File configFile = new File("E:\\workspace_idea1\\SSMproject\\ssm_crud\\src\\main\\resources\\mbg.xml");
        System.out.println(configFile.getAbsolutePath());
        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);
    }
}

为防止找不到XML文件,加载文件时使用了绝对路径。
在这里插入图片描述运行完成将生成对应的bean,mapper接口以及相应的mapper.xml文件
mapper接口提供了增删改查的方法
mapper.xml文件中则包含了基本的增删改查操作。

2.3 根据业务逻辑修改自动生成的mapper中的文件

新增查询员工时能同时查到其对应部门的信息,步骤:

在员工接口中新增方法

 List<Employee> selectByExampleWithDept(EmployeeExample example);
 Employee selectByPrimaryKeyWithDept(Integer empId);

在员工类中添加部门类的属性,表示员工属于的部门信息,并生成对应get与set方法

 private Department department;

在员工mapper.xml中添加新增方法的配置

<!--新增方法
    List<Employee> selectByExampleWithDept(EmployeeExample example);
    Employee selectByPrimaryKeyWithDept(Integer empId);-->

  <select id="selectByExampleWithDept" resultMap="WithDeptResultMap">
    select
    <if test="distinct">
      distinct
    </if>
    <include refid="WithDept_Column_List" />
    from emp left join dept on emp.d_id=dept.dept_id
    <if test="_parameter != null">
      <include refid="Example_Where_Clause" />
    </if>
    <if test="orderByClause != null">
      order by ${orderByClause}
    </if>
  </select>

<select id="selectByPrimaryKeyWithDept" resultMap="WithDeptResultMap">
  select
  <include refid="WithDept_Column_List" />
  from emp left join dept on emp.d_id=dept.dept_id
  where emp_id = #{empId,jdbcType=INTEGER}
</select>

新增方法中,列不再使用基本列,使用了带部门信息的新增列。因此要新增列的sql定义:

<!--  新增sql-->
  <sql id="WithDept_Column_List">
    emp_id, emp_name, gender, email, d_id,dept_id,dept_name
  </sql>

返回的resultMap也与基本定义的不一致,因此要新增resultMap定义:

<resultMap id="WithDeptResultMap" type="com.js.bean.Employee">
    <id column="emp_id" jdbcType="INTEGER" property="empId" />
    <result column="emp_name" jdbcType="VARCHAR" property="empName" />
    <result column="gender" jdbcType="CHAR" property="gender" />
    <result column="email" jdbcType="VARCHAR" property="email" />
    <result column="d_id" jdbcType="INTEGER" property="dId" />
<!--    指定联合查询的部门字段的封装-->
    <association property="department" javaType="com.js.bean.Department">
      <id column="dept_id" property="deptId"/>
      <id column="dept_name" property="deptName"/>
    </association>
  </resultMap>

2.4 写业务层前,测试数据库层mapper

2.4.1以前的测试方式

public class DepartmentMapperTest {
    @Test
    public void testCRUD(){
        //1.创建Spring容器
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.从容器中取得mapper
        DepartmentMapper mapper = context.getBean(DepartmentMapper.class);
        //3.利用mapper操作
    }
}

2.4.2 使用Spring单元测试,可以自动注入需要的组件

导入需要的依赖:

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
      <scope>test</scope>
    </dependency>

测试实例:

/**
 ContextConfiguration:指定Spring配置文件的位置
 直接autowired我们要使用的组件即可
*/
@ContextConfiguration(locations = {"classpath*:applicationContext.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class DepartmentMapperTest {
    @Autowired
    DepartmentMapper departmentMapper;
    @Test
    public void testCRUD(){
        System.out.println(departmentMapper);
    }
}

结果:拿到了DepartmentMapper接口的代理对象
在这里插入图片描述

同理可创建员工测试MapperTest,拿到员工代理对象。

插入测试
departmentMapper.insertSelective(new Department(null,"开发部"));
departmentMapper.insertSelective(new Department(null,"测试部"));

结果:
在这里插入图片描述
※注:进行批量插入(员工):使用可以执行批量操作的sqlSession
配置一个可以执行批量的sqlSession:在Spring配置中配置

<!--    配置可以执行批量的sqlSession,没有无参构造器,BATCH可以积攒sql语句-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
    <constructor-arg name="executorType" value="BATCH"></constructor-arg>
</bean>

配置完成后,便可以在员工Mapper测试类进行自动注入:

@ContextConfiguration(locations = {"classpath*:applicationContext.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class EmployeeMapperTest {
    @Autowired
    EmployeeMapper employeeMapper;
    @Autowired
    SqlSession sqlSession;

批量插入:

 @Test
    public void TestCRUD(){
        System.out.println(employeeMapper);
        employeeMapper.insert(new Employee(null,"Tom","M","Tom@qq.com",1));
//        实现批量插入,使用可以执行批量操作的sqlSession
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        for (int i=0;i<100;i++){
            //使用UUID拼接员工姓名
            String uid = UUID.randomUUID().toString().substring(0, 5)+i;
            mapper.insert(new Employee(null,uid,"M",uid+"@qq.com",1));
        }
    }

三、结合web服务器实现页面,包括了CRUD操作的页面展示

3.1 查询

3.1.1访问index.jsp页面

	我们可以规定查询的url为:/emps
	那么在index.jsp中:规定跳转页面
<jsp:forward page="/emps"></jsp:forward>

这会在控制器中查找对应路径的方法进行执行。

3.1.2 index页面发送查询员工列表的http请求

3.1.3EmployeeController控制器接受请求,查出员工数据

创建EmployeeController控制器类,处理员工增删改查处理,并创建相应的service类,利用service对象调用方法进行查询。而在service层中,必然会调用DAO,在service对象中进行DAO的注入。

Service对象创建:

@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;

    //查询所有员工,没有查询条件,因此参数为null
    public List<Employee> getAll() {
        return employeeMapper.selectByExampleWithDept(null);
    }
}

Controller对象创建:

@Controller
public class EmployeeController {
    @Autowired
    EmployeeService employeeService;
    /**
     * @param
     * @return java.lang.String
     * @methodname getEmps
     * @Description 查询员工数据
     * @Date 2020/11/12 21:32
     **/
    //方法上的RequestMapping注解的path表示了访问的路径
    @RequestMapping(path = "/emps")
    public String getEmps() {
        List<Employee> list=employeeService.getAll();
        return "list";
    }
}

**注:****这是未加分页的情况,查询的数据可能非常多。因此要进行分页处理

*****进行分页处理

请求参数中加入整形数据pn表示页面,不传表示默认值。在控制器方法中获取参数,在查询时候加入limit限制。(具体通过引入mybatis的pagehelper插件进行)。
①引入依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.10</version>
  </dependency>

②在mybatis全局配置中注册插件

<plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>

③在service中查询方法进行调用,并利用Model添加结果,可以带给页面

@Controller
public class EmployeeController {
    @Autowired
    EmployeeService employeeService;
    /**
     * @param
     * @return java.lang.String
     * @methodname getEmps
     * @Description 查询员工数据
     * @Date 2020/11/12 21:32
     **/
    //方法上的RequestMapping注解的path表示了访问的路径
    @RequestMapping(path = "/emps")
    public String getEmps(@RequestParam(value = "pn",defaultValue = "1") Integer pn, Model model) {
        //引入PageHelper分页插件,在查询之前只需要调用,传入页码及每页数据大小
        PageHelper.startPage(pn,5);
        //startPage后面紧跟的查询就是一个分页查询
        List<Employee> list=employeeService.getAll();
        //使用PageInfo类对查询结果进行包装,4表示页面上要连续显示几页
        // pageInfo包装后,数据个数,页码数等信息都能找到,对于页面展示很有帮助
        PageInfo page=new PageInfo(list,4);
        model.addAttribute("pageInfo",page);
        return "list";
    }
}
*****编写MockMvc测试类虚拟模拟mvc请求进行测试,获取处理结果(@ContextConfiguration的加载文件需要加载spring和springmvc的配置文件)

注:@Before引入的是junit下的:import org.junit.Before;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {"classpath*:applicationContext.xml","classpath*:springmvc.xml"})
public class MCVTest {
    //传入SpringMvc的IOC
    @Autowired
    WebApplicationContext context;
    //虚拟的mvc请求,获取处理结果
    MockMvc mockMvc;
    @Before
    public void initMockMvc(){
        mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
    }

    @Test
    public void testPage() throws Exception {
        //模拟请求拿到返回值,MockMvcRequestBuilders的后面可以是get,post,put,delete等请求
        MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/emps").param("pn", "1")).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 num : nums) {
            System.out.print(" "+num);
        }
        List<Employee> list = pageInfo.getList();
        for (Employee employee : list) {
            System.out.println("员工ID:"+employee.getEmpId());
        }
    }
}

成功获取结果:
在这里插入图片描述

3.1.4跳转到list.jsp页面进行展示

在list页面中取出数据,进行显示,进行与首页相同的CSS样式,JS引入,基于Bootstrap搭建页面。

基本页面框架搭建:

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%--引入标签库--%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <title>员工列表</title>
    <%
        pageContext.setAttribute("APP_PATH", request.getContextPath());
    %>
    <%--    引入jquery--%>
    <link href="./static/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet">
    <script type="text/javascript" src="${APP_PATH}/static/js/jquery-1.7.2.js"></script>
    <%--    引入样式--%>
    <script src="${APP_PATH}/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></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-4 col-md-offset-8">
            <button type="button" class="btn btn-info">新增</button>
            <button type="button" class="btn btn-danger">删除</button>
        </div>
    </div>
    <%--    显示表格--%>
    <div class="row">
        <div class="col-md-12">
            <table class="table table-hover">
                <tr>
                    <th>empId</th>
                    <th>empName</th>
                    <th>gender</th>
                    <th>email</th>
                    <th>deptName</th>
                    <th>操作</th>
                </tr>
                <c:forEach items="${pageInfo.list}" var="emp">
                    <tr>
                        <td>${emp.empId}</td>
                        <td>${emp.empName}</td>
                        <td>${emp.gender}</td>
                        <td>${emp.email}</td>
                        <td>${emp.department.deptName}</td>
                        <td>
                            <button type="button" class="btn btn-info btn-sm">
                                <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
                                编辑
                            </button>
                            <button type="button" class="btn btn-danger btn-sm">
                                <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
                                删除
                            </button>
                        </td>
                    </tr>
                </c:forEach>
            </table>
        </div>
    </div>
    <%--    显示分页--%>
    <div class="row">
        <%--        分页文字信息--%>
        <div class="col-md-6">
            当前页面:${pageInfo.pageNum} 总页码:${pageInfo.pages} 共${pageInfo.total}条记录
        </div>
        <%--        分页条信息--%>
        <div class="col-md-6">
            <nav aria-label="Page navigation">
                <ul class="pagination">
                    <li><a href="${APP_PATH}/emps">首页</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="page_Num">
                        <c:if test="${page_Num==pageInfo.pageNum}">
                            <li class="active"><a href="#">${page_Num}</a></li>
                        </c:if>
                        <c:if test="${page_Num!=pageInfo.pageNum}">
                            <li><a href="${APP_PATH}/emps?pn=${page_Num}">${page_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>
</body>
</html>

页面效果展示:
在这里插入图片描述

3.1.5优化改进查询:服务器端以json格式发送信息发送给客户端,由客户端解析json获取数据,不再受限于浏览器,也可由安卓,IOS进行访问(使用ajax)。

步骤:

1.index,jsp页面直接发送ajax请求进行员工分页查询
2.服务器端查出数据,以json形式返回给浏览器
3.浏览器收到json字符串,使用js对json解析,使用js通过dom增删改改变页面

导入依赖jar包:处理json

<dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.11.2</version>
    </dependency>

bean中书写一个通用的操作通知信息的类Msg:可用于通知操作成功与失败

public class Msg {
    //表示状态码
    private int code;
    //提示信息
    private String msg;
    //用户要返回给浏览器的信息,封装在map中
    private Map<String,Object> extend=new HashMap<String,Object>();

    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;
    }
}

重新提供Controller中的方法:不再需要以Model类储存

    @RequestMapping("/emps")
    @ResponseBody
    public Msg getEmpsWithjson(@RequestParam(value = "pn",defaultValue = "1") Integer pn){
        //引入PageHelper分页插件,在查询之前只需要调用,传入页码及每页数据大小
        PageHelper.startPage(pn,5);
        //startPage后面紧跟的查询就是一个分页查询
        List<Employee> list=employeeService.getAll();
        //使用PageInfo类对查询结果进行包装,4表示页面上要连续显示几页
        // pageInfo包装后,数据个数,页码数等信息都能找到,对于页面展示很有帮助
        PageInfo page=new PageInfo(list,4);
        return Msg.success().add("PageInfo",page);
    }
}

重新书写index页面进行显示:利用ajax发送请求:

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%--引入标签库--%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <title>员工列表</title>
    <%
        pageContext.setAttribute("APP_PATH", request.getContextPath());
    %>
    <%--    引入jquery--%>
    <link href="./static/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet">
    <script type="text/javascript" src="${APP_PATH}/static/js/jquery-1.7.2.js"></script>
    <%--    引入样式--%>
    <script src="${APP_PATH}/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></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-4 col-md-offset-8">
            <button type="button" class="btn btn-info">新增</button>
            <button type="button" class="btn btn-danger">删除</button>
        </div>
    </div>
    <%--    显示表格--%>
    <div class="row">
        <div class="col-md-12">
            <table class="table table-hover" id="emps_table">
                <thead>
                <tr>
                    <th>empId</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" id="page_info_area"></div>
        <%--        分页条信息--%>
        <div class="col-md-6" id="page_nav_area"></div>
    </div>
</div>
<%--页面加载完成以后,直接去发送ajax请求,要到分页数据--%>
<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) {
                // console.log(result);
                // 1.解析并显示员工数据
                build_emps_table(result);
                // 2.解析并显示分页信息
                bulid_page_info(result);
                //解析显示分页条信息
                build_page_nav(result);
                //跳转页面
            }
        });
    }

    function build_emps_table(result) {
        //清空table表
        $("#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.gender=="M"?"男":"女");
            var emailTd=$("<td></td>").append(item.email);
            var deptNameTd=$("<td></td>").append(item.department.deptName);
            var editBtn=$("<button></button>").addClass("btn btn-info 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);
                // 使用append进行添加
            $("<tr></tr>").append(empIdTd).append(empNameTd).append(genderTd).append(emailTd)
                .append(deptNameTd).append(BtnTd).appendTo("#emps_table tbody");
        });
    }
    // 解析显示分页信息
    function bulid_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","#"));
        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);
        })
        }
        ul.append(firstPageLi).append(prePageLi);
        $.each(result.extend.pageInfo.navigatepageNums,function (index,item) {

            var numLi=$("<li></li>").append($("<a></a>").append(item).attr("href","#"));
            if (result.extend.pageInfo.pageNum==item){
                numLi.addClass("active");
            }
            numLi.click(function () {
                to_page(item);
            })
            ul.append(numLi);
        })
        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.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(nextPageLi).append(lastPageLi);
        var navEle=$("<nav></nav>").append(ul);
        $("#page_nav_area").append(navEle);
    }

</script>
</body>
</html>


3.2 新增(添加员工)

1.index,jsp页面点击新增
2.弹出新增对话框(即bootstrap的js模态框)
3.查询数据库部门列表,显示在对话框中
4.用户输出数据完成保存
注:REST风格,规定URL,发送不同类型请求
/emp/{id} GET 查询员工
/emp/ POST 保存员工
/emp/{id} PUT 修改员工
/emp/{id} DELETE 删除员工

3.2.1服务器端逻辑

在EmployeeCtroller类中新增方法,并在相应service增加方法和调用mapper的方法。同时基于注解在service层的增删改方法上添加事务。
EmployeeCtroller:

//员工保存
    @RequestMapping(path="/emps",method = RequestMethod.POST)
    @ResponseBody
    public Msg saveEmp(Employee employee){
        employeeService.saveEmp(employee);
        return Msg.success();
    }

service:

@Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)
    public void saveEmp(Employee employee) {
        employeeMapper.insertSelective(employee);
    }

3.2.1增加输入校验规则(具体参考前端页面中的书写)

此外:①增加ajax发送请求验证用户名是否可用:
Controller层:

 //员工名称校验是否可用
    @RequestMapping(path="/checkuser",method = RequestMethod.POST)
    @ResponseBody
    public Msg checkUser(String empName){
        //先判断用户名是否是合法表达式
        String regx="(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})";
        boolean matches = empName.matches(regx);
        if (!matches){
            return Msg.fail().add("va_msg","用户名必须是6-16数字和字母组合或2-5位中文");
        }
        //数据库用户名校验
        boolean result=employeeService.checkUser(empName);
        if(result){
            return Msg.success();
        }
        return Msg.fail().add("va_msg","用户名不可用");
    }

Servive层:返回true表示可用,false表示用户名已存在

public boolean checkUser(String empName) {
        EmployeeExample example= new EmployeeExample();
        EmployeeExample.Criteria criteria = example.createCriteria();
        criteria.andEmpNameEqualTo(empName);
        long count = employeeMapper.countByExample(example);
        return count==0;
    }

②新增JSR303后端校验:
导入jar包:

<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.1.5.Final</version>
</dependency>

在员工类中利用注解:

 @Pattern(regexp ="(^[a-zA-Z0-9_-]{6,16}$)|(^[\\u2E80-\\u9FFF]{2,5})",
            message = "用户名必须是6-16数字和字母组合或2-5位中文")
    private String empName;
 @Pattern(regexp = "^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$",
            message = "邮箱格式不合法")
    private String email;

如何校验:在controller层封装对象时添加valid注解:BindingResult用于判断封装结果

 //员工保存
    @RequestMapping(path="/emps",method = RequestMethod.POST)
    @ResponseBody
    public Msg saveEmp(@Valid Employee employee, BindingResult  result){
        if (result.hasErrors()){
            //校验失败,返回失败,在模态框中显示校验失败的错误信息
            Map<String,Object> map=new HashMap<String, Object>();
            List<FieldError> fieldErrors = result.getFieldErrors();
            for (FieldError error : fieldErrors) {
                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();
        }
    }

3.3 修改

与新增类似,利用模态框输入参数,点击保存上传参数给服务器,访问数据库进行修改。

tomcat服务器发送put请求:
方案一:利用POST的type,在ajax访问参数中加入_method.在tomcat服务器中,只有POST请求会进行数据封装。

$.ajax({
            url:"${APP_PATH}/emps/"+$(this).attr("edit_id"),
            type:"POST",
            data:$("#empUpdateModel form").serialize()+"&_method=PUT",
            success:function (result) {
                alert(result.msg);
            }
        });

方案二:配置filter(不必再传递_method参数,type直接使用PUT),FormContentFilter,可以处理PUT和DELETE请求

<filter>
    <filter-name>formContentFilter</filter-name>
    <filter-class>org.springframework.web.filter.FormContentFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>formContentFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

Controller及Servive的方法:

 //更新员工
    @RequestMapping(path="/emps/{empId}",method = RequestMethod.PUT)
    @ResponseBody
    public Msg saveEmp(Employee employee){
        employeeService.updateEmp(employee);
        return Msg.success();
    }
 //员工更新
    @Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)
    public void updateEmp(Employee employee) {
        employeeMapper.updateByPrimaryKeySelective(employee);
    }

3.4 删除

3.4 .1单个删除

请求的URL:"/emps/{id}"。

//删除员工byId
    @RequestMapping(path="/emps/{id}",method = RequestMethod.DELETE)
    @ResponseBody
    public Msg deleteEmpById(@PathVariable(name = "id") Integer id){
        employeeService.deleteEmpById(id);
        return Msg.success();
    }
//员工删除
    @Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)
    public void deleteEmpById(Integer id) {
        employeeMapper.deleteByPrimaryKey(id);
    }

3.4.2批量删除

contrlloer层取消单个删除的方法,进行整合:

//批量删除员工
    @RequestMapping(path="/emps/{ids}",method = RequestMethod.DELETE)
    @ResponseBody
    public Msg deleteEmp(@PathVariable(name = "ids") String ids){
        String[] str_ids= ids.split("-");
        if (str_ids.length==1){
            employeeService.deleteEmpById(Integer.parseInt(str_ids[0]));
        }else{
        ArrayList<Integer> in_ids = new ArrayList<>();
        for (String id : str_ids) {
            in_ids.add(Integer.parseInt(id));
        }
        employeeService.deleteEmpBatch(in_ids);
        }
        return Msg.success();
    }

Service层

//批量删除
    @Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.REPEATABLE_READ,rollbackFor = Exception.class)
    public void deleteEmpBatch(List<Integer> ids) {
        EmployeeExample example = new EmployeeExample();
        EmployeeExample.Criteria criteria = example.createCriteria();
        //delete from XXX where emp_id in XXX
        criteria.andEmpIdIn(ids);
        employeeMapper.deleteByExample(example);
    }

四、页面jsp文件

index.jsp:

<%--
  Created by IntelliJ IDEA.
  User: js
  Date: 2020/11/11
  Time: 23:05
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%--引入标签库--%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <title>员工列表</title>
    <%
        pageContext.setAttribute("APP_PATH", request.getContextPath());
    %>
    <%--    引入jquery--%>
    <script src="${APP_PATH}/static/js/jquery-1.12.4.min.js"></script>
    <link href="./static/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet">
    <%--    引入样式--%>
    <script src="${APP_PATH}/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
</head>
<body>

<!-- 员工修改的模态框 -->
<div class="modal fade" id="empUpdateModel" 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="myModalLabe">员工修改</h4>
            </div>
            <div class="modal-body">
                <form class="form-horizontal">
                    <div class="form-group">
                        <label class="col-sm-2 control-label">empName</label>
                        <div class="col-sm-10">
                            <p class="form-control-static" id="empName_upadate_static"></p>
                        </div>
                    </div>
                    <div class="form-group">
                        <label class="col-sm-2 control-label">gender</label>
                        <div class="col-sm-10">
                            <label class="radio-inline">
                                <input type="radio" name="gender" id="gender1_update_input" value="M"></label>
                            <label class="radio-inline">
                                <input type="radio" name="gender" id="gender2_update_input" value="F"></label>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="email_update_input" class="col-sm-2 control-label">email</label>
                        <div class="col-sm-10">
                            <input type="text" class="form-control" name="email" id="email_update_input"
                                   placeholder="email@qq.com">
                            <span class="help-block"></span>
                        </div>
                    </div>

                    <div class="form-group">
                        <label for="email_add_input" class="col-sm-2 control-label">deptName</label>
                        <div class="col-sm-4">
                            <%--                            部门提交部门ID即可--%>
                            <select class="form-control" name="dId" id="dept_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="emp_update_btn">更新</button>
            </div>
        </div>
    </div>
</div>


<!-- 员工添加的模态框 -->
<div class="modal fade" id="empAddModel" 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 type="text" class="form-control" name="empName" id="empName_add_input"
                                   placeholder="empName">
                            <span class="help-block"></span>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="empName_add_input" class="col-sm-2 control-label">gender</label>
                        <div class="col-sm-10">
                            <label class="radio-inline">
                                <input type="radio" name="gender" id="gender1_add_input" value="M"></label>
                            <label class="radio-inline">
                                <input type="radio" name="gender" id="gender2_add_input" value="F"></label>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="email_add_input" class="col-sm-2 control-label">email</label>
                        <div class="col-sm-10">
                            <input type="text" class="form-control" name="email" id="email_add_input"
                                   placeholder="email@qq.com">
                            <span class="help-block"></span>
                        </div>
                    </div>

                    <div class="form-group">
                        <label for="email_add_input" class="col-sm-2 control-label">deptName</label>
                        <div class="col-sm-4">
                            <%--                            部门提交部门ID即可--%>
                            <select class="form-control" name="dId" id="dept_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="emp_save_btn">保存</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-4 col-md-offset-8">
            <button type="button" class="btn btn-info" id="emp_add_model_btn">新增</button>
            <button type="button" class="btn btn-danger" id="emp_delete_all_btn">删除</button>
        </div>
    </div>
    <%--    显示表格--%>
    <div class="row">
        <div class="col-md-12">
            <table class="table table-hover" id="emps_table">
                <thead>
                <tr>
                    <th>
                        <input type="checkbox" id="check_all"/>
                    </th>
                    <th>empId</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" id="page_info_area"></div>
        <%--        分页条信息--%>
        <div class="col-md-6" 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) {
                // console.log(result);
                // 1.解析并显示员工数据
                build_emps_table(result);
                // 2.解析并显示分页信息
                bulid_page_info(result);
                //解析显示分页条信息
                build_page_nav(result);
                //跳转页面
            }
        });
    }

    // 清空表单
    function reset_form(ele) {
        $(ele)[0].reset();
        //清空样式
        $(ele).removeClass("has-error has success");
        $(ele).find(".help-block").text("");

    }

    // 绑定事件,点击新增按钮,弹出模态框
    $("#emp_add_model_btn").click(function () {

        reset_form("#empAddModel form")
        // $("#empAddModel form")[0].reset();
        // 发送ajax请求,查出部门信息,显示在下拉列表
        getDepts("#empAddModel select");

        $("#empAddModel").modal({
            backdrop: "static"
        })
    });

    //绑定编辑按钮,弹出模态框
    $(document).on("click", ".edit_btn", function () {
        getDepts("#empUpdateModel select");
        //发送ajax请求,获取员工信息
        var empId = $(this).attr("edit_id");
        getEmp(empId);

        // 把员工ID传递给更新按钮
        $("#emp_update_btn").attr("edit_id", empId);
        $("#empUpdateModel").modal({
            backdrop: "static"
        })
    });

    //绑定删除事件

    $(document).on("click", ".delete_btn", function () {
        var empName = $(this).parents("tr").find("td:eq(2)").text();
        var empId = $(this).attr("delete_id");
        if (confirm("确认删除【" + empName + "】吗")) {
            //确认。发送ajax请求
            $.ajax({
                url: "${APP_PATH}/emps/" + empId,
                type: "DELETE",
                success: function (result) {
                    alert(result.msg);
                    //回到本页
                    to_page(currentPage);
                }
            })
        }

    })
    //绑定保存按钮单击事件
    $("#emp_save_btn").click(function () {
        // 发送ajax请求前对提交的数据进行校验
        if (validate_add_form() == false) {
            return false;
        }
//判断之前的ajax用户名校验是否成功
        if ($(this).attr("ajax-va") == "error") {
            return false;
        }
        $.ajax({
            url: "${APP_PATH}/emps",
            type: "POST",
            data: $("#empAddModel form").serialize(),
            success: function (result) {
                if (result.code == 100) {
                    //员工保存成功
                    alert(result.msg);
                    //1.关闭模态框
                    $("#empAddModel").modal('hide');
                    //2.来到最后一页,显示刚才的数据(发送ajax请求)
                    to_page(totalRecord);
                } else {
                    //显示错误信息
                    if (undefined != result.extend.errorFields.email) {
                        //显示邮箱错误信息
                        $("#email_add_input").parent().addClass("has-error");
                        $("#email_add_input").next("span").text(result.extend.errorFields.email);
                    }
                    if (undefined != result.extend.errorFields.empName) {
                        //显示用户名错误信息
                        $("#empName_add_input").parent().addClass("has-error");
                        $("#empName_add_input").next("span").text(result.extend.errorFields.empName);
                    }
                }
            }
        });
    });

    //绑定批量删除事件
    $("#emp_delete_all_btn").click(function () {
        var empNames="";
        var empIds="";
        $.each($(".check_item:checked"), function () {
            empNames += $(this).parents("tr").find("td:eq(2)").text() + ",";
            empIds+=$(this).parents("tr").find("td:eq(1)").text()+"-";
        })
        empNames=empNames.substring(0,empNames.length-1);
        empIds=empIds.substring(0,empIds.length-1);
        if (confirm("你确定要删除【"+empNames+"】吗?")) {
            $.ajax({
                url:"${APP_PATH}/emps/"+empIds,
                type:"DELETE",
                success:function (result) {
                    alert(result.msg);
                    to_page(currentPage);
                }
            })
        }
    });

    // 绑定更新按钮单击事件
    $("#emp_update_btn").click(function () {
        var email = $("#email_update_input").val();
        var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/
        if (!regEmail.test(email)) {
            $("#email_add_input").parent().addClass("has-error");
            $("#email_add_input").next("span").text("邮箱格式不合法")
            return false;
        }
        $("#email_add_input").parent().addClass("has-success");
        $("#email_add_input").next("span").text("")
        // 发送ajax请求进行保存
        $.ajax({
            url: "${APP_PATH}/emps/" + $(this).attr("edit_id"),
            type: "PUT",
            data: $("#empUpdateModel form").serialize(),
            success: function () {
                // alert(result.msg);
                //关闭模态框
                $("#empUpdateModel").modal("hide");
                //   回到本页面
                to_page(currentPage);
            }
        });
    });


    $("#empName_add_input").change(function () {
        var empName = this.value;
        //发送ajax请求至服务端校验是否可用
        $.ajax({
            url: "${APP_PATH}/checkuser",
            data: "empName=" + empName,
            type: "POST",
            success: function (result) {
                $("#empName_add_input").parent().removeClass("has-success has-error")
                if (result.code == 100) {
                    $("#empName_add_input").parent().addClass("has-success");
                    $("#empName_add_input").next("span").text("用户名可用");
                    $("#emp_save_btn").attr("ajax-va", "success");
                } else {
                    $("#empName_add_input").parent().addClass("has-error");
                    $("#empName_add_input").next("span").text(result.extend.va_msg);
                    $("#emp_save_btn").attr("ajax-va", "error");
                }
            }
        })

    });

    //显示页面员工信息
    function build_emps_table(result) {
        //清空table表
        $("#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.gender == "M" ? "男" : "女");
            var emailTd = $("<td></td>").append(item.email);
            var deptNameTd = $("<td></td>").append(item.department.deptName);
            var editBtn = $("<button></button>").addClass("btn btn-info btn-sm edit_btn").append($("<span></span>").addClass("glyphicon glyphicon-pencil")).append("编辑");
            editBtn.attr("edit_id", item.empId);
            var delBtn = $("<button></button>").addClass("btn btn-danger btn-sm delete_btn").append($("<span></span>").addClass("glyphicon glyphicon-trash")).append("删除");
            delBtn.attr("delete_id", item.empId);
            var BtnTd = $("<td></td>").append(editBtn).append(" ").append(delBtn);
            // 使用append进行添加
            $("<tr></tr>").append(checkBoxTd).append(empIdTd).append(empNameTd).append(genderTd).append(emailTd)
                .append(deptNameTd).append(BtnTd).appendTo("#emps_table tbody");
        });
    }

    //全选,全不选
    $("#check_all").click(function () {
        // attr获取checked都是undefiened,使用prop修改和读取dom原生属性值
        $(".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);
    });

    // 解析显示分页信息
    function bulid_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", "#"));
        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);
            })
        }
        ul.append(firstPageLi).append(prePageLi);
        $.each(result.extend.pageInfo.navigatepageNums, function (index, item) {

            var numLi = $("<li></li>").append($("<a></a>").append(item).attr("href", "#"));
            if (result.extend.pageInfo.pageNum == item) {
                numLi.addClass("active");
            }
            numLi.click(function () {
                to_page(item);
            })
            ul.append(numLi);
        })
        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.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(nextPageLi).append(lastPageLi);
        var navEle = $("<nav></nav>").append(ul);
        $("#page_nav_area").append(navEle);
    }

    //查询部门信息函数
    function getDepts(ele) {
        $.ajax({
            url: "${APP_PATH}/depts",
            type: "GET",
            success: function (result) {
                // console.log(result);"extend":{"depts":[{"deptId":1,"deptName":"开发部"},{"deptId":2,"deptName":"测试部"}]
                //显示下拉列表
                $(ele).empty();
                $.each(result.extend.depts, function () {
                    var optionEle = $("<option></option>").append(this.deptName).attr("value", this.deptId);
                    optionEle.appendTo(ele)
                })
            }
        });
    }

    //查询员工信息函数
    function getEmp(id) {
        $.ajax({
            url: "${APP_PATH}/emps/" + id,
            type: "get",
            success: function (result) {
                // console.log(result);
                // extend: {emp: {empId: 103, empName: "Tom", gender: "M", email: "Tom@qq.com", dId: 1, department: null}}
                var empData = result.extend.emp;
                $("#empName_upadate_static").text(empData.empName);
                $("#email_update_input").val(empData.email);
                $("#empUpdateModel input[type=radio]").val([empData.gender]);
                $("#empUpdateModel select").val([empData.dId]);
            }
        });
    }

    //校验函数
    function validate_add_form() {
        //拿到校验的数据,使用正则表达式进行校验
        var email = $("#email_add_input").val();
        var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/
        if (!regEmail.test(email)) {
            $("#email_add_input").parent().addClass("has-error");
            $("#email_add_input").next("span").text("邮箱格式不合法")
            return false;
        } else {
            $("#email_add_input").parent().addClass("has-success");
            $("#email_add_input").next("span").text("")
        }
        return true;
    }


</script>
</body>
</html>








项目构建中遇到的bug总结

无法加载Spring配置文件

java.lang.IllegalStateException: Failed to load ApplicationContext

并非在main目录下进行得测试,而是与main并列的Test文件目录下进行。
错误定义:

@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
@RunWith(SpringJUnit4ClassRunner.class)

修改:在classpath后加上*:

@ContextConfiguration(locations = {"classpath*:applicationContext.xml"})
@RunWith(SpringJUnit4ClassRunner.class)

插入测试时候出现数据库连接错误

create connection SQLException, url: jdbc:mysql://localhost:3306/ssm, errorCode 1045, state 28000

错误原因: 配置文件中 username 与 Mysql 关键字冲突
修改dbdruid.properties中的username为user即可。
修改前:
在这里插入图片描述

使用MockMvc模拟请求时出错

具体异常:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'employeeService': Unsatisfied dependency expressed through field 'employeeMapper'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'employeeMapper' defined in file [E:\workspace_idea1\SSMproject\ssm_crud\target\classes\com\js\dao\EmployeeMapper.class]: Unsatisfied dependency expressed through bean property 'sqlSessionFactory'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in URL [file:/E:/workspace_idea1/SSMproject/ssm_crud/target/classes/applicationContext.xml]: Invocation of init method failed; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/mybatis-config]

末尾:Could not open ServletContext resource [/mybatis-config]
从最后可以看出是在spring中的mybatis配置无法读到。
检查spring中配置:

<!--    与mybatis整合,可以生成sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="configLocation" value="mybatis-config"></property>
    <property name="dataSource" ref="dataSource"></property>

修改为加上classpath:路径后,测试成功

<!--    与mybatis整合,可以生成sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="configLocation" value="classpath:mybatis-config"></property>
    <property name="dataSource" ref="dataSource"></property>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值