尚硅谷_SSM_雷丰阳整合

SSM-CRUD

ssm:SpringMVC+Spring+Mybatis

CRUD:Create(创建)、Retrieve(查询)、Update(更新)、Delete(删除)

功能点:

  • 分页
  • 数据校验
    • JQuery前端校验+JSR303后端校验
  • ajax
  • Rest风格的URI;使用HTTP协议请求方式的动词,来表示对资源的操作(GET(查询)、POST(新增)、PUT(修改)、DELETE(删除))

技术点

  • 基础框架-SSM(SpringMVC+Spring+Mybatis)
  • 数据库-MySQL
  • 前端框架-BootStrap快速搭建简洁美观的界面
  • 项目的依赖管理-Maven
  • 分页-pagehelper
  • 逆向工程-Mybatis Generator

路径问题:

不以/开始的相对路径,找资源,以当前资源的路径为准,经常容易出现问题
以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:8080/)需要加上项目名
http://localhost:8080/ssm_crud


基础环境搭建

创建一个maven工程

新建一个web项目

项目名和项目位置

maven的一些配置

创建完成后的界面

创建一些必需的文件夹

可以看到我们的 dreamland-web 子工程没有 java、resources 和 test 目录,我们需要手动创建一下,操作过程如下:

main -> New -> Directory ==> 创建 java

main -> New -> Directory ==> 创建 resources

src -> New -> Directory ==> 创建 test

test -> New -> Directory ==> 创建 java

test -> New -> Directory ==> 创建 resources

然后对创建好的 java、resources 和 test/java、test/resourcs 目录均右键选择 Mark Diretory as,然后分别进行如下操作:

java -> Sources Root //java源码根目录

resources -> Resources Root//java 配置文件目录

test/java -> Test Sources Root//java 测试源码目录

test/java -> Test Sources Root//java 测试配置文件目录

引入项目依赖的jar包

  • spring
  • springmvc
  • mybatis
  • 数据库连接池,驱动包
  • 其他(jstl、servlet-api、junit)
<dependencies>
  <!--引入项目依赖的jar包-->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.3.2</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.3.2</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.2</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>5.3.2</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.2</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.2</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.2</version>
  </dependency>

  <!--mybatis-->
  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
  </dependency>
  <!--Mybatis整合Spring的适配包-->
  <!-- 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>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.1</version>
  </dependency>
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.46</version>
  </dependency>

  <!--jstl、servlet-api、junit-->
  <dependency>
    <groupId>javax.servlet.jsp.jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
  </dependency>
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    <scope>provided</scope>
  </dependency>

  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
  </dependency>
</dependencies>

引入BootStrap前端框架

BookStrap官方网站

  1. 在官方网站下载BookStrap 样式所需css、js

  2. 下载jQuery,因jQuery官网访问一般容易出现问题,可以在这个网站下载jQuery

  3. 引入Bootstrap

    引入Bootstrap

  4. <%
        pageContext.setAttribute("APP_PATH",request.getContextPath());
    %>
    <%--
    不以/开始的相对路径,找资源,以当前资源的路径为准,经常容易出现问题
    以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:8080/)需要加上项目名
                            http://localhost:8080/ssm_crud
    --%>
    
    <link type="text/css" rel="stylesheet" href="${APP_PATH}/static/bootstrap/css/bootstrap.css">
    <%--引入jquery--%>
    <script src="${APP_PATH}/static/js/jquery-3.5.1.js"></script>
    <script src="${APP_PATH}/static/bootstrap/js/bootstrap.js"></script>
    

引入jQuery和BookStrap

  1. 测试结果

    可以看到这个按钮已经发生变化了

    可以看到这个按钮已经发生变化了

  2. 可能会出现以下错误:bootstrap.min.js:6 Uncaught Error: Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4

    原因:Bootstrap的JavaScript要求jQuery 1.9.1或更高版本,但低于版本4

编写SSM整合的关键配置文件

web.xml、spring、springmvc、mybatis

web.xml(web.xml)

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.1"
         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_3_1.xsd">

  <display-name>Archetype Created Web Application</display-name>


  <!--1、配置Spring的配置文件-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!--2、SpringMVC的前端控制器,拦截所有请求-->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
      <!--    <init-param>-->
<!--      <param-name>contextConfigLocation</param-name>-->
    <!--不指定位置时,默认就是类路径下servlet-name后加上-servlet-->
<!--      <param-value>classpath:</param-value>-->
<!--    </init-param>-->
  </servlet>
  <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>

