SSM_CRUD练习项目

文章目录

功能点

  1. 分页功能
  2. 新增和修改时的数据校验(前端JQuery校验+后端JSR303)
  3. Ajax异步请求
  4. Rest风格的URI(使用不同http协议的请求方式表示对数据的不同操作。GET(查询)、POST(新增)、PUT(修改)、DELETE(删除))

环境与技术

电脑环境:macOS
电脑软件:IDEA、Navicat Premium
基础框架:ssm(Spring+SpringMVC+MyBatis)
前端框架:bootstrap
数据库:MySQL8.0.18
服务器:Tomcat8.5.51
依赖管理:Maven3.5.4
分页工具:pagehelper
逆向工程:MyBatis Generator

最终的效果图

在这里插入图片描述
实现的功能:员工信息的新增、修改、单个和多个删除。

项目地址:https://github.com/angenin/ssm_crud/


搭建基本环境


创建maven项目

创建一个maven项目,选用webapp模板,文件名为ssm_crud。
在这里插入图片描述

添加基本目录

在src下新建test目录,test下再新建java和resources目录,在main目录下也添加java和resources目录。
在这里插入图片描述

引入依赖

  • 在pop.xml中先引入基本的依赖的jar包
  <!--  引入依赖的jar包  -->
  <dependencies>

<!--    SpringMVC-->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>

<!--    Spring jdbc-->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>

<!--    Spring 面向切面编程-->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.4.RELEASE</version>
    </dependency>

<!--    MyBatis-->
    <!-- https://mvnrepository.com/artifact/org.mybatis/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.2</version>
    </dependency>

<!--    数据库连接池-->
    <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
    <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.5.2</version>
    </dependency>

<!--    数据库驱动-->
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.18</version>
    </dependency>

<!--      jsp-->
      <!-- https://mvnrepository.com/artifact/javax.servlet/jsp-api -->
      <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>jsp-api</artifactId>
          <version>2.0</version>
          <scope>provided</scope>
      </dependency>
      
    <!-- 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>4.0.1</version>
        <scope>provided</scope>
    </dependency>

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

配置tomcat服务器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

引入bootstrap前端框架

下载

进入官网下载:https://www.bootcss.com/
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

引入

在项目的webapp目录下新建static目录,并把下载解压好的文件放入,再在static目录下新建一个js目录,用于存放JQuery文件。
- 在index.jsp中的head标签里写入
html <script type="text/javascript" src="static/js/jquery-3.4.1.min.js"></script> <link href="static/bootstrap-dist/css/bootstrap.min.css" rel="stylesheet"/> <script src="static/bootstrap-dist/js/bootstrap.min.js"></script>

  • 也可以直接在index.jsp的head标签里引入下面这两个标签。
    在这里插入图片描述
    在这里插入图片描述
应用测试
  • 使用bootstrap的样式,点击官网的全局CSS样式。
    在这里插入图片描述
    这里的样式都可以选择使用,我们用按钮来测试效果。
    在这里插入图片描述
    复制到index.jsp中,然后启动。
    在这里插入图片描述
    在这里插入图片描述
    前端的bootstrap的框架就引入成功了。
    在这里插入图片描述

创建数据库

create database ssm_crud;

use ssm_crud;

create table tbl_dept(
	dept_id int(11) PRIMARY KEY auto_increment,
	dept_name varchar(255) not null
);

create table tbl_emp(
	emp_id int(11) PRIMARY KEY auto_increment,
	emp_name varchar(255) not null,
	gender char(1),	
	email varchar(255) not null,
	d_id int(11),
	FOREIGN KEY(d_id) REFERENCES tbl_dept(dept_id)
);


编写ssm整合的关键配置文件。

在main下的java目录里新建以下目录:
在这里插入图片描述

在main目录下的resources目录添加Spring的配置文件,名为applicationContext.xml。
在这里插入图片描述
在main目录下的resources目录添加三个file文件,名为jdbcConfig.properties、springmvc.xml和mybatis-config.xml。
在这里插入图片描述
在resources目录下添加mapper目录。
在WEB-INF目录下新建一个名为views的目录。
在WEB-INF目录下新建一个名为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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>
  • 配置webapp/WEB-INF/web.xml文件
	<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
         http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
  <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>
<!--    不指定springmvc配置文件的路径,必须在WEB—INF目录下
        新建一个与前端控制器同名并加上-Servlet(即dispatcherServlet-Servlet)
        的spring配置文件作为springmvc的配置文件-->
    <load-on-startup>1</load-on-startup>
  </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>
  • 配置resources/springmvc.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:ontext="http://www.springframework.org/schema/context"
       xsi:schemaLocation=" http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

<!--    SpringMVC的配置文件,包含页面跳转逻辑的控制,配置-->
    <ontext:component-scan base-package="com.angenin" use-default-filters="false">
<!--        只扫描加了Controller的控制器类-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </ontext:component-scan>
<!--    另一种写法-->
<!--    <context:component-scan base-package="com.angenin.crud.controller"/>-->

    <!--    配置视图解析器,方便页面返回-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--        路径:最前面/表示在webapp目录下)-->
        <property name="prefix" value="/WEB-INF/views/"/>
        <!--        后缀-->
        <property name="suffix" value=".jsp"/>
    </bean>

<!--    两个标准配置-->
<!--    将springmvc不能处理的请求交给tomcat-->
    <mvc:default-servlet-handler/>
    <!--    能支持springmvc更高级的一些功能,JSR303校验,快捷的ajax,映射动态请求等等-->
    <mvc:annotation-driven/>

</beans>
  • 配置resources/jdbcConfig.properties文件
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/ssm_crud
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.user=root
jdbc.password=123456
  • 配置resources/applicationContext.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.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.angenin">
<!--        不扫描加了Controller注解的控制器类-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    
    <context:property-placeholder location="classpath:jdbcconfig.properties"/>
    <bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
        <property name="driverClass" value="${jdbc.driverClass}"/>
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

<!--==============================================================-->

<!--    配置和MyBatis的整合-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--        指定mybatis全局配置文件的位置-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--        数据源-->
        <property name="dataSource" ref="pooledDataSource"/>
<!--        -->
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
    </bean>
    
<!--    配置扫描器,将mybatis接口的实现加入ioc容器中-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--        扫描所有dao接口的实现,加入到ioc容器中-->
        <property name="basePackage" value="com.angenin.crud.dao"/>
    </bean>

<!--==============================================================-->

<!--    事务控制的配置-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--        控制住数据源-->
        <property name="dataSource" ref="pooledDataSource"/>
    </bean>

<!--    1.开启基于注解的事务;2.使用xml配置形式的事务(必要主要的都是使用配置式)-->
    <aop:config>
<!--        切入点表达式-->
        <aop:pointcut id="txPoint" expression="execution(* com.angenin.crud.service..*(..))"/>
<!--        配置事务增强-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
    </aop:config>

<!--    配置事务增强,事务如何切入-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
<!--            所有方法都是事务方法-->
            <tx:method name="*"/>