SpringMVC(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的配置文件,包含网站跳转逻辑,配置,use-default-filters="false"禁用默认行为,只扫描Controller注解-->
    <context:component-scan base-package="com.qykhhr.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>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!--两个标准配置-->
    <!--将SpringMVC不能处理的请求交给Tomcat-->
    <mvc:default-servlet-handler></mvc:default-servlet-handler>

    <!--能支持SpringMVC更高级的一些功能,比如JSR303校验,快捷的ajax请求,映射动态请求-->
    <mvc:annotation-driven></mvc:annotation-driven>
</beans>

Spring(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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--Spring的配置文件,这里主要配置和业务逻辑有关的-->
    <!--Spring配置文件的核心点:数据源、与Mybatis的整合、事务控制-->

    <context:component-scan base-package="com.qykhhr">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <!--==============配置和mybatis的整合==============-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--指定Mybatis全局配置文件的位置-->
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <property name="dataSource" ref="dataSource"></property>
        <!--指定mybatis的mapper文件的位置-->
        <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
    </bean>

    <!--配置扫描器,将mybatis接口的实现加入到IOC容器中-->
    <bean  class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--扫描所有dao接口的实现,加入到IOC容器中-->
        <property name="basePackage" value="com.qykhhr.crud.dao"></property>
    </bean>
    <!--==========================================================================-->

    <!--==============事务控制的配置==============-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--控制住数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--开启基于注解的事务,使用xml配置形式的事务(必要主要的都是使用配置式)-->
    <aop:config>
        <!--切入点表达式-->
        <aop:pointcut id="txPoint" expression="execution(* com.qykhhr.crud.service..*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"></aop:advisor>
    </aop:config>

    <!--配置事务增强,事务如何切入-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--所有方法都是事务方法-->
            <tx:method name="*"/>
            <!--以get开始的所有方法-->
            <tx:method name="get*" read-only="true"></tx:method>
        </tx:attributes>
    </tx:advice>
    <!--========================================================-->
</beans>

mybatis(mybatis-config.xml)

<?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"/>
    </settings>
    <typeAliases>
        <package name="com.qykhhr.crud.bean"/>
    </typeAliases>
</configuration>

使用mybatis的逆向工程生成对应的bean以及mapper

首先导包mybatis-generator-core

<!--MBG,mybatis逆向工程-->
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
  <groupId>org.mybatis.generator</groupId>
  <artifactId>mybatis-generator-core</artifactId>
  <version>1.4.0</version>
</dependency>

在当前工程路径下编写generatorConfig.xml(mgb.xml)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>

    <context id="DB2Tables" targetRuntime="MyBatis3">

        <!--逆向生成的bean以及mapper没有注释-->
        <commentGenerator>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <!--配置数据库连接-->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/ssm_crud?useSSL=false"
                        userId="root"
                        password="614310">
        </jdbcConnection>

        <javaTypeResolver >
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!--指定javaBean生成位置-->
        <javaModelGenerator targetPackage="com.qykhhr.crud.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接口生成的位置:mapper接口-->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.qykhhr.crud.dao"
                             targetProject=".\src\main\java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>

        <!--指定每个表的生成策略-->
        <table tableName="tbl_emp" domainObjectName="Employee"></table>
        <table tableName="tbl_dept" domainObjectName="Department"></table>
    </context>
</generatorConfiguration>

在编写一个测试类,运行

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class MBGTest {
    public static void main(String[] args) 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);
    }
}
  1. 数据库表对应的bean以及mapper就会自动生成

    Department.java、DepartmentExample.java、Employee.java、EmployeeExample.java

    DepartmentMapper.java、EmployeeMapper.java

    DepartmentMapper.xml、EmployeeMapper.xml

    自动生成的javaBean和mapper

    5.在执行main方法的时候出现了报错javax.net.ssl.SSLException MESSAGE: closing inbound before receiving peer's close_notify

    解决办法:设置useSSL为false

    <!--配置数据库连接-->
    <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                    connectionURL="jdbc:mysql://localhost:3306/ssm_crud?useSSL=false"
                    userId="root"
                    password="614310">
    </jdbcConnection>
    

修改mapper文件,添加自己需要的

​ Employee.java添加

//希望查询员工的同时,部门信息也是查询好的
private Department department;

​ EmployeeMapper.java添加

List<Employee> selectByExampleWithDept(EmployeeExample example);

Employee selectByPrimaryKeyWithDept(Integer empId);

​ EmployeeMapper.xml添加

<resultMap id="WithDeptResultMap" type="com.qykhhr.crud.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.qykhhr.crud.bean.Department">
    <id column="dept_id" property="deptId"/>
    <result column="dept_name" property="deptName"/>
  </association>
</resultMap>
<sql id="WithDept_Column_List">
    e.emp_id, e.emp_name, e.gender, e.email, e.d_id , d.dept_id,d.dept_name
  </sql>
  <!-- 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 tbl_emp e
    left join tbl_dept d on e.d_id=d.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 tbl_emp e
    left join tbl_dept d on e.d_id=d.dept_id
    where emp_id = #{empId,jdbcType=INTEGER}
  </select>

测试mapper

package com.qykhhr.crud.test;

import com.qykhhr.crud.dao.DepartmentMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * 测试dao层工作
 * 推荐Spring的项目就可以使用Spring的单元测试,可以自动注入我们需要的组件
 *
 * 1、导入SpringTest模块
 * 2、@ContextConfiguration指定Spring配置文件的位置
 * 3、直接autowired要使用的组件即可
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class MapperTest {

    @Autowired
    DepartmentMapper departmentMapper;
    /**
     * 测试DepartmentMapper
     */
    @Test
    public void testCRUD(){
//        //1、创建SpringIOC容器
//        ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//        //2、从容器中获取mapper
//        DepartmentMapper bean = ioc.getBean(DepartmentMapper.class);
        System.out.println(departmentMapper);
    }
}

测试出现的问题:Caused by: java.lang.IllegalArgumentException: Result Maps collection already contains value for com.qykhhr.crud.dao.EmployeeMapper.BaseResultMap

原因:使用MyBatis Generator逆向生成mapper的时候,如果多次生成会造成mapper内容重复生成,然后mapper里面的id出现了冲突

解决:在mapper文件中找到相同的id,会发现后面有一半内容和前面相同,将后面相同的内容删除就行了

查询

  • 访问index.jsp
  • index.jsp页面发送查询员工列表的请求
  • EmployeeController来接收请求,查出员工数据
  • 来到list.jsp页面进行展示
  1. 引入PageHelper分页插件
  • 导包

    • <!--pagehelper分页插件-->
      <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.2.0</version>
      </dependency>
      
  • 在mybatis配置文件(mybatis-config.xml)中进行注册

    • <!--注册分页插件-->
      <plugins>
          <plugin interceptor="com.github.pagehelper.PageInterceptor">
          </plugin>
      </plugins>
      
  • 在Controller中使用

    • import com.github.pagehelper.PageHelper;
      import com.github.pagehelper.PageInfo;
      import com.qykhhr.crud.bean.Employee;
      import com.qykhhr.crud.service.EmployeeService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Controller;
      import org.springframework.stereotype.Repository;
      import org.springframework.ui.Model;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RequestParam;
      import java.util.List;
      /**
       * 处理员工CRUD请求
       */
      @Controller
      public class EmployeeController {
          @Autowired
          EmployeeService employeeService;
          /**
           * 查询员工数据(分页查询)
           * @return
           */
          @RequestMapping("/emps")
          public String getEmps(@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo, Model model){
              //引入PageHelper分页插件
              //在查询之前只需要调用:传入页码以及每页的大小
              PageHelper.startPage(pageNo,5);
              //startPage后面紧跟的这个查询就是一个分页查询
              List<Employee> emps = employeeService.getAll();
      
              //使用PageInfo包装查询后的结果,只需要将pageInfo交给页面就行了
              //封装了详细的分页信息,包括我们查询出来的数据,传入连续显示的页数
              PageInfo page = new PageInfo(emps,5);
              model.addAttribute("pageInfo",page);
              return "list";
          }
      }
      

list页面的编写

路径问题:

不以/开始的相对路径,找资源,以当前资源的路径为准,经常容易出现问题
以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:8080/)需要加上项目名
http://localhost:8080/ssm_crud

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
  Created by IntelliJ IDEA.
  User: 雨林
  Date: 2021/1/12
  Time: 13:16
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>员工列表</title>
    <%
        pageContext.setAttribute("APP_PATH",request.getContextPath());
    %>
    <%--
    不以/开始的相对路径,找资源,以当前资源的路径为准,经常容易出现问题
    以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:8080/)需要加上项目名
                            http://localhost:8080/ssm_crud
    --%>

    <link type="text/css" rel="stylesheet" href="${APP_PATH}/static/bootstrap/css/bootstrap.css">
    <%--引入jquery--%>
    <script src="${APP_PATH}/static/js/jquery-3.5.1.js"></script>
    <script src="${APP_PATH}/static/bootstrap/js/bootstrap.js"></script>
</head>
<body>
    <%--搭建显示页面--%>
    <div class="container">
        <%--标题SSM-CRUD--%>
        <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 class="btn btn-primary">新增</button>
                <button class="btn btn-danger">删除</button>
            </div>
        </div>
        <%--显示表格数据--%>
        <div class="row">
            <div class="col-md-12">
                <table class="table table-hover">
                    <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.gender=="M"?"男":"女"}</th>
                            <th>${emp.email}</th>
                            <th>${emp.department.deptName}</th>
                            <th>
                                <button class="btn btn-primary btn-sm">
                                    <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
                                    编辑
                                </button>
                                <button class="btn btn-danger btn-sm">
                                    <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">
                当前第${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?pageNo=1">首页</a></li>

                        <c:if test="${pageInfo.hasPreviousPage}">
                            <li>
                                <a href="${APP_PATH}/emps?pageNo=${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?pageNo=${page_Num}">${page_Num}</a></li>
                            </c:if>
                        </c:forEach>

                        <c:if test="${pageInfo.hasNextPage}">
                            <li>
                                <a href="${APP_PATH}/emps?pageNo=${pageInfo.pageNum+1}" aria-label="Next">
                                    <span aria-hidden="true">&raquo;</span>
                                </a>
                            </li>
                        </c:if>

                        <li><a href="${APP_PATH}/emps?pageNo=${pageInfo.pages}">末页</a></li>
                    </ul>
                </nav>
            </div>

        </div>
    </div>
</body>
</html>

展示效果

展示效果

查询-ajax

  • index.jsp页面直接发送Ajax请求进行员工分页数据的查询
  • 服务器将查出的数据,以json字符串的形式返回给浏览器
  • 浏览器收到json字符串。可以使用js对json进行解析,使用js通过dom增删改改变页面
  • 返回json。实现客户端的无关性
  1. 可以使用@ResponseBody注解将数据以json形式返回到页面

    需要导入jackson支持json包

    <!--@RequestBody将数据转为json需要包-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.3</version>
    </dependency>
    
  2. 编写一个通用的返回json的类

    package com.qykhhr.crud.bean;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 通用的返回json的类
     */
    public class Msg {
    
        private int code;//状态码:100-成功 200-失败
        private String msg;//提示信息
        private Map<String,Object> extend = new HashMap<>();//装载要发送到页面的json数据
    
        public static Msg success(){
            Msg msg = new Msg();
            msg.setCode(100);
            msg.setMsg("处理成功!");
            return msg;
        }
    
        public static Msg fail(){
            Msg msg = new Msg();
            msg.setCode(200);
            msg.setMsg("处理失败!");
            return msg;
        }
    
        public Msg add(String key,Object value){
            this.getExtend().put(key, value);//将获取到的数据添加到map中
            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;
        }
    }
    
  3. 在控制器中将数据以json形式发送到前端页面

    /**
     * 将数据以json的形式传到页面
     * 需要导入jackson包
     * @param pageNo
     * @param model
     * @return
     */
    @RequestMapping("/emps")
    @ResponseBody
    public Msg getEmpWithJson(@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo, Model model){
        //引入PageHelper分页插件
        //在查询之前只需要调用:传入页码以及每页的大小
        PageHelper.startPage(pageNo,5);
        //startPage后面紧跟的这个查询就是一个分页查询
        List<Employee> emps = employeeService.getAll();
    
        //使用PageInfo包装查询后的结果,只需要将pageInfo交给页面就行了
        //封装了详细的分页信息,包括我们查询出来的数据,传入连续显示的页数
        PageInfo page = new PageInfo(emps,5);
        return Msg.success().add("pageInfo",page);
    }
    
  4. 前端对数据进行处理

    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%--
      Created by IntelliJ IDEA.
      User: 雨林
      Date: 2021/1/12
      Time: 13:16
      To change this template use File | Settings | File Templates.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>员工列表</title>
        <%
            pageContext.setAttribute("APP_PATH",request.getContextPath());
        %>
        <%--
        不以/开始的相对路径,找资源,以当前资源的路径为准,经常容易出现问题
        以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:8080/)需要加上项目名
                                http://localhost:8080/ssm_crud
        --%>
    
        <link type="text/css" rel="stylesheet" href="${APP_PATH}/static/bootstrap/css/bootstrap.css">
        <%--引入jquery--%>
        <script src="${APP_PATH}/static/js/jquery-3.5.1.js"></script>
        <script src="${APP_PATH}/static/bootstrap/js/bootstrap.js"></script>
    </head>
    <body>
    <%--搭建显示页面--%>
    <div class="container">
        <%--标题SSM-CRUD--%>
        <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 class="btn btn-primary">新增</button>
                <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>#</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">
        //页面加载完成之后直接去第1页
        //1、页面加载完成以后,直接去发送ajax请求,要到分页数据
        $(function(){
            $.ajax({
                url:"${APP_PATH}/emps",
                data:"pageNo=1",
                type:"GET",
                success:function (result) {
                    //console.log(result);
    
                    //1.解析并显示员工数据
                    build_emps_table(result);
                    //2.解析并显示分页信息
                    build_page_info(result);
                    //3、解析显示分页条
                    build_emps_nav(result);
                }
            });
        });
    
        //跳转到指定页面
        function to_page(pageNo) {
            $.ajax({
                url:"${APP_PATH}/emps",
                data:"pageNo="+pageNo,
                type:"GET",
                success:function (result) {
                    //console.log(result);
    
                    //1.解析并显示员工数据
                    build_emps_table(result);
                    //2.解析并显示分页信息
                    build_page_info(result);
                    //3、解析显示分页条
                    build_emps_nav(result);
                }
            });
        }
    
        /*解析显示分页数据*/
        function build_emps_table(result){
            //在发送每个Ajax请求的时候都清空表格内容
            $("#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-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);
    
                //append方法执行完成以后还是返回原来的元素,这里每次append都会产生新的<tr></tr>
                $("<tr></tr>").append(empIdTd).append(empNameTd)
                    .append(genderTd).append(emailTd).append(deptNameTd)
                    .append(btnTd)
                    .appendTo("#emps_table tbody");
            })
        }
    
        /*解析显示分页信息*/
        function build_page_info(result){
            //在发送Ajax的请求时要清空里面的内容,要不然就会追加内容
            $("#page_info_area").empty();
    
            $("#page_info_area").append("当前第"+result.extend.pageInfo.pageNum+"页,总"
                +result.extend.pageInfo.pages+"页,总"+result.extend.pageInfo.total+"条记录")
        }
    
        /*解析显示分页条*/
        function build_emps_nav(result){
            $("#page_nav_area").empty();
    
            //$("#page_nav_area")
            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;"));
            //判断如果没有上一页,就不能点击
            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)
                });
            }
            
    
            var nextPageLi = $("<li></li>").append($("<a></a>").append("&raquo;"));
            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(firstPageLi).append(prePageLi);
    
            //遍历给ul中添加页码提示
            $.each(result.extend.pageInfo.navigatepageNums,function(index,item){
                var numLi = $("<li></li>").append($("<a></a>").append(item));
                if(result.extend.pageInfo.pageNum == item){
                    numLi.addClass("active");
                }
                numLi.click(function(){
                    to_page(item);
                });
                ul.append(numLi);
            })
            //添加末页的提示
            ul.append(nextPageLi).append(lastPageLi);
    
            //把ul加入到nav
            var navEle = $("<nav></nav>").append(ul);
    
            navEle.appendTo("#page_nav_area");
        }
    </script>
    </body>
    </html>
    

    reasonable:分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。

    <!--注册分页插件-->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <!--分页参数合理化,-->
            <property name="reasonable" value="true"/>
        </plugin>
    </plugins>
    