<!--            小范围优先-->
<!--            以get开头的所有方法,只读-->
            <tx:method name="get*" read-only="true"/>
        </tx:attributes>
    </tx:advice>
</beans>
  • 配置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.angenin.crud.bean"/>
    </typeAliases>

</configuration>

使用mybatis的逆向工程生成对应的bean、dao和mapper

在pop.xml中的<dependencies></dependencies>里引入

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

然后在resources目录下新建generatorConfig.xml文件,并写入
(在<javaModelGenerator><sqlMapGenerator><javaClientGenerator>标签里的targetProject属性,因为我是macOS,所有用/,如果是Windows系统,需要把/改为\。)

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

    <!--导入属性配置-->
    <properties resource="jdbcConfig.properties"/>

    <!-- context 是逆向工程的主要配置信息 -->
    <!-- id:名字,targetRuntime:设置生成的文件适用的mybatis版本 -->
    <context id="mbg" targetRuntime="MyBatis3">

        <!-- 生成注释为false 不生成为true 【不生成注释时,重复生成会被重复写入而导致报错】 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!--jdbc的数据库连接-->
        <jdbcConnection connectionURL="${jdbc.jdbcUrl}"
                        driverClass="${jdbc.driverClass}"
                        userId="${jdbc.user}"
                        password="${jdbc.password}"/>

        <!--类型处理器,在数据库类型和java类型之间的转换控制-->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

<!--        指定javaBean生成的位置-->
        <javaModelGenerator targetPackage="com.angenin.crud.bean"
                            targetProject="./src/main/java">
            <!-- 是否允许子包 -->
            <property name="enableSubPackages" value="true" />
            <!-- 是否清理从数据库中查询出的字符串左右两边的空白字符 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

<!--        指定sql映射文件生成的位置-->
<!--        targetProject:为文件的硬盘路径  ./为当前工程下(也可以写绝对路径)-->
        <sqlMapGenerator targetPackage="mappers"
                         targetProject="./src/main/resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>

<!--        指定dao接口生成的位置,mapper接口-->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.angenin.crud.dao"
                             targetProject="./src/main/java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>


<!--        table指定每个表的生成策略-->
<!--        tableName:要生成的表,domainObjectName:表生成对应类名-->
        <table tableName="tbl_emp" domainObjectName="Employee"/>
        <table tableName="tbl_dept" domainObjectName="Department"/>
    </context>
</generatorConfiguration>

在test/java目录下新建MBGTest.java,写入

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("src/main/resources/generatorConfig.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);
    }
}

运行后生成对应的bean、dao和mapper文件。
在这里插入图片描述

增添查询员工信息时带上部门信息的业务

在dao/EmployeeMapper.java中添加:

	//  因为逆向工程生成的查询只生成d_id,没有dept表的信息,所以自定义两个查询方法。
	//  自定义,查询时带上dept表里信息
    List<Employee> selectByExampleWithDept(EmployeeExample example);
    Employee selectByPrimaryKeyWithDept(Integer empId);

然后在bean/Employee.java里添加:

	private Department department;

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }

最后往resources/mappers/EmployeeMapper.xml中添加:

<!--  自定义返回集合-->
  <resultMap id="WithDeptResultMap" type="com.angenin.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.angenin.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>
  
  <!--  自定义查询,查询员工同时带上部门信息-->
  <select id="selectByExampleWithDept" parameterType="com.angenin.crud.bean.EmployeeExample" 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" parameterType="java.lang.Integer" 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>

测试CRUD功能

  • 在pop.xml中导入spring的单元测试依赖。
<!--      spring单元测试-->
      <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-test</artifactId>
          <version>5.2.4.RELEASE</version>
          <scope>test</scope>
      </dependency>
  • 为bean下的Department.java和Employee.java添加有参和无参构造器。
//Department
	public Department() {
    }

    public Department(Integer deptId, String deptName) {
        this.deptId = deptId;
        this.deptName = deptName;
    }

//==========================
//Employee
	public Employee() {
    }

    public Employee(Integer empId, String empName, String gender, String email, Integer dId) {
        this.empId = empId;
        this.empName = empName;
        this.gender = gender;
        this.email = email;
        this.dId = dId;
    }
  • 在resources/applicationContext.xml中加入一个可批量插入的sqlSession。
<!--    配置一个可以执行批量插入的sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
        <constructor-arg name="executorType" value="BATCH"/>
    </bean>
  • 在test/java下新建一个MapperTest.java,并写入:
import com.angenin.crud.bean.Department;
import com.angenin.crud.bean.Employee;
import com.angenin.crud.dao.DepartmentMapper;
import com.angenin.crud.dao.EmployeeMapper;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.UUID;

/*测试DepartmentMapper
 * 推荐Spring的项目使用Spring的单元测试,可以自动注入我们需要的组件
 * 1.导入SpringTest模块
 * 2.@ContextConfiguration(locations = {"classpath:applicationContext.xml"})   指定spring配置文件的位置
 */
// - @RunWith(SpringJUnit4ClassRunner.class)指定Test用什么单元测试来运行
// - 如果出现`ExceptionInInitializerError`错误,
//   是因为`SpringJUnit4ClassRunner`要求`JUnit 4.12`或更高,
//   在pop.xml中把junit版本调高即可。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class MapperTest {

    //3.直接autowired要使用的组件即可
    @Autowired
    DepartmentMapper departmentMapper;
    @Autowired
    EmployeeMapper employeeMapper;
    @Autowired
    SqlSession sqlSession;

    @Test
    public void testCRUD(){
//        1.插入几个部门
        departmentMapper.insertSelective(new Department(null, "开发部"));
        departmentMapper.insertSelective(new Department(null, "测试部"));
        departmentMapper.insertSelective(new Department(null, "运维部"));

//        2.生成员工数据,测试员工表的插入
        employeeMapper.insertSelective(new Employee(null, "张三", "M", "zhangsan@qq.com", 1));

//        3.批量插入多个员工;使用可以批量插入操作的sqlSession
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        for (int i = 0; i < 1000; i++) {
            String uid = UUID.randomUUID().toString().substring(0, 5) + i;
            employeeMapper.insertSelective(new Employee(null, uid, "M", uid + "@qq.com", 1));
        }
    }
}

在这里插入图片描述
在这里插入图片描述


CRUD-查询


步骤

1、访问 index.jsp页面
2、index.jsp页面发送出查询员工列表请求
3、EmployeeController来接受请求,查出员工数据
4、来到list.jsp页面进行展示


1. index.jsp只用于跳转到查询页面,所以只留以下内容。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<jsp:forward page="/emps"/>
2. 在pop.xml中引入分页插件。
<!--      引入PageHelper分页插件-->
      <dependency>
          <groupId>com.github.pagehelper</groupId>
          <artifactId>pagehelper</artifactId>
          <version>5.1.8</version>
      </dependency>
3. 在mybatis-config.xml中注册分页插件。
<!--    注册PageInterceptor分页插件-->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
4. 然后在controller下新建一个EmployeeController.java,用于处理员工CRUD请求。
package com.angenin.crud.controller;