新增

  • 在index.jsp页面点击“新增”
  • 弹出新增对话框
  • 去数据库查询部门列表,显示在对话框中
  • 用户输入数据,并进行校验
  1. 创建员工添加模态框

    员工信息添加的表单

    员工信息添加的表单

    
    <%--员工添加的模态框--%>
    <!-- Modal -->
    <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 class="col-sm-2 control-label">empName</label>
                            <div class="col-sm-10">
                                <input type="text" name="empName" class="form-control" id="empName_add_input" placeholder="empName">
                            </div>
                        </div>
                        <div class="form-group">
                            <label class="col-sm-2 control-label">email</label>
                            <div class="col-sm-10">
                                <input type="email" name="email" class="form-control" id="email_add_input" placeholder="email@qykhhr.com">
                            </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_add_input" value="M" checked="checked"> 男
                                </label>
                                <label class="radio-inline">
                                    <input type="radio" name="gender" id="gender2_add_input" value="F"> 女
                                </label>
                            </div>
                        </div>
                        <div class="form-group">
                            <label class="col-sm-2 control-label">deptName</label>
                            <div class="col-sm-4">
                                <%--部门提交部门id即可--%>
                                <select name="dId" class="form-control" id="dept_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="emp_save_btn">保存</button>
                </div>
            </div>
        </div>
    </div>
    
    <script>
        $("#emp_add_modal_btn").click(function(){
            $("#empAddModal").modal({
                backdrop:"static"
            });
        });
    </script>
    
  2. 从数据库中获取部门信息,显示到添加员工的模态框中

    1. 先创建一个service
    import com.qykhhr.crud.bean.Department;
    import com.qykhhr.crud.dao.DepartmentMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import java.util.List;
    @Service
    public class DepartmentService {
    
        @Autowired
        private DepartmentMapper departmentMapper;
    
        public List<Department> getDepts() {
    
            List<Department> departments = departmentMapper.selectByExample(null);
            return departments;
        }
    
    }
    
    
    1. 再创建一个Controller

      import com.qykhhr.crud.bean.Department;
      import com.qykhhr.crud.bean.Msg;
      import com.qykhhr.crud.service.DepartmentService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.ResponseBody;
      import java.util.List;
      /**
       * 处理和部门有关的请求
       */
      @Controller
      public class DepartmentController {
      
          @Autowired
          private DepartmentService departmentService;
      
          /**
           * 返回所有的部门信息
           */
          @RequestMapping("/depts")
          @ResponseBody
          public Msg getDepts(){
              //查出的所有部门信息
              List<Department> list = departmentService.getDepts();
              return Msg.success().add("depts",list);
          }
      }
      
  3. 在index.jsp发送Ajax请求

    <script>
    //点击新增按钮弹出模态框
        $("#emp_add_modal_btn").click(function(){
            $("#dept_add_select").empty();
    
            //发送Ajax请求,查出部门信息,显示在下拉列表中
            getDepts();
    
            //弹出模态框
            $("#empAddModal").modal({
                backdrop:"static"
            });
        });
    
        //查出所有的部门信息并显示在下拉列表中
        function getDepts(){
               $.ajax({
                   url:"${APP_PATH}/depts",
                   type:"GET",
                   success:function (result) {
                        //console.log(result);
                       $.each(result.extend.depts,function(){
                           var optionEle = $("<option></option>").append(this.deptName).attr("value",this.deptId);
                           optionEle.appendTo("#dept_add_select");
                       });
                   }
               });
        }
    </script>
    

URI:

/emp/{id} GET 查询员工

/emp POST 保存员工

/emp/{id} PUT 修改员工

/emp/{id} DELETE 删除员工

保存员工信息

在index.jsp中发送Ajax请求

<script>
    $("#emp_save_btn").click(function(){


       //1、模态框中填写的表单数据提交给服务器进行保存
        //2、发送Ajax请求保存员工

        $.ajax({
            url:"${APP_PATH}/emp",
            type:"POST",
            data:$("#empAddModal form").serialize(),
            success:function () {
                //员工保存之后
                //1、关闭模态框
                $("#empAddModal").modal('hide');
                // $("#empAddModal input").empty();
                //2、来到最后一页,显示刚才保存的数据
                //发送Ajax请求显示最后一页数据即可
                to_page(totalRecord);
            }
        })
    });    
</script>

在EmployeeController中保存员工信息

/**
 * 定义员工返回
 * @return
 */
@RequestMapping(value = "/emp",method = RequestMethod.POST)
public String saveEmp(Employee employee){
    employeeService.saveEmp(employee);
    return "/**
     * 定义员工返回
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/emp",method = RequestMethod.POST)
    public Msg saveEmp(Employee employee){
        employeeService.saveEmp(employee);
//        return "lastPage";
        return Msg.success();
    }lastPage";
}

对输入的员工信息进行正则表达式校验

  1. 进行校验,并以弹窗的形式输出错误信息
//校验表单数据
function validate_add_form(){
    //1、拿到要校验的数据,使用正则表达式进行校验
    var empName = $("#empName_add_input").val();
    var regName = /(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/;
    if(!regName.test(empName)){//如果校验失败,提示
        alert("用户名可以是2-5位中文或者6-16位英文和数字的组合");
        return false;
    }
    //校验邮箱格式
    var email = $("#email_add_input").val();
    var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;

    if(!regEmail.test(email)){
        alert("邮箱格式不正确");
        return false;
    }
    return true;
}

//点击保存员工的Ajax请求
$("#emp_save_btn").click(function(){


   //1、模态框中填写的表单数据提交给服务器进行保存
    //还需要对将要提交给服务器的数据进行校验
   if( !validate_add_form()){
       return false;
   }
    //2、发送Ajax请求保存员工

    $.ajax({
        url:"${APP_PATH}/emp",
        type:"POST",
        data:$("#empAddModal form").serialize(),
        success:function () {
            //员工保存之后
            //1、关闭模态框
            $("#empAddModal").modal('hide');
            // $("#empAddModal input").empty();
            //2、来到最后一页,显示刚才保存的数据
            //发送Ajax请求显示最后一页数据即可
            to_page(totalRecord);
        }
    })
});
  1. 使用正则表达式校验信息,将错误信息放到输入框下面,并将输入框变成红色

效果

//校验表单数据
function validate_add_form(){
    //1、拿到要校验的数据,使用正则表达式进行校验
    var empName = $("#empName_add_input").val();
    var regName = /(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/;
    if(!regName.test(empName)){//如果校验失败,提示
        // alert("用户名可以是2-5位中文或者6-16位英文和数字的组合");
        show_validate_msg("#empName_add_input","error","用户名可以是2-5位中文或者6-16位英文和数字的组合");
        // $("#empName_add_input").parent().addClass("has-error");
        // $("#empName_add_input").next("span").text("用户名可以是2-5位中文或者6-16位英文和数字的组合");
        return false;
    }else {
        // $("#empName_add_input").parent().addClass("has-success");
        // $("#empName_add_input").next("span").text("");
        show_validate_msg("#empName_add_input","success","");
    }
    //校验邮箱格式
    var email = $("#email_add_input").val();
    var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;

    if(!regEmail.test(email)){
        //alert("邮箱格式不正确");
        // $("#email_add_input").parent().addClass("has-error");
        // $("#email_add_input").next("span").text("邮箱格式不正确");
        show_validate_msg("#email_add_input","error","邮箱格式不正确");
        return false;
    }else {
        // $("#email_add_input").parent().addClass("has-success");
        // $("#email_add_input").next("span").text("");
        show_validate_msg("#email_add_input","success","");
    }
    return true;
}

//显示校验结果的提示信息
function show_validate_msg(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);
    }
}
  1. 使用Ajax校验用户名是否重复,从数据库中查询

    使用$("#empAddModal form")[0].reset();//清除表单数据(表单重置)

  • 在index.jsp页面当输入框发生变化,就发送Ajax请求,到数据库中查看新增用户名是否可用

    //当用户名输入框发生变化,发送Ajax请求校验新增用户名是否可用
    $("#empName_add_input").change(function(){
        //发送Ajax请求校验用户名是否可用
        var empName = this.value;
        $.ajax({
            url:"${APP_PATH}/checkuser",
            data:"empName="+empName,
            type:"POST",
            success:function (result) {
                if(result.code==100){
                    show_validate_msg("#empName_add_input","success","用户名可用");
                    $("#emp_save_btn").attr("ajax-va","success");
                }else{
                    show_validate_msg("#empName_add_input","error",result.extend.va_msg);
                    $("#emp_save_btn").attr("ajax-va","error");
                }
            }
        });
    
    })
    
  • Controller接收/checkuser请求,去service层调用方法

    /**
     * 用于查看用户名是否重复
     * @param empName
     * @return
     */
    @ResponseBody
    @RequestMapping("/checkuser")
    public Msg checkuser(String empName){
        //先判断用户名是否是合法的表达式
        String regx = "(^[a-zA-Z0-9_-]{6,16}$)|(^[\\u2E80-\\u9FFF]{2,5})";
        if(!empName.matches(regx)){
            return Msg.fail().add("va_msg","用户必须是6-16位数字和字母的组合或者2-5位中文");
        }
    
        //数据库用户名重复校验
        boolean b = employeeService.checkUser(empName);
        if(b){
            return Msg.success();
        }else{
            return Msg.fail().add("va_msg","用户名不可用");
        }
    }
    
  • 在Service层创建一个方法,用于查看数据库中是否有该用户名

    /**
     * 校验用户名是否可用
     * @param empName
     * @return true 表示可用  false 表示不可用
     */
    public boolean checkUser(String empName) {
        EmployeeExample employeeExample = new EmployeeExample();
        EmployeeExample.Criteria criteria = employeeExample.createCriteria();
        criteria.andEmpNameEqualTo(empName);
        long count = employeeMapper.countByExample(employeeExample);
        return count == 0;//如果count == 0 表示可用
    }
    