import com.angenin.crud.bean.Employee;
import com.angenin.crud.service.EmployeeService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
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;

    /**
     * 查询员工数据(分页显示)
     * @param pn 页码
     * @param model
     * @return
     */
    @RequestMapping("/emps")
    public String getEmps(@RequestParam(value="pn", defaultValue = "1")Integer pn, Model model){

        //引入PageHelper分页插件
        //查询前调用分页插件,之后的查询就是分页查询(页码和每页显示的数据数)
        //第三个参数:emp_id asc 为指定排序的规则,按emp_id正序排序(不加后面插入数据后排序可能有问题)
        PageHelper.startPage(pn, 5, "emp_id asc");
        List<Employee> emps = employeeService.getAll();

        //使用pageInfo包装查询后的结果,传入查询结果和连续显示的页数
        PageInfo page = new PageInfo(emps, 5);
        //把page传到下个页面
        model.addAttribute("pageInfo", page);

//        页面跳转到WEB-INF/views下的list.jsp页面(springMVC的视图解析器)
        return "list";
    }
}
5. 在service下新建一个EmployeeService.java
package com.angenin.crud.service;

import com.angenin.crud.bean.Employee;
import com.angenin.crud.dao.EmployeeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;

    /**
     * 查询所有员工
     * @return
     */
    public List<Employee> getAll() {
        return employeeMapper.selectByExampleWithDept(null);
    }
}
6. 测试分页查询的数据

在test/java下新建一个MvcTest.java用于测试。

import com.angenin.crud.bean.Employee;
import com.github.pagehelper.PageInfo;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import java.util.List;


/**
 * 使用Spring测试模块提供的测试请求功能,测试crud请求的正确性
 */
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration    //获取springmvc的ioc容器
@ContextConfiguration(locations = {"classpath:applicationContext.xml", "classpath:springmvc.xml"})
public class MvcTest {

    //传入SpringMVC的IOC
    @Autowired
    WebApplicationContext context;

    //虚拟mvc请求,获取到处理结果
    MockMvc mockMvc;

    @Before     //使用junit的Before
    public void initMockMvc(){
        mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
    }

    @Test
    public void testPage() throws Exception {
        //模拟发送get请求     param为请求传入的键值对
        //andReturn为获取返回值
        MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/emps").param("pn", "1")).andReturn();

        //请求成功以后,请求域中会有pageInfo
        MockHttpServletRequest request = result.getRequest();
        PageInfo pi = (PageInfo) request.getAttribute("pageInfo");

        System.out.println("当前页码:" + pi.getPageNum());
        System.out.println("总页码:" + pi.getPages());
        System.out.println("总记录数:" + pi.getTotal());
        System.out.print("在页面需要连续显示的页码:");
        int[] nums = pi.getNavigatepageNums();
        for (int num : nums) {
            System.out.print(" " + num);
        }
        System.out.println();

        //获取员工数据
        List<Employee> list = pi.getList();
        for (Employee employee : list) {
            System.out.println("ID: " + employee.getEmpId() + ",name: " + employee.getEmpName());
        }
    }
}

测试效果:
在这里插入图片描述
把传入的键值对改为5,也没有问题。
在这里插入图片描述

7. 在webapp/WEB-INF/views里新建一个list.jsp页面,为员工列表页面。

编写list.jsp。

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入标签核心库--%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <title>员工列表</title>

    <%
        //获取当前项目名(得到的是 /加项目名(例:/ssm_crud) ,所以使用的时候前面不用加/而后面需要)
        pageContext.setAttribute("APP_PATH", request.getContextPath());
    %>
<%-- web的路径:
       - 不以/开始的相对路径,找资源,以当前资源的路径为基准,经常出问题。
       - 以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:3306)再需要加上项目名(项目名也不是写死的)
            http://localhost:8080/ssm_crud
 --%>
    <script type="text/javascript" src="${ APP_PATH }/static/js/jquery-3.4.1.min.js"></script>
    <link href="${ APP_PATH }/static/bootstrap-dist/css/bootstrap.min.css" rel="stylesheet"/>
    <script src="${ APP_PATH }/static/bootstrap-dist/js/bootstrap.min.js"></script>
</head>
<body>
<%--    搭建显示页面(官方的全局CSS样式的栅格系统) --%>
    <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 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>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 == "M" ? "男" : "女" }</td>
                            <td>${ emp.email }</td>
                            <td>${ emp.department.deptName }</td>
                            <td>
                                <button class="btn btn-info 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>
                            </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?pn=1">首页</a></li>
                        <%--如果有上一页--%>
                        <c:if test="${ pageInfo.hasPreviousPage }">
                            <%--上一页--%>
                            <li>
                                <a href="${ APP_PATH }/emps?pn=${ pageInfo.pageNum - 1 }" aria-label="Previous">
                                    <span aria-hidden="true">&laquo;</span>
                                </a>
                            </li>
                        </c:if>
                        <%--显示五页--%>
                        <c:forEach items="${ pageInfo.navigatepageNums }" var="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>
效果图

实现了数据的查询和分页以及分页的跳转功能。
在这里插入图片描述
但只能适用于PC浏览器和服务器的交互,实际上发送请求的可能是安卓或iOS,所以这里我们把数据用josn的格式用ajax发送给客户端。


查询-ajax


步骤

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


1. 注释掉EmployeeController.java中的getEmps方法,新建一个getEmpsWithJson方法。
	/**
     * 查询员工数据(分页显示)
     * json和ajax
     * 导入jackson包
     * @param pn 页码
     * @return
     */
    @RequestMapping("/emps")
    @ResponseBody
    public PageInfo getEmpsWithJson(@RequestParam(value="pn", defaultValue = "1")Integer pn){
        //引入PageHelper分页插件
        //查询前调用分页插件,之后的查询就是分页查询(页码和每页显示的数据数)
        //第三个参数:emp_id asc 为指定排序的规则,按emp_id正序排序(不加后面插入数据后排序可能有问题)
        PageHelper.startPage(pn, 5, "emp_id asc");
        List<Employee> emps = employeeService.getAll();

        //使用pageInfo包装查询后的结果,传入查询结果和连续显示的页数
        PageInfo page = new PageInfo(emps, 5);

        return page;
    }
2. 在pop.xml中引入json的依赖
<!--      返回json字符串的支持-->
      <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
      <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
          <version>2.10.2</version>
      </dependency>

启动后页面显示json格式的数据。
在这里插入图片描述
因为PageInfo返回到页面不清楚是成功还是失败,所以我们还需要在bean中定义一个提示信息的类。

3. 在bean中定义一个Msg.java用于请求的结果进行处理和返回
package com.angenin.crud.bean;

import java.util.HashMap;
import java.util.Map;

public class Msg {

    //状态码   100:成功;200:失败
    private int code;
    //提示信息
    private String msg;

    //用户要返回给浏览器的数据
    private Map<String, Object> extend = new HashMap<>();