清除表单样式

//清除表单数据(表单完整重置(表单的数据,表单的样式))
function reset_form(ele){
    $(ele)[0].reset();
    //清空表单样式
    $(ele).find("*").removeClass("has-error has-success");
    $(ele).find(".help-block").text("");
}

//点击新增按钮弹出模态框
$("#emp_add_modal_btn").click(function(){
    $("#dept_add_select").empty();

    //清除表单数据(表单重置)
    reset_form("#empAddModal form");
    // $("#empAddModal form").reset();


    //发送Ajax请求,查出部门信息,显示在下拉列表中
    getDepts();

    //弹出模态框
    $("#empAddModal").modal({
        backdrop:"static"
    });
});

问题

前端校验可以通过修改页面内容或者通过禁用js功能,跳过js校验,所以我们需要进行后端校验

重要数据(后端校验(JSR303),唯一约束)

通过修改页面内容,实现跳过前端校验

通过修改页面内容,实现跳过前端校验

使用JSR303校验

  1. 导包,导入Hibernate-Validator

    <!--JSR303校验支持:tomcat7及以上的服务器
    tomcat7及以下的服务器:el表达式。额外给服务器的lib包中替换新的标准的el
    -->
    <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
    <dependency>
      <groupId>org.hibernate.validator</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>7.0.0.Final</version>
    </dependency>
    
  2. 在bean中修改属性

    @Pattern(regexp = "(^[a-zA-Z0-9_-]{6,16}$)|(^[\\u2E80-\\u9FFF]{2,5})"
            ,message = "用户名必须是2-5位中文或者6-16位英文和数字的组合")
    private String empName;
    
    //@Email
    @Pattern(regexp = "^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$"
            ,message = "邮箱格式不正确")
    private String email;
    
  3. 对前端传来的数据进行JSR303校验

    @Valid Employee employee, BindingResult result

    /**
         * 员工保存
         * 1、支持JSR303校验
         * 2、导入Hibernate-Validator
         * @return
         */
        @ResponseBody
        @RequestMapping(value = "/emp",method = RequestMethod.POST)
        public Msg saveEmp(@Valid Employee employee, BindingResult result){
            if(result.hasErrors()){
                Map<String,Object> map = new HashMap<>();
    
                //校验失败,在模态框中显示校验失败的错误信息
                List<FieldError> fieldErrors = result.getFieldErrors();
                for (FieldError fieldError : fieldErrors){
                    System.out.println("错误的字段名:" + fieldError.getField());
                    System.out.println("错误信息:" + fieldError.getDefaultMessage());
                    map.put(fieldError.getField(),fieldError.getDefaultMessage());
                }
                return Msg.fail().add("errorFields",map);
            }else{
                employeeService.saveEmp(employee);
    //        return "lastPage";
                return Msg.success();
            }
    
        }
    
  4. 进行校验,判断是否有效

    //点击保存员工的Ajax请求
    $("#emp_save_btn").click(function(){
    
       //1、模态框中填写的表单数据提交给服务器进行保存
        //还需要对将要提交给服务器的数据进行校验
       if( !validate_add_form()){
           return false;
       }
        //判断之前的Ajax用户名校验是否成功,如果成功
        if($(this).attr("ajax-va") == "error"){
            return false;
        }
        //2、发送Ajax请求保存员工
    
        $.ajax({
            url:"${APP_PATH}/emp",
            type:"POST",
            data:$("#empAddModal form").serialize(),
            success:function (result) {
                //员工保存之后
                if(result.code == 100){
                    //1、关闭模态框
                    $("#empAddModal").modal('hide');
                    // $("#empAddModal input").empty();
                    //2、来到最后一页,显示刚才保存的数据
                    //发送Ajax请求显示最后一页数据即可
                    to_page(totalRecord);
                }else{
                    //显示失败信息
                    // console.log(result);
                    //有哪个字段的错误就显示哪个字段的
                    if(undefined != result.extend.errorFields.email){
                        //显示邮箱错误信息
                        show_validate_msg("#email_add_input","error",result.extend.errorFields.email);
                    }
                    if(undefined != result.extend.errorFields.empName){
                        //显示员工名字的错误信息
                        show_validate_msg("#empName_add_input","error",result.extend.errorFields.empName);
                    }
                }
    
    
            }
        })
    

修改员工信息

修改示意图

  1. 点击编辑
  2. 弹出用户修改的模态框(显示用户信息)
  3. 点击更新,完成用户修改
  • 点击编辑,弹出用户模态框

    //我们是按钮创建之前就绑定了click,所以绑定不上
    //1、可以在创建按钮的时候绑定 2、绑定点击.live()
    //jquery新版没有live,使用on方法替代
    $(document).on("click",".edit_btn",function(){
        // alert("edit");
        //1、查出员工信息,显示员工信息
        getDepts("#empUpdateModal select");
        //2、查出部门信息,并显示部门列表
        getEmp($(this).attr("edit-id"));
    
        //把员工id传递给模态框的更新按钮
        $("#emp_update_btn").attr("edit-id",$(this).attr("edit-id"));
    
        //3、弹出模态框
        $("#empUpdateModal").modal({
            backdrop: "static"
        });
    })
    
    //根据id查询员工数据
    function getEmp(id){
        $.ajax({
            url:"${APP_PATH}/emp/"+id,
            type:"GET",
            success:function (result) {
                // console.log(result);
                var empData = result.extend.emp;
                $("#empName_update_static").text(empData.empName);
                $("#email_update_input").val(empData.email);
                $("#empUpdateModal input[name=gender]").val([empData.gender]);
                $("#empUpdateModal select").val([empData.dId]);
            }
        });
    }
    
    • Controller根据id查询员工
    /**
     * 根据id查询员工
     * @param id
     * @return
     */
    @RequestMapping(value = "/emp/{id}",method = RequestMethod.GET)
    @ResponseBody
    public Msg getEmp(@PathVariable("id") Integer id){
        Employee employee = employeeService.getEmp(id);//
        return Msg.success().add("emp",employee);
    }
    
    • Service层从数据库查询员工
    /**
     * 安装员工ID查询员工信息
     * @param id
     * @return
     */
    public Employee getEmp(Integer id) {
        return employeeMapper.selectByPrimaryKey(id);
    }
    
  • 点击更新,更新数据

    //点击更新,更新员工数据
    $("#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)){
            show_validate_msg("#email_update_input","error","邮箱格式不正确");
            return false;
        }else {
            show_validate_msg("#email_update_input","success","");
        }
        //发送Ajax请求保存更新的员工数据
        $.ajax({
            url:"${APP_PATH}/emp/"+$(this).attr("edit-id"),
            type:"PUT",
            // data:$("#empUpdateModal form").serialize()+"&_method=PUT",//type可以用POST提交
            data:$("#empUpdateModal form").serialize(),//直接使用Ajax发送PUT请求
            success:function (result) {
                // alert(result.msg);
                //1、关闭模态框
                $("#empUpdateModal").modal("hide");
                //2、回到本页面
                to_page(currentPage);
            }
        })
    })
    
    • Controller封装前端传来的数据封装成Employee对象

      /**
       * 员工更新方法
       * 如果直接发ajax=PUT形式的请求
       * 封装的数据
       * Employee
       * [empId=1014,empName=null,gender=null,email=null,dId=null]
       *
       * 问题:
       * 请求体中有数据,但是Employee对象封装不上
       * sql拼串就发产生异常 update tbl_emp where emp_id = 1014;
       *
       * 原因:
       * Tomcat:
       *      1、将请求体中的数据,封装成一个map
       *      2、request.getParameter("empName")就会从这个map中取值
       *      3、SpringMVC封装POJO对象的时候
       *          会把POJO中每个属性的值:request.getParameter("email");
       * Ajax发送PUT请求引发的问题
       *      PUT请求,请求体中的数据,request.getParameter("empName");拿不到数据
       *      Tomcat一看是PUT,不会封装请求体中的数据为map,只有POST形式的请求才封装请求体为map
       *
       * 解决方案
       * 1、我们要能支持直接发送PUT之类的请求还要封装请求体中的数据
       * 2、配置上HttpPutFormContentFilter
       * 3、它的作用:将请求体中的数据解析包装成一个map,request被重新包装
       * request.getParameter()被重写,就会从自己封装的map中取数据
       * @param employee
       * @return
       */
      @RequestMapping(value = "/emp/{empId}",method = RequestMethod.PUT)
      @ResponseBody
      public Msg saveEmp(Employee employee){
          System.out.println("将要更新的员工数据:" + employee);
      
          employeeService.updateEmmp(employee);
          return Msg.success();
      }
      
    • Service层更新数据数据

      public void updateEmmp(Employee employee) {
          employeeMapper.updateByPrimaryKeySelective(employee);
      }
      
    • 配置HttpPutFormContentFilter

      在web.xml中进行配置

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