    //请求成功方法
    public static Msg success(){
        Msg result = new Msg();
        result.setCode(100);
        result.setMsg("处理成功!");
        return result;
    }

    //请求失败方法
    public static Msg fail(){
        Msg result = new Msg();
        result.setCode(200);
        result.setMsg("处理失败!");
        return result;
    }

    //添加信息的方法
    public Msg add(String key, Object value){
        this.getExtend().put(key, value);
        return this;
    }


    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Map<String, Object> getExtend() {
        return extend;
    }

    public void setExtend(Map<String, Object> extend) {
        this.extend = extend;
    }
}

并修改getEmpsWithJson方法。

public Msg getEmpsWithJson(@RequestParam(value="pn", defaultValue = "1")Integer pn){
        //引入PageHelper分页插件
        //查询前调用分页插件,之后的查询就是分页查询(页码和每页显示的数据数)
        //第三个参数:emp_id asc 为指定排序的规则,按emp_id正序排序(不加后面插入数据后排序可能有问题)
        PageHelper.startPage(pn, 5, "emp_id asc");
        List<Employee> emps = employeeService.getAll();

        //使用pageInfo包装查询后的结果,传入查询结果和连续显示的页数
        PageInfo page = new PageInfo(emps, 5);

        return Msg.success().add("pageInfo", page);
    }

重新运行
在这里插入图片描述

4. 在mybatis-config.xml中给配置分页插件配置一个参数
!--    注册PageInterceptor分页插件-->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--            新增属性:reasonable分页的合理化参数
                            pageNum<=0,而查询第一页,如果pageNum>pages,而查询最后一页-->
            <property name="reasonable" value="true"/>
        </plugin>
    </plugins>
5. 舍弃list.jsp,重写index.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入标签核心库--%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <title>员工列表</title>

    <%
        //获取当前项目名(得到的是 /加项目名(例:/ssm_crud) ,所以使用的时候前面不用加/而后面需要)
        pageContext.setAttribute("APP_PATH", request.getContextPath());
    %>
    <%-- web的路径:
           - 不以/开始的相对路径,找资源,以当前资源的路径为基准,经常出问题。
           - 以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:3306)再需要加上项目名(项目名也不是写死的)
                http://localhost:8080/ssm_crud
     --%>
    <script type="text/javascript" src="${ APP_PATH }/static/js/jquery-3.4.1.min.js"></script>
    <%--    下载的bootstrap前端框架有问题,用第二种方式引入--%>
    <%--    <link href="${ APP_PATH }/static/bootstrap-dist/css/bootstrap.min.css" rel="stylesheet"/>--%>
    <%--    <script src="${ APP_PATH }/static/bootstrap-dist/js/bootstrap.min.js"></script>--%>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
    <%--    搭建显示页面(官方的全局CSS样式的栅格系统) --%>
    <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 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>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">
        //1.页面加载完成后,发送一个ajax请求,要到分页数据
        $(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.解析并显示分页信息
                    build_page_info(result);
                    //3.解析并显示分页条数据
                    build_page_nav(result);
                }
            });
        }

        //解析显示员工信息
        function build_emps_table(result) {
            //每次页面跳转都先清空原先的员工数据
            $("#emps_table tbody").empty();

            var emps = result.extend.pageInfo.list;
            //jquery自带的遍历
            $.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 edit_btn")
                        .append($("<span></span>").addClass("glyphicon glyphicon-pencil"))
                        .append("编辑");
                var delBtn = $("<button></button>").addClass("btn btn-danger btn-sm delete_btn")
                        .append($("<span></span>").addClass("glyphicon glyphicon-trash"))
                        .append("删除");
                var btnTd = $("<td></td>").append(editBtn).append(" ").append(delBtn);
                //把相同员工的单元格添加到同一行
                $("<tr></tr>").append(empIdTd)
                    .append(empNameTd)
                    .append(genderTd)
                    .append(emailTd)
                    .append(deptNameTd)
                    .append(btnTd)
                    .appendTo("#emps_table tbody"); //添加到id为emps_table的表里的tbody中
            });
        }

        //解析显示分页信息
        function build_page_info(result) {
            //每次页面跳转都先清空分页信息
            $("#page_info_area").empty();

            $("#page_info_area").append("当前 " + result.extend.pageInfo.pageNum + " 页," +
                "总 " + result.extend.pageInfo.pages  + " 页," +
                "总 " + result.extend.pageInfo.total + " 条记录");
        }

        //解析显示分页条
        function build_page_nav(result) {
            //每次页面跳转都先清空table表格数据
            $("#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){
                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;").attr("href", "#"));
            //尾页
            var lastPageLi = $("<li></li>").append($("<a></a>").append("尾页").attr("href", "#"));
            //如果没有下一页,给后一页和尾页按钮增加不能点击的效果
            if(!result.extend.pageInfo.hasNextPage){
                nextPageLi.addClass("disabled");
                lastPageLi.addClass("disabled");
            }else{
                //如果有下一页
                //为下一页添加点击翻页事件
                nextPageLi.click(function () {
                    to_page(result.extend.pageInfo.pageNum + 1);
                });
                //为尾页页添加点击翻页事件
                lastPageLi.click(function () {
                    to_page(result.extend.pageInfo.pages);
                });
            }

            //添加首页
            ul.append(firstPageLi).append(prePageLi);

            //5个连续显示的页码
            $.each(result.extend.pageInfo.navigatepageNums, function (index, item) {
                var numLi = $("<li></li>").append($("<a></a>").append(item).attr("href", "#"));
                //如果循环到的页码是当前页码
                if(item == result.extend.pageInfo.pageNum){
                    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).appendTo("#page_nav_area");
        }

    </script>
</body>
</html>
效果图

实现效果与第一种做法一致。
在这里插入图片描述


CRUD-新增


步骤

1、在 index.jsp页面点击”新增”按钮
2、弹出增对话框(bootstrap框架js插件的模态框里的动态实例)
3、到数据库查询部门列表,显示在对话框中
4、用户输入数据后进行校验(jquery前端校验,ajax用户名重复校验,重要数据(后端校验JSR303),唯一约束)
5、完成保存


1. 为index.jsp的新增按钮增加弹窗效果

使用bootstrap官网js插件的模态框。
在这里插入图片描述

  1. 为新增按钮添加一个id,顺便为删除按钮也添加一个id。
...
<%--  按钮--%>
        <div class="row">
            <div class="col-md-4 col-md-offset-8">
                <button class="btn btn-primary" id="emp_add_modal_btn">新 增</button>
                <button class="btn btn-danger" id="emp_delete_modal_btn">删 除</button>
            </div>
        </div>
        <%--  显示表格数据--%>	
        ...
  1. 在index.jsp的body里添加模态框代码。
<%--    员工添加按钮的模态框   --%>
    <div class="modal fade" id="empAddModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                    <%--       弹窗标题             --%>
                    <h4 class="modal-title" id="myModalLabel">员工添加</h4>
                </div>
                <%--   弹窗内容     --%>
                <div class="modal-body">
                    <%--      表单    --%>
                    <%--      empName    --%>
                    <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_update_static"></p>
                            </div>
                        </div>
                        <%--      email    --%>
                        <div class="form-group">
                            <label 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">
                            </div>
                        </div>
                        <%--      gender    --%>
                        <div class="form-group">
                            <label class="col-sm-2 control-label">email</label>
                            <div class="col-sm-10">
                                <label class="radio-inline">
                                    <input type="radio" name="gender" id="gender1_add_input" value="M" checked></label>
                                <label class="radio-inline">
                                    <input type="radio" name="gender" id="gender2_add_input" value="F"></label>
                            </div>
                        </div>
                        <%--      deptName    --%>
                        <div class="form-group">
                            <label class="col-sm-2 control-label">deptName</label>
                            <div class="col-sm-4">
                                <%--      提交部门id即可    --%>
                                <select class="form-control" name="dId"></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">保存</button>
                </div>
            </div>
        </div>
    </div>
  1. 在Service包中创建一个名为DepartmentService.java的类,用于处理DepartmentController.java发送过来的业务。
package com.angenin.crud.service;

import com.angenin.crud.bean.Department;
import com.angenin.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() {
//        查出所有部门信息并返回给对应的controller
        return departmentMapper.selectByExample(null);
    }
}
  1. 在Controller包中新建一个名为DepartmentController.java的控制器,用于处理和部门有关的请求,在里面新建一个名为的方法,用于返回查询到的所有部门信息。
package com.angenin.crud.controller;

import com.angenin.crud.bean.Department;
import com.angenin.crud.bean.Msg;
import com.angenin.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);
    }
}
  1. 在index.jsp页面底部的script中为新增按钮添加点击弹窗事件。
		//新增按钮的点击弹窗事件
        $("#emp_add_modal_btn").click(function () {
            //发送ajax请求,查出部门信息,显示在下拉列表中
            getDepts("#empAddModal select");
            //调用模态框
            $("#empAddModal").modal({
                backdrop: "static"
            });
        });

        //查出所有部门信息并显示在下拉列表中
        function getDepts(ele){
        	//清空下拉列表
            $(ele).empty();
            $.ajax({
                url: "${APP_PATH}/depts",
                type: "get",
                success: function (result) {
                    //显示部门信息到下拉列表中
                    $.each(result.extend.depts, function () {  //不传参使用this代表当前被遍历的对象
                        var optionEle = $("<option></option>").append(this.deptName)
                            .attr("value", this.deptId).appendTo("ele");
                    });
                }
            });
        }