删除单一员工

发送/emp/id,请求类型是DELETE

//单个删除
$(document).on("click",".delete_btn",function(){
    //1、弹出是否确认删除对话框
    // alert($(this).parents("tr").find("td:eq(1)").text());//当前元素父元素的tr下的第二个td标签中的内容
    var empName = $(this).parents("tr").find("td:eq(1)").text();
    var empId = $(this).attr("del-id");
    if(confirm("确认删除【"+empName+"】吗?")){
        //确认,发送Ajax请求删除即可
        $.ajax({
            url:"${APP_PATH}/emp/" + empId,
            type:"DELETE",
            success:function (result) {
                alert(result.msg);
                //回到本页
                to_page(currentPage);
            }
        })
    }
})

Controller接收该请求,调用Service层的方法

@RequestMapping(value = "/emp/{id}",method = RequestMethod.DELETE)
@ResponseBody
public Msg deleteEmpById(@PathVariable("id") Integer id){
    employeeService.deleteEmp(id);
    return Msg.success();
}

Service层调用方法,从数据库中删除数据

/**
 * 删除员工
 * @param id
 */
public void deleteEmp(Integer id) {
    employeeMapper.deleteByPrimaryKey(id);
}

删除批量数据

全选和全不选

效果图

  • 先将表格样式改变

    <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>
    
  • 显示分页数据的时候加上checkbox,单选框这一项

    /*解析显示分页数据*/
    function build_emps_table(result){
        $.each(emps,function (index,item) {
            var checkBoxTd = $("<td><input type='checkbox' class='check_item'/></td>");
          
            //append方法执行完成以后还是返回原来的元素,这里每次append都会产生新的<tr></tr>
            $("<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是undefined
        //我们这些dom原生的属性,attr获取自定义属性的值
        //prop修改和读取dom原生属性的值
        // alert($(this).attr("checked"));
        // alert($(this).prop("checked"));
        // $(this).prop("checked");
        $(".check_item").prop("checked",$(this).prop("checked"));
    
    })
    
    //check_item,
    $(document).on("click",".check_item",function () {
        //判断当前选中的元素是否5个
        // alert($(".check_item:checked").length);
        var flag = $(".check_item:checked").length == $(".check_item").length;
    
        $("#check_all").prop("checked",flag);
    })
    
  • 点击删除按钮,删除多个员工信息

    //点击全部删除,就批量删除
    $("#emp_delete_all_btn").click(function () {
        var empNames = "";
        var del_idstr = "";
        $.each($(".check_item:checked"),function(){
            // alert($(this).parents("tr").find("td:eq(2)").text());
            //组装员工名字字符串
            empNames += $(this).parents("tr").find("td:eq(2)").text() + ",";
            //组装员工id字符串
            del_idstr += $(this).parents("tr").find("td:eq(1)").text() + "-"
        });
        empNames = empNames.substring(0,empNames.length-1);
        del_idstr = del_idstr.substring(0,del_idstr.length-1);
        if(confirm("确认删除【"+empNames+"】吗?")){
            $.ajax({
                url:"${APP_PATH}/emp/" + del_idstr,
                type:"DELETE",
                success:function (result) {
                    alert(result.msg);
                    //回到当前页面
                    to_page(currentPage);
                }
            })
        }
    });
    
  • Controller接收请求,将字符串进行区分,并进行类型转换

    /**
     * 单个批量二合一
     * 批量删除:1-2-3
     * 单个删除:1
     * @param
     * @return
     */
    @RequestMapping(value = "/emp/{ids}",method = RequestMethod.DELETE)
    @ResponseBody
    public Msg deleteEmp(@PathVariable("ids") String ids){
        if(ids.contains("-")){
            //批量删除
            String[] str_ids = ids.split("-");
            List<Integer> del_ids = new ArrayList<>();
    
            //组装id的集合
            for (String id : str_ids){
                del_ids.add(Integer.parseInt(id));
            }
            employeeService.deleteBatch(del_ids);
        }else{
            //单个删除
            Integer id = Integer.parseInt(ids);
            employeeService.deleteEmp(id);
        }
        return Msg.success();
    }
    
  • Service层进行批量删除

    /**
     * 批量删除
     * @param ids
     */
    public void deleteBatch(List<Integer> ids) {
        EmployeeExample employeeExample = new EmployeeExample();
        EmployeeExample.Criteria criteria = employeeExample.createCriteria();
        criteria.andEmpIdIn(ids);
        //delete from xxx where emp_id in (1,2,3);
        employeeMapper.deleteByExample(employeeExample);
    }
    

总结

all_btn").click(function () {
var empNames = “”;
var del_idstr = “”;
. e a c h ( .each( .each((".check_item:checked"),function(){
// alert($(this).parents(“tr”).find(“td:eq(2)”).text());
//组装员工名字字符串
empNames += $(this).parents(“tr”).find(“td:eq(2)”).text() + “,”;
//组装员工id字符串
del_idstr += $(this).parents(“tr”).find(“td:eq(1)”).text() + “-”
});
empNames = empNames.substring(0,empNames.length-1);
del_idstr = del_idstr.substring(0,del_idstr.length-1);
if(confirm(“确认删除【”+empNames+"】吗?")){
KaTeX parse error: Expected '}', got 'EOF' at end of input: … url:"{APP_PATH}/emp/" + del_idstr,
type:“DELETE”,
success:function (result) {
alert(result.msg);
//回到当前页面
to_page(currentPage);
}
})
}
});


- Controller接收请求,将字符串进行区分,并进行类型转换

```java
/**
 * 单个批量二合一
 * 批量删除:1-2-3
 * 单个删除:1
 * @param
 * @return
 */
@RequestMapping(value = "/emp/{ids}",method = RequestMethod.DELETE)
@ResponseBody
public Msg deleteEmp(@PathVariable("ids") String ids){
    if(ids.contains("-")){
        //批量删除
        String[] str_ids = ids.split("-");
        List<Integer> del_ids = new ArrayList<>();

        //组装id的集合
        for (String id : str_ids){
            del_ids.add(Integer.parseInt(id));
        }
        employeeService.deleteBatch(del_ids);
    }else{
        //单个删除
        Integer id = Integer.parseInt(ids);
        employeeService.deleteEmp(id);
    }
    return Msg.success();
}
  • Service层进行批量删除

    /**
     * 批量删除
     * @param ids
     */
    public void deleteBatch(List<Integer> ids) {
        EmployeeExample employeeExample = new EmployeeExample();
        EmployeeExample.Criteria criteria = employeeExample.createCriteria();
        criteria.andEmpIdIn(ids);
        //delete from xxx where emp_id in (1,2,3);
        employeeMapper.deleteByExample(employeeExample);
    }
    

[外链图片转存中…(img-lcUnSKjP-1611281390431)]

至此,SSM整合结束

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值