效果图

在这里插入图片描述

2. 编写新建按钮的弹窗的保存按钮的保存效果
  1. 在EmployeeService.java中添加一个名为saveEmp的方法,用于保存新增的员工信息。
	/**
     * 员工保存方法
     * @param employee
     */
    public void saveEmp(Employee employee) {
        //因为我们的员工id是自增的,所有使用insertSelective有选择的插入,而不是insert。
        employeeMapper.insertSelective(employee);
    }
  1. 在EmployeeController.java中新增一个名为saveEmp的方法,用于保存新增的员工信息。
    /**
     * 保存新增的员工信息
     * Rest风格的URI,我们规定post的请求为保存请求
     */
    @RequestMapping(value = "/emp", method = RequestMethod.POST)
    @ResponseBody
    public Msg saveEmp(Employee employee){  //接收页面表单传递过来的employee对象信息
        employeeService.saveEmp(employee);
        return Msg.success();
    }
  1. 在index.jsp页面中为弹窗的保存按钮添加一个id=“emp_save_btn”。

  2. 在index.jsp页面底部的script中添加一个全局变量var totalRecord;,为保存后的页面跳转做准备,此全局变量在分页信息那赋值。


   //定义一个全局变量:总记录数和当前页(在分页信息那赋值)
   var totalRecord, currentNum;

//----------------------------

	function build_page_info(result) {
		...
		//为全局变量赋值
		totalRecord = result.extend.pageInfo.total;
		currentNum = result.extend.pageInfo.pageNum;
	}
  1. 在index.jsp页面底部的script中为保存按钮添加点击保存事件。
		//弹窗的保存按钮的点击保存事件
        $("#emp_save_btn").click(function () {
            //调用JQuery的serialize,系列化表单里的员工信息
            //系列化后的字符串:empName=cat&email=cat%40qq.com&gender=F&dId=3
            // alert($("#empAddModal form").serialize());

            //发送ajax请求把保存表单内新增的员工信息提交给服务器
            $.ajax({
                url: "${APP_PATH}/emp",
                type: "post",
                data: $("#empAddModal form").serialize(),
                success: function(result){
                    // alert(result.msg);
                    //当保存成功:
                    //1.关闭弹窗
                    $("#empAddModal").modal('hide');
                    //2.跳转到最后一页
                    // (因为我们已经添加了bootstrap的合理化参数,所以当超过总页数将会跳转到最后一页)
                    to_page(totalRecord);
                }
            });
        });
效果图

在这里插入图片描述
在这里插入图片描述

3. 问弹窗表单增加校验功能

提交的数据需要满足才可保存:

  • 用户名合法
  • 邮箱合法
  • 员工姓名不重复(只是示例,练习,可把员工姓名看成用户名就不会觉得逻辑有问题了)
校验员工姓名和邮箱
  1. 在弹窗的保存按钮的点击保存事件的中最上面添加,先进行数据的校验。
	//对提交的数据进行校验
    if(!validate_add_form()){
         return false;
     }
  1. 在script标签中新建一个validate_add_form的方法,用于数据的校验。
        //校验表单数据
        function validate_add_form(){
            //拿到数据,用jQuery的正则表达式进行校验
            //校验员工姓名
            var empName = $("#empName_add_input").val();
            //正则:允许(6到16位,允许a-z A-Z 0-9 _ - 2到5个中文)
            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;
        }

效果:
在这里插入图片描述

  1. 使用bootstrap官网全局css样式美化错误提醒。
    在这里插入图片描述

    1. 在body标签中,弹窗代码内的表单的empName和email位置的<input type="text" .../>下添加<span class="help-block"></span>,用于错误时显示的提示信息。
    2. 新建一个show_validate_msg方法,用于校验的提示信息。
    //校验的提示信息
        //ele校验的元素,status校验状态,msg提示信息
        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. 修改js中校验表单数据方法
    //校验表单数据
        function validate_add_form(){
            //拿到数据,用jQuery的正则表达式进行校验
            //校验员工姓名
            var empName = $("#empName_add_input").val();
            //正则:允许(6到16位,允许a-z A-Z 0-9 _ - 2到5个中文)
            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个英文和数字的组合!");
    
                return false;
            }else{
                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("邮箱格式不正确!");
                //显示校验的信息
                show_validate_msg("#email_add_input", "error", "邮箱格式不正确!");
    
                return false;
            }else{
                show_validate_msg("#email_add_input", "success", "");
            }
    
            //校验通过
            return true;
        }
    
  2. 为了给用户更好的体验效果,我们再给输入文本框增加一个失去焦点事件,让用户输入完文本后,就发起校验,而不用等到去点击保存才提示,顺便解决了用户名和邮箱都不合法,只有用户名提示错误信息。(当然保存的校验也要保留)

    1. 把validate_add_form方法拆为validate_add_form_empName和validate_add_form_email两个方法。
    //校验表单员工名
        function validate_add_form_empName(ele){
            //拿到数据,用jQuery的正则表达式进行校验
            //校验员工姓名
            var empName = $(ele).val();
            //正则:允许(6到16位,允许a-z A-Z 0-9 _ - 2到5个中文)
            var regName = /(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/;
            if(!regName.test(empName)){
                // alert("用户名可以是2-5位中文或6-16个英文和数字的组合!");
                //显示校验的信息
                show_validate_msg(ele, "error", "用户名可以是2-5位中文或6-16个英文和数字的组合!");
    
                return false;
            }else{
                show_validate_msg(ele, "success", "");
            }
            //校验通过
            return true;
        }
        //校验表单邮箱
        function validate_add_form_email(ele) {
            //校验邮箱
            var email = $(ele).val();
            var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
            if(!regEmail.test(email)){
                // alert("邮箱格式不正确!");
                //显示校验的信息
                show_validate_msg(ele, "error", "邮箱格式不正确!");
    
                return false;
            }else{
                show_validate_msg(ele, "success", "");
            }
            //校验通过
            return true;
        }
    
    1. 把弹窗的保存按钮的点击保存事件中的判断稍做修改。
    //弹窗的保存按钮的点击保存事件
    $("#emp_save_btn").click(function () {
          //对提交的数据进行校验
          if(!validate_add_form_empName("#empName_add_input") || !validate_add_form_email("#email_add_input")){
        		alert("请正确输入信息!");
              return false;
          }
    
    1. 在js中新建两个文本框的失去焦点事件。
        //员工名输入框失去焦点校验事件(弹窗)
        $("#empName_add_input").blur(function () {
            validate_add_form_empName("#empName_add_input");
        });
        //邮箱输入框失去焦点校验事件(弹窗)
        $("#email_add_input").blur(function () {
            validate_add_form_email("#email_add_input");
        });
    
    1. 在新增按钮的点击弹窗事件中增加表单重置功能,每次打开都是初始状态。
    //清除表单数据(表单重置)
    //清空文本框内容
    $("#empAddModal form")[0].reset();
    //去除表单样式
    $("#empAddModal form").find("*").removeClass("has-success has-error");
    //清空有带有help-block类的内容(即错误信息)
    $("#empAddModal form").find(".help-block").text("");
    
校验员工姓名是否重复
  1. 在DepartmentExample.java中新建一个查询用户名是否可用的方法。
	/**
     * 查询用户名是否已存在
     * @param empName
     * @return true为可用(输入的用户名不存在)
     */
    public boolean checkUser(String empName) {

        //查询条件
        EmployeeExample example = new EmployeeExample();
        EmployeeExample.Criteria criteria = example.createCriteria();
        //查询数据库中emp_name和传参empName相同的记录数
        criteria.andEmpNameEqualTo(empName);

        //countByExample方法返回符合条件的记录数
        long count = employeeMapper.countByExample(example);
        return count == 0;
    }
  1. 在EmployeeController.java中新建方法。
	/**
     * 校验用户名是否已存在
     */
    @RequestMapping("/checkuser")
    @ResponseBody
    public Msg checkuser(@RequestParam("empName")String empName){
        boolean b = employeeService.checkUser(empName);
        if(b){
            //用户名可用
            return Msg.success();
        }else{
            //用户名不可用
            return Msg.fail();
        }
    }
  1. 在index.jsp的js中新建一个方法。
		//检验用户名是否可用
        function validate_usable_empName(ele) {
            //获取输入框的内容
            var empName = $(ele).val();
            //发送ajax请求校验用户名是否存在
            $.ajax({
                url: "${APP_PATH}/checkuser",
                data: "empName=" + empName,
                type: "get",
                success: function (result) {
                    //判断是否成功(100为成功,200为失败)
                    if(100 == result.code){
                        show_validate_msg(ele, "success", "用户名可用");
                        //并给保存按钮添加一个自定义属性,success代表用户名可用
                        $("#emp_save_btn").attr("ajax-va", "success");
                    }else{
                        show_validate_msg(ele, "error", "用户名已存在!");
                        //并给保存按钮添加一个自定义属性,fail代表用户名不可用
                        $("#emp_save_btn").attr("ajax-va", "error");
                    }
                }
            });
        }
  1. 在js的validate_add_form_empName方法中的else里中调用validate_usable_empName方法,实现用户名输入框失去焦点后先校验格式,格式符合再校验用户名是否可用。

  2. 因为用户名通过格式校验后,用户名是否可用保存按钮不知道,所以需要在保存按钮的点击事件里数据校验判断之下再加一个判断。

	 //判断用户名是否通过可用性校验
	 if($(this).attr("ajax-va") == "error"){
	      //没通过,不保存
	      alert("请正确输入信息!");
	      return false;
	  }
后端校验数据
  1. 对用户名进行校验

    1. 在EmployeeController.java的checkuser中也进行用户名的校验。
       /**
         * 校验用户名是否已存在
         */
        @RequestMapping("/checkuser")
        @ResponseBody
        public Msg checkuser(@RequestParam("empName")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", "用户名已存在!");
            }
        }
    
    1. index.jsp里的validate_usable_empName方法中,ajax请求的success的else中show_validate_msg的错误信息就改为result.extend.va_msg,从后端拿到的错误信息。
    function validate_usable_empName(ele) {
    	...
    	}else{
    		//错误信息从后端返回的数据中获取
         	show_validate_msg(ele, "error", result.extend.va_msg);
    	...
    }
    
  2. JSR303校验

    1. 导入依赖
      <!--   JSR303校验   -->
      <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
      <dependency>
          <groupId>org.hibernate.validator</groupId>
          <artifactId>hibernate-validator</artifactId>
          <version>6.1.2.Final</version>
      </dependency>
    
    1. 在Employee.java类的empName和email属性上加上校验注解。
    //@Pattern(regexp = "", message 错误信息) 自定义校验注解
    @Pattern(regexp = "(^[a-zA-Z0-9_-]{6,16}$)|(^[\\u2E80-\\u9FFF]{2,5})",
                message = "用户名必须是6-16个英文和数字或2-5位中文的组合!")
    private String empName;
    
    //@Email  //邮箱校验注解
    @Pattern(regexp = "^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$",
                message = "邮箱格式不正确!")
    private String email;
    
    1. 修改EmployeeController.java中的saveEmp方法。
    /**
     * 保存新增的员工信息
     * Rest风格的URI,我们规定post的请求为保存请求
     * @param employee  页面表单传递过来的employee对象信息
     * @param result    封装校验的结果
     * @return
     */
    @RequestMapping(value = "/emp", method = RequestMethod.POST)
    @ResponseBody   //加上@Valid注解的参数需要校验
    public Msg saveEmp(@Valid Employee employee, BindingResult result){
        //进行校验
        if(result.hasErrors()){
            //校验失败
            Map<String, Object> map = new HashMap<>();
            List<FieldError> errors = result.getFieldErrors();
            for (FieldError fieldError : errors) {
                //获取错误的字段名
                System.out.println("错误的字段名:" + fieldError.getField());
                //获取错误的信息
                System.out.println("错误信息:" + fieldError.getDefaultMessage());
                //把错误信息保存到map中
                map.put(fieldError.getField(), fieldError.getDefaultMessage());
            }
    
            return Msg.fail().add("errorFields", map);
        }else{
            //校验成功
            employeeService.saveEmp(employee);
            System.out.println(employee);
            return Msg.success();
        }
    }
    
    1. 修改index.jsp中弹窗的保存按钮点击事件中ajax请求的success方法。
    success: function(result){
    
         if(100 == result.code){
             //当保存成功:
             //1.关闭弹窗
             $("#empAddModal").modal('hide');
             //2.跳转到最后一页
             // (因为我们已经添加了bootstrap的合理化参数,所以当超过总页数将会跳转到最后一页)
             to_page(totalRecord);
    
         }else{
             //显示失败信息,哪个错误就显示哪个
             if(undefined != result.extend.errorFields.empName){
                 //显示员工名字的错误信息
                 show_validate_msg("#empName_add_input", "error", result.extend.errorFields.empName);
                 alert(result.extend.errorFields.empName);
             }
             if(undefined != result.extend.errorFields.email){
                 //显示邮箱的错误信息
                 show_validate_msg("#email_add_input", "error", result.extend.errorFields.email);
                 alert(result.extend.errorFields.email);
             }
    
         }
     }
    

CRUD-修改


步骤

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


1. 实现点击编辑弹出员工信息模态框
  1. 在index.jsp中再添加一个模态框。
    <%--   员工修改的模态框   --%>
    <div class="modal fade" id="empUpdateModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                    <%--       弹窗标题             --%>
                    <h4 class="modal-title">员工修改</h4>
                </div>
                <%--   弹窗内容     --%>
                <div class="modal-body">
                    <%--      表单    --%>
                    <%--      empName    --%>
                    <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" class="form-control" name="empName" id="empName_update_input" placeholder="empName">
                                <span class="help-block"></span>
                            </div>
                        </div>
                        <%--      email    --%>
                        <div class="form-group">
                            <label 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>
                        <%--      gender    --%>
                        <div class="form-group">
                            <label class="col-sm-2 control-label">email</label>
                            <div class="col-sm-10">
                                <label class="radio-inline">
                                    <input type="radio" name="gender" id="gender1_update_input" value="M" checked></label>
                                <label class="radio-inline">
                                    <input type="radio" name="gender" id="gender2_update_input" value="F"></label>
                            </div>
                        </div>
                        <%--      deptName    --%>
                        <div class="form-group">
                            <label class="col-sm-2 control-label">deptName</label>
                            <div class="col-sm-4">
                                <%--      提交部门id即可    --%>
                                <select class="form-control" name="dId"></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>
  1. 把getDepts修改为有参函数。
    在这里插入图片描述

  2. 在EmployeeService.java中新建一个名为getEmp的方法,用于根据id查询员工信息。

	/**
     * 根据id查询员工信息
     * @param id
     * @return
     */
    public Employee getEmp(Integer id) {
        Employee employee = employeeMapper.selectByPrimaryKey(id);
        return employee;
    }
  1. 在EmployeeController.java新建一个名为getEmp的方法,用于查询员工信息。
	/**
     * 查询员工信息
     * 查询指定是get请求
     * @param id
     * @return
     */
    @RequestMapping(value = "/emp/{id}", method = RequestMethod.GET)
    @ResponseBody
    public Msg getEmp(@PathVariable("id")Integer id){
        //查询指定的员工信息
        Employee employeee = employeeService.getEmp(id);
        return Msg.success().add("emp", employeee);
    }
  1. 解析显示员工信息的方法build_emps_table中的循环里,为修改增加一个自定义属性,就能让每个按钮和对应的员工绑定起来,同理,在这里先把删除按钮也一起设置。
function build_emps_table(result) {
	...
	$.each(){
		...
			//修改删除按钮,在同一个单元格内
			var editBtn = $("<butt..
			//为编辑按钮添加一个自定义属性,表示当前员工id
			editBtn.attr("edit-id", item.empId);
			var delBtn...
			//同理,也为删除按钮添加一个自定义属性
			delBtn.attr("del-id", item.empId);
		...
	}
	...
}
  1. 为修改按钮添加点击事件。
	//因为在按钮创建之前就绑定click,所以绑定不了
    //解决:在创建按钮后在绑定 或 绑定点击.live()
    //jquery新版用on代替了live
    //为修改按钮添加点击事件
    $(document).on("click", ".edit_btn", function () {
        //查出并显示部门信息
        getDepts("#empUpdateModal select");
        //查出并显示员工信息,$(this)当前被点击的编辑按钮
        getEmp($(this).attr("edit-id"));

		//把员工的id传递给模态框的更新按钮
        $("#emp_update_btn").attr("edit-id", $(this).attr("edit-id"));
        
        //调用模态框
        $("#empUpdateModal").modal({
            backdrop: "static"
        });
    });
  1. js中新建一个查询员工信息的方法。
	//查询员工信息
    function getEmp(id){
        $.ajax({
            url: "${APP_PATH}/emp/" + id,
            type: "get",
            success: function (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]);
            }
     });

完成员工对应信息在模态框显示。

2. 实现点击弹窗内更新按钮修改员工信息
  1. js中为修改模态框邮箱输入框新建失去焦点校验事件,用于在用户修改邮箱后校验邮箱的合法性。
//修改模态框邮箱输入框失去焦点校验事件(弹窗)
$("#email_add_input").blur(function () {
    validate_add_form_email("#email_update_input");
});
  1. 并在修改按钮事件里添加清空前一次校验样式。
	$(document).on("click", ".edit_btn", function () {
            //清除当前元素的校验状态
            $("#email_update_input").parent().removeClass("has-success has-error");
            $("#email_update_input").next("span").text("");
       		...
     }
  1. 在EmployeeService.java中新建一个updateEmp方法,用于根据主键有选择的更新员工信息。
    /**
     * 根据主键有选择的更新员工信息
     * @param employee
     */
    public void updateEmp(Employee employee) {
        employeeMapper.updateByPrimaryKeySelective(employee);
    }	
  1. 在EmployeeController.java中新建一个saveEmp方法,用于员工的更新。
    /**
     * 员工更新
     * 更新指定put请求
     * @param employee
     * @return
     */
    @RequestMapping(value = "/emp/{empId}", method = RequestMethod.PUT)
    @ResponseBody
    public Msg saveEmp(Employee employee){
        //更新员工信息
        employeeService.updateEmp(employee);
        return Msg.success();
    }
  1. 然后为模态框的更新按钮添加点击事件。
 //更新按钮的点击事件
 $("#emp_update_btn").click(function () {
     //校验邮箱信息
     if (!validate_add_form_email("#email_update_input")){
         alert("邮箱格式不正确,请输入正确的邮箱");
         return false;
     }

     //发送ajax请求更新员工信息
     $.ajax({
         url: "${APP_PATH}/emp/" + $(this).attr("edit-id"),
         //第一种方法:
         // 如果ajax直接发put请求,请求体中的数据,request.getgetParameter("empName")拿不到
         // tomcat发现是put请求,就不会封装数据为map,只有post形式的请求才会封装请求体为map
         // 所以要在web.xml中注册FormContentFilter过滤器,支持put和delete请求
         //第二种方法:
         // ajax发送post请求,并在发送的data加上 "_method=put"
         // HiddenHttpMethodFilter把post转为put
         type: "put",
         data: $("#empUpdateModal form").serialize(),
         success: function (result) {
             //关闭对话框
             $("#empUpdateModal").modal("hide");
             //回到本页面(刷新)
             to_page(currentPage);
         }
     });

 });

CRUD-删除


步骤

1、单个删除
2、多个删除


1. 实现单个删除
  1. 在EmployeeService.java中新建deleteEmp方法,用于根据id删除指定员工信息。
	/**
     * 根据id删除指定员工信息
     * @param id
     */
    public void deleteEmp(Integer id) {
        employeeMapper.deleteByPrimaryKey(id);
    }
  1. 在EmployeeController.java中新建deleteEmp方法,用于删除单个员工信息。
    /**
     * 删除单个员工信息
     * 删除指定delete请求
     * @param id
     * @return
     */
    @RequestMapping(value = "/emp/{id}", method = RequestMethod.DELETE)
    public Msg deleteEmpById(@PathVariable("id")Integer id){
        //按照id删除指定员工信息
        employeeService.deleteEmp(id);
        return Msg.success();
    }
  1. 在js中为员工对应的删除按钮添加点击事件(单个删除)。
//为员工对应的删除按钮添加点击事件(单个删除)
        $(document).on("click", ".delete_btn", function () {
            //获取删除的员工姓名
            var empName = $(this).parents("tr").find("td:eq(1)").text();
            //获取删除的员工id
            var empId = $(this).attr("del-id");

            //弹出是否确认删除提示
            if(confirm("确定要删除【" + empName + "】吗?")){
                //确认,发送ajax请求删除员工
                $.ajax({
                    url: "${APP_PATH}/emp/" + empId,
                    type: "delete",
                    success: function (result) {
                        //回到本页(刷新页面)
                        to_page(currentPage);
                    }
                });
            }else {
                //取消
                return false;
            }
        });
2. 多选删除
  1. 为行首添加checkbox全选选项。
     ...
	 <thead>
	     <tr>
	         <th>
	             <input type="checkbox" id="check_all">
	         </th>
	         <th>empId</th>
	         <th>empName</th>
	         <th>gender</th>
        ...
  1. 在build_emps_table函数中为员工行也添加上checkbox选项。
	...
	$.each(emps, function (index, item) {
	     var checkBoxTd = $("<td><input type='checkbox' class='check_item'/></td>");
	     //员工信息单元格
	     ...
	     //把相同员工的单元格添加到同一行
	     $("<tr></tr>")
	         .append(checkBoxTd)
	         .append(empIdTd)
	         .append(empNameTd)
	         .append(genderTd)
	         .append(emailTd)
	         ...

在这里插入图片描述

  1. 为th行首的全选按钮添加点击全选/全不选事件。
        $("#check_all").click(function () {
            //attr获取checked是undefined;
            //attr获取自定义属性的值;
            //prop修改和读取dom原生属性的值
            //check_item应跟随全选按钮,一同选中或不选中
            $(".check_item").prop("checked", $(this).prop("checked"));
        });
  1. 因为我们多加了一列,所以之前单个员工删除按钮事件中获取员工姓名需要改为2。
    在这里插入图片描述

  2. 为td行首的按钮添加点击事件。

        //check_item的点击事件
        $(document).on("click", ".check_item", function () {
            //是否满足选中的个数和当前页员工的个数相同
            var flag = $(".check_item:checked").length == $(".check_item").length;
            //check_all全选按钮要和判断的结果相同,如果相同就选中,不同不选中
            $("#check_all").prop("checked", flag);
        });
  1. 在EmployeeService.java中新建一个deleteBatch方法,用于批量删除。
    /**
     * 批量删除员工信息
     * @param ids
     */
    public void deleteBatch(List<Integer> ids) {
        //自定义条件
        EmployeeExample example = new EmployeeExample();
        EmployeeExample.Criteria criteria = example.createCriteria();
        //删除语句将变成:delete from xxx where emd_id in (1, 2, 4 ...)
        criteria.andEmpIdIn(ids);
        employeeMapper.deleteByExample(example);
    }
  1. 修改EmployeeController.java中的deleteEmpById改为deleteEmp,并修改内容实现单个多选二合一删除。
    /**
     * 单个多选二合一删除员工信息
     * 多个删除:id中间用-分隔,如 1-2-4
     * 单个删除:如3
     * 删除指定delete请求
     * @param ids
     * @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<>();
            for (String id : str_ids) {
                del_ids.add(Integer.parseInt(id));
            }
            //批量删除
            employeeService.deleteBatch(del_ids);

        }else{
            //单个删除
            //按照id删除指定员工信息
            Integer id = Integer.parseInt(ids);
            employeeService.deleteEmp(id);
        }
        return Msg.success();
    }
  1. 为上方删除按钮添加点击删除选中员工信息事件。
        //为上方删除按钮添加点击删除选中员工信息事件
        $("#emp_delete_modal_btn").click(function () {
            var empName = "";
            var del_idstr = "";
            //遍历当前页被选中的员工
            $.each($(".check_item:checked"), function () {
                //获取被选中的员工姓名
                empName += $(this).parents("tr").find("td:eq(2)").text() + ",";
                //组装员工id字符串
                del_idstr += $(this).parents("tr").find("td:eq(1)").text() + "-";;
            });
            //去除多余 ,
            empName = empName.substring(0, empName.length - 1);
            //去除多余 -
            del_idstr = del_idstr.substring(0, del_idstr.length - 1);

            if(confirm("确定要删除【" + empName + "】吗?")){
                //确认,发送ajax请求删除指定员工
                $.ajax({
                    url: "${APP_PATH}/emp/" + del_idstr,
                    type: "delete",
                    success: function (result) {
                        //提示删除成功
                        alert(result.msg);
                        //回到当前页
                        to_page(currentPage);
                    }
                });
            }else{
                //取消
                return false;
            }
        });

磕磕碰碰的跟着老师做完了这个项目,第一次用ssm做项目,还是有很多知识点不够扎实,也学到了很多东西,学后端,前端的知识也需要了解,接下来继续练习,然后学习springboot。

学习视频:https://www.bilibili.com/video/BV17W411g7zP?p=1

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值