目录
  1. 1. ssm_crud项目
    1. 1.1. 一、项目说明
      1. 1.1.1. 1.项目预览
      2. 1.1.2. 2.项目的功能点
      3. 1.1.3. 3.项目技术点
    2. 1.2. 二、基础环境的搭建
      1. 1.2.1. 1.创建maven工程
      2. 1.2.2. 2.引入项目依赖的jar包
      3. 1.2.3. 3.引入bootstrap,一个前端的框架
    3. 1.3. 三、配置文件的编写
      1. 1.3.1. 1.web.xml的配置
        1. 1.3.1.1. (1)启动spring容器的监听器(项目一起动,spring的容器就启动)
        2. 1.3.1.2. (2)配置前端控制器
        3. 1.3.1.3. (3)配置字符编码过滤器(防止乱码)
        4. 1.3.1.4. (4)配置支持Rest风格URI的过滤器
      2. 1.3.2. 2.springmvc.xml的配置
        1. 1.3.2.1. (1)扫描所有的业务逻辑组件
        2. 1.3.2.2. (2)配置视图解析器
        3. 1.3.2.3. (3)两个标准配置
      3. 1.3.3. 3.applicationContext.xml的配置
        1. 1.3.3.1. (1)配置数据源(c3p0)
        2. 1.3.3.2. (2)和mybatis的整合配置
        3. 1.3.3.3. (3)事务控制的配置
      4. 1.3.4. 3.mybatis-config.xml(全局配置文件+mapper文件)
        1. 1.3.4.1. (1)配置驼峰命名规则
        2. 1.3.4.2. (2)配置类型别名
      5. 1.3.5. 4.逆向工程
        1. 1.3.5.1. (1)引入依赖jar
        2. 1.3.5.2. (2)创建连接数据库的mgb.xml配置文件
        3. 1.3.5.3. (3)创建MGBTest类完成逆向工程的创建
        4. 1.3.5.4. (4)生成的不一定符合要求,再添加自己的需求(联合查询)
      6. 1.3.6. 5.创建测试类,测试逆向工程生成的方法
    4. 1.4. 四、完成查询逻辑
      1. 1.4.1. (一)不分页查询
        1. 1.4.1.1. 1.访问index.jsp页面
        2. 1.4.1.2. 2.index.jsp页面发出查询员工列表请求
        3. 1.4.1.3. 3.EmployeeController来接收请求,查出员工数据
        4. 1.4.1.4. 来到show.jsp页面展示
      2. 1.4.2. (二)分页查询
        1. 1.4.2.1. 1. 在controller层中传入要查询的页数,默认值为1
        2. 1.4.2.2. 2. 引入PageHelper分页插件
        3. 1.4.2.3. 3.写单元测试(用spring的单元测试),测试利用分页插件能否取到值
    5. 1.5. 五、完成查询界面构建(bootstrap)
      1. 1.5.1. 1.引入bootstrap
      2. 1.5.2. 2.利用bootstrap搭建页面
        1. 1.5.2.1. 创建栅格点击进入
      3. 1.5.3. 3.添加删除和新增按钮
      4. 1.5.4. 4.添加显示数据的table
      5. 1.5.5. 5.添加分页显示
    6. 1.6. 六、把数据插到页面中
      1. 1.6.1. 1.引入标签库
      2. 1.6.2. 2.遍历员工数据并填充到相应的位置
      3. 1.6.3. 3.填充分页信息
        1. 1.6.3.1. (1) 填充文字信息
        2. 1.6.3.2. (2) 填充分页条信息
    7. 1.7. 七、用ajax改造查询请求
      1. 1.7.1. 需求分析
      2. 1.7.2. 0.业务逻辑
      3. 1.7.3. 1.在Empcontroller类中写一个返回json字符串的方法
      4. 1.7.4. 2.创建一个专门返回json数据的类
      5. 1.7.5. 1.index.jsp页面,直接发送ajax请求进行员工的分页查询
        1. 1.7.5.1. (1)准备工作
        2. 1.7.5.2. (2)从index.jsp发起请求
        3. 1.7.5.3. (3)显示查询信息
        4. 1.7.5.4. (4)解析list里的员工数据
        5. 1.7.5.5. (5)解析分页信息
    8. 1.8. 剩余项目
      1. 1.8.1. REST风格
    9. 1.9. 八、用户新增
      1. 1.9.1. 0.逻辑
      2. 1.9.2. 1.点击新增,弹出模态框
        1. 1.9.2.1. (1)在页面中引入模态框
        2. 1.9.2.2. (2)点击新增,弹出模态框
        3. 1.9.2.3. (3)把模态框需要的样子定义出来
      3. 1.9.3. 2.填充模态框中的部门信息
      4. 1.9.4. 3.点击保存按钮保存数据
        1. 1.9.4.1. 0.逻辑
        2. 1.9.4.2. 1.方法
      5. 1.9.5. 4.保存数据之前先校验(前端校验)
        1. 1.9.5.1. (1)如果校验失败,返回false
        2. 1.9.5.2. (2)校验的方法
        3. 1.9.5.3. (3)使用bootstrap中的表单校验状态完善校验显示的功能
        4. 1.9.5.4. (4)把校验的方法抽取出来
        5. 1.9.5.5. (5)ajax校验用户名是否重复
        6. 1.9.5.6. (6)完善用户名校验1
        7. 1.9.5.7. (7)完善用户名校验2
      6. 1.9.6. 5.重要数据的后端校验(JSR303)
        1. 1.9.6.1. (1)想要实现JSR303的支持,必须要导入Hibernate-Validator包
        2. 1.9.6.2. (2)后端校验的实现
    10. 1.10. 九、修改
      1. 1.10.1. 0.逻辑
      2. 1.10.2. 1.创建员工修改的模态框
      3. 1.10.3. 2.点击编辑,显示员工员工信息
      4. 1.10.4. 3.点击修改,更新员工
    11. 1.11. 十、删除
      1. 1.11.1. 1.单个删除
        1. 1.11.1.1. (1)单个删除的步骤
      2. 1.11.2. 2.批量删除
        1. 1.11.2.1. (1)添加多选框
        2. 1.11.2.2. (2) 完成全选全不选功能
        3. 1.11.2.3. (3)批量删除
ssm_crud项目详细步骤

ssm_crud项目

一、项目说明

1.项目预览

点击进入项目预览

2.项目的功能点

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

    3.项目技术点

  • 1.基础框架 ssm(spring+springmvc+mybatis)
  • 2.数据库 mysql
  • 3.项目的依赖管理管理 maven
  • 4.分页 pagehelper
  • 5.逆向工程 Mybatis-Gennerate

    二、基础环境的搭建

    1.创建maven工程

  • 在idea中创建maven项目,选阿帕奇的webapp

2.引入项目依赖的jar包

在pom.xml文件中导入以下jar包的依赖点击进入maven仓库中央仓库

  • spring (Spring JDBC(事务控制);Spring Aspects(切面编程))
  • springmvc(Spring Web MVC)
  • mybatis (Mybatis;Mybatis Spring(整合包))
  • 数据库连接池(c3p0),驱动包(MySQL Connect)
  • 其他(Jstl Servlet Api junit)

    3.引入bootstrap,一个前端的框架

9018347a5cae3991e3d88f3c68053ec8.png

三、配置文件的编写

1.web.xml的配置

(1)启动spring容器的监听器(项目一起动,spring的容器就启动)

  • 配置监听器:项目一启动,就会加载spring的配置配置文件(指定spring配置文件的位置)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
       <!-- 1、启动Spring的容器 -->
    <!-- needed for ContextLoaderListener -->
    <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <!-- Bootstraps the root web application context before servlet initialization -->
    <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
  • spring的配置文件:主要配合和业务逻辑有关的

(2)配置前端控制器

  • 用来拦截所有的请求

  • 指定springmvc配置的位置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <!--2、SpringMVC的前端控制器,拦截所有请求-->
    <!-- The front controller of this Spring Web application, responsible for handling all application requests -->
    <servlet>
    <servlet-name>springDispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- Map all requests to the DispatcherServlet for handling -->
    <servlet-mapping>
    <servlet-name>springDispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>

    4361e6316e60219b69f40e1c27d8962c.png

  • 注意:如图是指定springmvc位置的配置方法,也可以不指定位置。就是在与web.xml同级的目录下创建一个servlet名-servlet的文件,如图就是创建一个springDispatcherServlet-servlet.xml的文件就可以代替springmvc.xml的文件

    (3)配置字符编码过滤器(防止乱码)

  • 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <!--字符编码过滤器(所有过滤器之前) -->
    <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>

    af1bb2d76edd72c54495c10d940bdaab.png

    (4)配置支持Rest风格URI的过滤器

  • 因为页面无法支持delete、put请求,还需要一个过滤器

  • 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!--使用Rest风格的URI-->
    <!-- HiddenHttpMethodFilter:将普通的get/post请求转化为put/delete请求 -->
    <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>

    2.springmvc.xml的配置

  • 主要包含网站跳转逻辑的组件

    (1)扫描所有的业务逻辑组件

  • 在java.com.wantao包下创建bean、controller、service、dao、utils、test包

  • 我们希望只扫描controller(控制器)

    1
    2
    3
    4
    5
    <!--springmvc的配置文件,包含网站跳转逻辑控制,配置 -->
    <context:component-scan base-package="com.wantao" use-default-filters="false">
    <!--只扫描控制器-->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    3edf3492198fd7eab0cf844408e4e945.png

    (2)配置视图解析器

  • 方便页面的解析

  • 在WEB-INF下建一个view包

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

    (3)两个标准配置

  • 代码

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

3.applicationContext.xml的配置

  • 主要是配置和逻辑有关的
  • Spring配置文件的核心点:(数据源、与mybatis的整合、事务控制)

(1)配置数据源(c3p0)

  • 代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!--配置数据源-->
    <bean id="PoolDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="jdbcUrl" value="jdbc:mysql://47.94.229.156:3306/ssm_crud?serverTimezone = GMT"></property>
    <property name="driverClass" value="com.mysql.cj.jdbc.Driver">
    </property>
    <property name="user" value="root">
    </property>
    <property name="password"
    value="root">
    </property>
    </bean>
  • 也可以把连接信息写在jdbc.properties文件中

(2)和mybatis的整合配置

  • 配置SqlSessionFactoryBean,他能创建SqlSessionFactory
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <!--================配置spring和mabatis的整合=================== -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--指定mybatis的全局配置文件 -->
    <property name="configLocation" value="classpath:mybatis-config.xml">
    </property>
    <!--配置数据源 -->
    <property name="dataSource" ref="PoolDataSource">
    </property>
    <!--配置mybatis映射文件 -->
    <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.wantao.dao">
    </property>
    </bean>
    <!--====================================-->
  • 注意:在spring和mybatis整合的时候,并不是一定要把mapper的映射文件放到和dao层的文件一个路径,也可以在sqlSessionFactory中配置映射文件存放的位置;在MapperScannerConfiger中配置要扫描的dao层

    (3)事务控制的配置

  • 要能管事务,就是控制住数据源
    1
    2
    3
    4
    5
    6
    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--控制数据源-->
    <property name="dataSource" ref="PoolDataSource">
    </property>
    </bean>

  • 1.开启注解的事务,2.使用xml配置形式的事务(比较重要的都是采用配置式)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <!--开启注解事务或者使用xml配置形势的事务(主要的都是配置式)-->
    <aop:config>
    <!--切点表达式 -->
    <aop:pointcut expression="execution(* com.wantao.service..*(..))" id="txPointCut" />
    <!--配置事物增强 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
    </aop:config>
    <!--配置事物增强,事务如何切入-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
    <!--代表所有方法都是事务方法-->
    <tx:method name="*" />
    <!--以find开头的所有方法-->
    <tx:method name="find*" read-only="true" />
    <tx:method name="get*" read-only="true" />
    </tx:attributes>
    </tx:advice>

    3.mybatis-config.xml(全局配置文件+mapper文件)


全局配置文件

  • 注意:这个全局配置文件不是必须的,也可以放在放在applicationContext.xml中,以property的形势放在class为SqlSessionFactoryBeanbean中。
  • 搜索mybatis的官方文档(点击进入
  • 找到表头,复制进去
    1
    2
    <?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">

    (1)配置驼峰命名规则

  • 代码
    1
    2
    3
    4
    5
    <!-- 是否开启自动驼峰命名规则(camel case)映射,
    即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。-->
    <settings>
    <setting name="mapUnderscoreToCamelCase" value="true" />
    </settings>

    (2)配置类型别名

  • 代码
    1
    2
    3
    <typeAliases>  
    <package name="com.wantao.bean" />
    </typeAliases>

配置mapper文件

  • 一个mapper对应一个dao,所以需要先创建数据库
  • 然后用逆向工程生成需要的dao、mapper、xml

4.逆向工程

  • 去网页搜索 Mybatis Generator(GitHub–>进入帮助文档)点击进入

    (1)引入依赖jar

  • 在pom.xml文件中添加
    1
    2
    3
    4
    5
    6
    <!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
    <dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.7</version>
    </dependency>

    (2)创建连接数据库的mgb.xml配置文件

  • 网页位置
    d18decc80b804e2de439cd82dbe6096b.png
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3">
<!-- 设置生成的文件没有注释 -->
<commentGenerator>
<property name="suppressAllComments" value="true" />
</commentGenerator>

<!-- 配置数据库连接 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/ssm_crud?serverTimezone = GMT"
userId="root" password="root">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>

<!--指定javaBean生成的位置-->
<javaModelGenerator targetPackage="com.wantao.bean"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>

<!--指定映射文件生成的位置 -->
<sqlMapGenerator targetPackage="mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>

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

<!-- 指定每个表生成策略 -->
<table schema="DB2ADMIN" tableName="tb_emp"
domainObjectName="Employee">
</table>
<table schema="DB2ADMIN" tableName="tb_dept"
domainObjectName="Department">
</table>
</context>
</generatorConfiguration>

(3)创建MGBTest类完成逆向工程的创建

  • 网页的位置
    adefafd2bd2cebe58c65ad2a53ba26be.png

    • 在test包下,创建一个MGBTest类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      public class MGBTest {
      @Test
      public void gennerator()
      throws IOException, XMLParserException, InvalidConfigurationException, SQLException, InterruptedException {
      List<String> warnings = new ArrayList<String>();
      boolean overwrite = true;
      File configFile = new File("src/main/resources/mgb.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);
      }
      }
  • 直接执行就可以获得逆向工程的映射文件和dao层接口、mapper、xml以及pojo

(4)生成的不一定符合要求,再添加自己的需求(联合查询)

  • 查员工信息的时候,希望根据员工的部门号查出员工的部门名

  • 1.EmployeeMapper.java中新增两个能查出部门的方法

    1
    2
    3
    4
    //带部门的查询方法List<Employee> 
    selectByExampleWithDept(EmployeeExample example);
    //带部门的查询方法Employee
    selectByPrimaryKeyWithDept(Integer empId);
  • 2.在Employee.java中引入部门信息

    1
    2
    3
    4
    5
    6
    7
    8
    //希望查询员工的同时,部门信息也是查询好的。
    private Department department;
    public Department getDepartment() {
    return department;
    }
    public void setDepartment(Department department) {
    this.department = department;
    }
  • 3.在EmployeeMapper.xml文件中添加新建的两个带部门的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
        <sql id="Base_Column_List_With_Dept"> 
    emp_id, emp_name, gender, email,d_id,dept_id,dept_name
    </sql>

    <resultMap id="BaseResultMapWithDept" type="com.wantao.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进行结果的联合显示 -->
    <association javaType="com.wantao.bean.Department" property="department">
    <id column="dept_id" property="deptId" />
    <result column="dept_name" jdbcType="VARCHAR" property="deptName" />
    </association>
    </resultMap>

    <select id="selectByExampleWithDept" resultMap="BaseResultMapWithDept">
    select
    <if test="distinct">
    distinct
    </if>
    <include refid="Base_Column_List_With_Dept" />
    from tb_emp left join tb_dept on tb_emp.d_id=tb_dept.dept_id
    <if test="_parameter != null">
    <include refid="Example_Where_Clause" />
    </if>
    <if test="orderByClause != null">
    order by ${orderByClause}
    </if>
    </select>
    <select id="selectByPrimaryKeyWithDept" parameterType="java.lang.Integer"
    resultMap="BaseResultMapWithDept">
    select
    <include refid="Base_Column_List_With_Dept" />
    from tb_emp left join tb_dept on tb_emp.d_id=tb_dept.dept_id
    where emp_id = #{empId,jdbcType=INTEGER}
    </select>

5.创建测试类,测试逆向工程生成的方法

  • 需求:1.测试是否数据库已经连通,生成的逆向工程是否可用;2.生成初始数据,用于后面的测试
  • 分析:此时的mybatis是和spring整合的,有以下两种方法

  • 在test包下创建一个MapperTest类,用于测试Dao层的方法

  • 方法一:通过原始方法进行测试

    1
    2
    3
    4
    5
    6
    1.创建springIoC容器
    2.从容器中获取mapper
    //1.创建springIoC容器
    ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
    //2.从容器中获取
    DepartmentMapper bean = ac.getBean(DepartmentMapper.class);

  • 方法二:通过spring-test进行测试
    推荐spring的项目就可以使用Spring的单元测试,看自动注入我们需要的组件
* 1.要导入spring的单元测试模块 @RunWith(SpringJUnit4ClassRunner.class),需要在pom.xml导入spring-test的依赖,可以在maven中央仓库搜索
    
1
2
3
4
5
6
7
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.7.RELEASE</version>
<scope>test</scope>
</dependency>
    • 2.通过@ContextConfiguration指定spring配置文件的位置,他能自动创建出IoC容器; @RunWith(SpringJUnit4ClassRunner.class),指定具体单元测试的组件(这里使用spring的单元测试);需要注入什么组件,直接autowire注入组件


      (1)测试spring

      1
      2
      3
      4
      5
      6
      7
      8
      @RunWith(SpringJUnit4ClassRunner.class)//指定具体单元测试的组件(这里使用spring的单元测试)
      @ContextConfiguration(locations = {"classpath:applicationContext.xml"}) // (实际上也是获取spring上下文)
      public class DaoTest2 {
      @Autowired
      DepartmentMapper departmentMapper;
      public void testCRUD(){
      System.out.println(departmentMapper);//测试spring配置是否ok
      }

      结果如下:
      org.apache.ibatis.binding.MapperProxy@314c8b4a
      这样就说明spring的配置成功了。


      (2)插入几个部门

      • 用有参构造器实现插入操作,在Department中生成有参构造器
      • 注意:一旦生成有参构造构造器,一定要生产无参的构造器,因为反射要经常的使用无参的构造
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Department {  
private Integer deptId;
private String deptName;
//因为生成了有参的构造器,一定要生产无参的构造器,因为反射要经常的使用无参的构造
public Department() {
}
public Department(Integer deptId, String deptName) {
this.deptId = deptId;
this.deptName = deptName;
}
public Integer getDeptId() {
return deptId;
}
public void setDeptId(Integer deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
@RunWith(SpringJUnit4ClassRunner.class)//指定具体单元测试的组件
@ContextConfiguration(locations = {"classpath:applicationContext.xml"}) // (实际上也是获取spring上下文)
public class DaoTest2 {
@Autowired
DepartmentMapper departmentMapper;
@Test
public void testDao() {
//1.插入几个部门
departmentMapper.insertSelective(new Department(null,"测试部"));
departmentMapper.insertSelective(new Department(null,"开发部"))
}
}
如果数据库中有数据,则说明mybatis配置正确。

这个时候进行测试的时候,不知道为啥就是连不上数据库,后来把jdbc.properties里的内容直接写入applicationContext,问题解决。
***
(3)测试员工插入,生成员工的数据,用于后面的开发
* 同样为了方便,给Employee生成有参无参构造器

1
2
3
4
5
6
7
8
9
10
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;
}
public Employee() {

}
在上面的测试类中直接加入下面的代码即可
1
employeeMapper.insertSelective(new Employee(null,"jerry","M","23@qq.com",1));

(4)重要需求:想要批量的添加员工用于测试,这个时候就需要sqlSession

  • 1.首先,在applicationContext.xml中配置sqlSession

    1
    2
    3
    4
    5
    <!--配置批量的sqlSession -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
    <constructor-arg name="executorType" value="BATCH"></constructor-arg>
    </bean>

    3026fea809bc30bd054ef4ecaeb6bba9.png

    8acc44e1308a6bbe884fd080f32cadad.png

  • 2.在测试类中注入SqlSession

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Autowired
    SqlSession sqlSession;// 批量处理

    3.批量生成1000个员工用于测试
    @Test
    public void testDao() {
    //批量插入多个员工,使用可以批量操作的sqlSession(常用语生成测试数据)
    EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
    for (int i = 0; i < 1000; i++) {
    String uuid = UUID.randomUUID().toString().substring(0, 5) + i;
    employeeMapper.insertSelective(new Employee(null, uuid, "M", uuid + "@qq.com", 1));
    }

    9502b2d2e00982798abaad585966162f.png

查看数据库的数据,如果加入,则成功
到此,测试就完成了


四、完成查询逻辑

(一)不分页查询

1.访问index.jsp页面

2.index.jsp页面发出查询员工列表请求

  • 规定查询请求的URI为/emps

  • 在index.jsp页面直接转到当前项目下的emps请求

    1
    2
    <%@ page language="java" contentType="text/html; charset=UTF-8"  pageEncoding="UTF-8" isELIgnored="false" %>
    <jsp:forward page="/emps"></jsp:forward>

    3.EmployeeController来接收请求,查出员工数据

  • 想要接受到emps的请求,就要写一个处理器,在controller包中,写一个EmployeeController的类,用这个类处理员工的增删改查请求。

  • 在EmployeeController的类中添加查找所有员工的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Controller
    public class EmployeeController{
    @Autowird
    EmployeeService employeeService;
    @RequestMapping("/emps")
    public String getEmps(){
    List<Employee> emps = employeeService.getAll();
    return "show";
    }
    }
    • 查询需要service层的方法,在controller层自动注入EmployeeService类,让service层查询方法,所以,要在service包下创建EmployeeService.java
  • 在EmployeeService类中,创建需要的方法

    1
    2
    3
    4
    5
    6
    7
    8
    @Service
    public class EmployeeService {
    @Autowired
    EmployeeMapper employeeMapper;
    public List<Employee> findAllEmployee() {
    //查询所有的(带上部门信息),所以调节为null
    return employeeMapper.selectByExampleWithDept(null);
    }
  • 查询还需要Mapper里相应的方法,自动注入(@Autowired)

  • 通过视图解析器,拼串后会来到/WEB-INF/views/show.jsp页面做展示*

    ![image.png](data:image/png;base64,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QAIkQAL+EKAB2h+ulEoCJEACJEACJEACJEACJEACJEACJEACJEACJEACo54ADdCjvgsQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAn4Q+AL/ogFfvZbvyRTrkzg70v/KO9ymwRIgARIgARIgARIgARIgARIgARIgARIgARIgATyhgA9oPOmKagICZAACZAACZAACZAACZAACZAACZAACZAACZAACRQWARqgC6s9WRsSIAESIAESIAESIAESIAESIAESIAESIAESIAESyBsCNEDnTVNQERIgARIgARIgARIgARIgARIgARIgARIgARIgARIoLAI0QBdWe7I2JEACJEACJEACJEACJEACJEACJEACJEACJEACJJA3BGiAzpumoCIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkUFgEaIAurPZkbUiABEiABEiABEiABEiABEiABEiABEiABEiABEggbwjQAJ03TUFFSIAESIAESIAESIAESIAESIAESIAESIAESIAESKCwCNAAXVjtydqQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQQN4QoAE6b5qCipAACZAACZAACZAACZAACZAACZAACZAACZAACZBAYRGgAbqw2pO1IQESIAESIAESIAESIAESIAESIAESIAESIAESIIG8IUADdN40BRUhARIgARIgARIgARIgARIgARIgARIgARIgARIggcIiQAN0YbUna0MCJEACJEACJEACJEACJEACJEACJEACJEACJEACeUOABui8aQoqQgIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAKFReALI6E60eh5dPx4K1qOBeLqlqB6Ty2qrh0J2lNHEiABEiABEiABEiABEiABEiABEiABEiABEiABEhidBOgBPTrbnbUmARIgARIgARIgARIgARIgARIgARIgARIgARIgAd8J0ADtO2IWQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAKjkwAN0KOz3VlrEiABEiABEiABEiABEiABEiABEiABEiABEiABEvCdQF4YoKPRHhyvvwmbX+rxvcIsgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIIDcEhn0RQmF87vjxJLQcExWehBcnnMbSm8pzU3uWQgIkQAIkQAIkQAIkQAIkQAIkQAIk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Y/+AX/wy+3AcE++YgU3dizFNtkfHCxVe0LN+vhJr7ijR1cj4V5Px6ExMROoA0hPv+CGq70idJuPCmYEESIAESIAESIAESIAESIAESIAESIAESIAESIAERgiBYfOA1vnoixP+98dq8Mb1zVh/V7l+KvGrG43/+9GT+H3ncZzrBfqO9SfOaxsVJZhYOg233n0zpklhN9REsT0hb9qGWuy4+xQ63j6O33a8hz4lXEYJJlYAV5dOw7dmTMG0m4wRnoHAtTfjoT1TcOKFV/HGa5+gD3Frt0GP4/VWGvAYCZAACZAACZAACZAACZAACZAACZAACZAACZAACRQ+gUA0GvXFRfdnvy18ePlQw78v/WM+qEEdSIAESIAESIAESIAESIAESIAESIAESIAESIAESMBEYFhDcJi04QESIAESIAESIAESIAESIAESIAESIAESIAESIAESIIGCIUADdME0JStCAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAvlFgAbo/GoPakMCJEACJEACJEACJEACJEACJEACJEACJEACJEACBUOABuiCaUpWhARIgARIgARIgARIgARIgARIgARIgARIgARIgATyiwAN0PnVHtSGBEiABEiABEiABEiABEiABEiABEiABEiABEiABAqGAA3QBdOUrAgJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJ5BcBGqDzqz2oDQmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAkUDAEaoAumKVkREiABEiABEiABEiABEiABEiABEiABEiABEiABEsgvAjRA51d7UBsSIAESIAESIAESIAESIAESIAESIAESIAESIAESKBgCNEAXTFOyIiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiSQXwRogM6v9qA2JEACJEACJEACJEACJEACJEACJEACJEACJEACJFAwBGiALpimZEVIgARIgARIgARIgARIgARIgARIgARIgARIgARIIL8IBIaGhqL5pRK1IQESIAESIAESIAESIAESIAESIAESIAESIAESIAESyCcCV155pSt1cuIBffnyZYh//CMBEiABEiABEiABEiABEiABEiABEiABEiABEiABEhg9BHJigB49OFlTEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABnQAN0DoJ/pIACZAACZAACZAACZAACZAACZAACZAACZAACZAACXhKgAZoT3FSGAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQgE6ABmidBH9JgARIgARIgARIgARIgARIgARIgARIgARIgARIgAQ8JUADtKc4KYwESIAESIAESIAESIAESIAESIAESIAESIAESIAESEAnQAO0ToK/JEACJEACJEACJEACJEACJEACJEACJEACJEACJEACnhKgAdpTnBRGAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiSgE6ABWifBXxIgARIgARIgARIgARIgARIgARIgARIgARIgARIgAU8J0ADtKU4KIwESIAESIAESIAESIAESIAESIAESIAESIAESIAES0AnQAK2T4C8JkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkICnBGiA9hQnhZEACZAACZAACZAACZAACZAACZAACZAACZAACZAACegEaIDWSfCXBEiABEiABEiABEiABEiABEiABEiABEiABEiABEjAUwI0QHuKk8JIgARIgARIgARIgARIgARIgARIgARIgARIgARIgAR0Al/QN/g7sglEI1/G3OAYdKWoRqj+Tzi4IpoiBU85IRCN1KGotTmZtLge3XfVohS92P5SEBsG4qeiIdTffQArigPJtNwiAQOBSLgOK482o0t0k+IatMxrQFUB9ZlIZBua3t2Hkxe6YnUU9Y+GEJr0ILZWzkJZAdXV0LTcdUig/c1xqO5JJq5ZMIjGMo6bgkg0GuF9Jdk1RsRWdGAb5r6wITneWWgdmt6Ng5VlFmd4KBsChX4/zYYN844OAhx/Rkc7j+Zacpwv/NbnOJa+jUeyPYoG6PTtyxQkoBIYNwmhKJIvl0WTUBYQxpIyTCoCoBug1VzcIwETAXHzCL7bDOi2toFmVO+fpH3QiPUpU5YRcyA60I61+6vRrF8Peh1FDQJd6OqpRnAw9vFmpNd1xDQKFSUBEhj1BCID7WiLvIF9Pc1AeeEYwgv5fuqm00bCcxB8N5VbioXU8hYMza+yOOH9oULth96Tyr3EaLQda5+pRrP83GZUoziEUNHtuH3SbKwo48c0I57RuG/6YC4gOBhTTIa0aA1aftKAKu3dWiVZCOM8ry+1TbnnksAItkfRAO2yzZmNBEiABEjAmoD2cPVCmpcX66w8SgIkQAIk4BMB42yDULlPBVEsCaQgwH6YAs5IOTXQhS7xr2cDNojZe3dZGwxHSnWoZ/YEAoEyzC4PYcOA9OFr8DQi0VlxRy3rMnoHTqonJt1qaXxWExX4Hq+vAm/g0V09xoAukPYPlP0FB4f+iCHpX3f9ZwVSuzyrRtEkTHGk0pSYR7SjtGj6kUkAACAASURBVEw0GgkEyhrRPb1G86jX6q+F4KhN+aA2Ejj1dj1p8pypmd6N7jWDGPrJEAbXdKN7QT1qxIwB/pEACZBAAREIFK/AwbohbawT45341z09VEA1zM+qFOr9ND9pU6t8JTAs44+YvffSdkSiDPOYr/0iV3qVFhvekC/sQ9ugfenCa7qtRzJYA6iZNMs2w6gc50fh9TUs45htr8vTEyPYHkUP6DztU1Rr5BAIFV+fUPb64hBguJEmTnKDBCwIlFU24mBlo8WZkXnI6mFSxDttlOKdCi+JsrIyNHLW5shsZGpNAiRAAnlIoNDup9kgLqs8iKHKpATTtG+uU5KEw63UBAx9JToQQUd4Jarl952BDWjqreVzXWqShX+29FbURJuTTiiBLuyL9GJFsc0D/2Ab9l0QofniaKI1uLU0NaaCG+d5faVucJ51RCAbe1SkvQ4r37gVWxtSz1ZwpIiDRPSAdgCJSUiABEiABJwS+BCnxcOk/hcN4fayNE+Telr+kgAJkAAJkAAJkAAJ5C2BQHEZquYfRIshhE/z6Y681ZmK5YZAIFCFWyepZXUNfKgekPeGTifXVBLHGX4DvL7kDsJtvwlE2+sQrG5GV3M1gnNzM5OFHtB+t2qG8qORADraxuDJfZ9HV9fnpdyfIRT6DLc/eBkrqvyd4hTT4Ut4Y9+XcBJCD0kNQNNjyu3/L1bVfuooVEAk8kW0Nf0X7DtplPUZQjWf4fZbndXJKzlqbdzsXY9JVzlYbPCqSUj6RtuXoy3EEn4S+053KTfhUHEIU8ofxKqQ/dco0yqx8cUehBdqx/6VeFKSGSquwe3TV6VdLCQS2Yamd/ehWY7hZae+4autXbJsjif4DIp4c5KkaAihSVNw+yTrOpkWw9DZDLRju5F3cQg107ei0WYhFa84m+RI1dE3hbfwQclbWD+u/3pZL12m+M2Hdjfx0dvMRX/WPHQibXijZx9OwtB3xDiW4vpSPMWKYwslfrh/btLbp7gG9fMasKI4gEhEfDVujl270RBqbtyqeHvLjMV2TK8mPNnTrPRnTZ/pW7GqtFQZV03tHdfHycKNSpxNm3yJ60saK4SeqfjodfKyvXSZdr+mRWqEjmmuFTtZVsf95pxpu1vpKB8T+hrHeKQZx+T8Xm0n+k+G47Ox/Gz4ZNsPTYu12Vwrss7KteVxX5TL0bfd8FHGMV2Q4bfr3SDGvWs4KHYdLBxlkSvlIa85m9rdovRMx4hMOfs9blhUKaeHTPXT78kZPEf52Q8zbS8Znqn/6HWzGFvtnp+94KPqFEGHy+cWWU6utjOd9ZlNe8l18uq+k5CT4fOPqd1t3oVMfczi3mJK46Ifymz07STrk1qsbv04xLvTVVMcvQ8mZTh7XtXLmDWpBhCL3Op/p99A+7xZlnGdO05L6SzCb5j46DKl35E2zjt1yRmt15fUtK43vX7ekBVxe10IGab+nNX17pE9atYq1IeasUHY+7o2IFh0Gi3dDagq06clyLX3ZpsGaG84Zi0lGg2gY+1/RXWzbHSWxcYM0l3VX8LpliE0VvnTKaLRL2JtcCzU24GsBzTDeFfXWDRv+C+oafkPW100WXPHotlgwE5K+zy6msW/L2Ff/Z9wcIW1Yd0rOclyvd2aUpy8lcRiX9lWWCnY9ABjaFJtcY+BajQfDaHmVnvjqCLUOLhJMrsGmtHV2ox9NkYbkz5GwTne115aXqpGs2x0lnUIiMVPxD/7OsnJxbZiLJTYYKALza1BNFs8HBpl6PvKTUSSlY6znt/L33T1OmnT5kKHfGt3Ixc3nLW+k2YRRPX6OoDGFDfaD7sk47NQcKAZG17QHDVQ3dqcnDoY6ELzu0GgeNBSXiRch5VH48ZqQ0U1fUQfNLzIiHAlk0SsbP06uHAawpfEZjJjQqpo19Ny3L2iSakN21IfFkJUPv6PPwnFLTa0PvCGxFmkKW9J+aHGQkzKQ35xFoW6aXd7ZadgEjqwVh8b5XZzMY7Zl5P6jJfjs7d8YnpnMm6Ult2O0FHpA7AWs7IWZcXWDETd3zgtTxn2d5aHH3ysa+bv0ULk7Oe44W9ruJeezfOG+1LNOf24LjIZN8waxY645eP1c4udfsN13Iv28uq+Y3rule+jWTz/eMU2m34o6iY+TivhUWTFxLuTWOCutRmnF1g/q4rkWbWXKQxHM97oFQYtWRHx/mG+l04ap6bxes9Nvbwe5/0Ile6mXka2hXR9+fW84QVnE/eBbZj7woaYI5M0FmVqT3BrjxL9e8XBIUyqm4NqzWjXjOrgSdS0HLC18RnrkOk+Q3BkSsyH9ML4vH3uV1IYn30o1BORn0dz9X/DtojZcByrUyrjszMFvJLjrDRnqbQLdWlygSHZcCUWR9AXHRpaukIx+MjStUH+mSA26EYl+aRxWxi1WoOos+BsTAq8gbX6IGY+qR3pOtqEdsPdz/QwZpM3V4dj+qQwPrtRpKcawVZr419C3MAGBPc7mULojnOiHC83HNSr6+hKbBuwuk4j2P6Sw37opc6OZeWAs7i+3phryUdTc2ADqt+1+KgUaI4Zny3qYjUNNfY1Pk3/E7ICXdjwQgp9LMpzckiODZZv408q/TVdjWNaeQu659kvUpNKnt/nZM6iLM/bXfS7F9OMjY7HMXc0vByfPeejVSmzcUMsdvOgPGU4HrPSlk7vG8n4liLRVbdjtk8LqvrDx7Zmvp4gZ3u8xnHDPuUwn8niecNLzf25LjIbNyzrkys+6Z5bLJXz9qC4DxgXj7Prx160l1f3Hf+ef7zi674fxhgF7Y3PDlXMtr2swnCcHOg1l57De6koPNt6mSuQ2RG768NKCq8vKyrOj/nxvOFP/3F/vXthj5KJVjUeRHdLTfxQF5qrizBnW0RO4tk2DdCeoXQvqGPtV2Ju75KIUM1FdHcPYWjoj9q/wcE/obvlMuprPpNS+bQZ+gw19ZfR0v0nRQehS3f3ZehdM1b657GvzaIbdYwx1OlTTZ5eH/E7KOSnq5MHcsQgvu3FcRj3TIb/GutMhlqviHfsr1ZfYIXX44LuhPF6cGkLagzeV81vrE2vT0984QcRCiAuT5Ml2x4DzXiyy/Ag0NukGsOLa9CydDChT/eCGoRkGcJTeM0ghuoOamEIvOKSkGPUJyr0SfIRRv7Bpd3oXlCP+vJQIpujDcFmerem/+CabrRMN+Q//aS9MVIvwCVnX1f1ldt8TTfq5dh8wqgZtjCsGzm7aHcxFV2+toqeNfRtUfaLRUoaPX3ajyouOWvNdJVo53qt33RLfVn0ne6l9aiR+3M6g1N8ir3oL/WG6xLxa6Fb7kdiuqH0kUd4s6w8KhmxpbaK9eUWVa5gtj8Zh0ubhqf3PZxUPZsTx40baixu+ct43o0/RtXj+4kXTskjQOM9zz4skY0oR4e95pxtu6dU2otxLGUBKU4axw2X47NvfFyMG9qUYanKXT1tiEjXsHQKpinD02ttPzbL+TLdzpaPMAI01iU/louxxhizVUxdFsdN/+ZXZaquo/RecvbqfpotZ6/HDUcghzuRdA/T7ospnje87ofZtpctOhfjhq2sDPgoMjx+blFke7gjQlesNTovRGvwYCg5I1QvzrP28ui+49vzj17hbH+z6Icd+80OJSHxwV56BhbXa6p3J6/aSx0XAat7au/ASYVWqHy26V46Gsd5Xl9Kt3C94+XzhlfXhakyWVzvJlkeHCirasRgdz10y0jXhiDm1LXbPg+7LdLCcuhWFPO5IRCNfBlPGuJd1LT8CQcb/4oyaUp4IBBFWdVfsKLxz765wwv9A4G/ovHgn9G44i+oKosqOojzZWV/QUPLp0pVu06bI7n0njaEEqn5qyZPzhgQ8uN1sgu/4ZUcudzh3haD2JNi+q7+p0+5l+IPB4qr0HBXi8FIZmE41mXIv3F5ejxjTdat6mcD44IQygu1yD+vAVXFSatPWVkjtt6oD0ciHIBY7Vou1Ntt40OJtiiFYQVlsUhDWdkKrJh/MKOp+DW3HtBi9IoYuuLr4azQVpPxT6zYnPbPBee0MrNIoNUr3odEvWor69WPBoOnTTeQfGt3y+q74Ky98C49iMbKFagS/UTqy6KMsuIVaEhzTSi6FNdja6hU6y9aOAzpZE3c+BQLvyOdkDY7wvGpVfFjcluJQ+IarTVe71oYgFgGRXagC6eHksKVDwBvtidPDJ6G+mgfO5WP409SaXXL9DIlPpDc5Y+xT5TsJWchL9t2V2moe56NY6pYR3tejc9+8hFxLuvvFqF1YnN+094HS1ep9wGbe5zwntPCb+ikojW41Wxz0c9m9esrn6w0yyJzAXL2etzIgm7Ossr3MKfPG14p5+t1kem4YVMpN3w8f26x0c3VYYMjQdA4C0fj1mAd4zfL5x9dXy/uO74//+jKZvvroh+a6ibiKS/oxsH5VcozsLhe9XcneQavrrJX15cWAkF29JCeaUVZJg9fnxcsz7Zevo7zvL7MjnF6h8zm18PnjWz7T8pquLjeU8rL8mSgbAUOdLegJm728WNxQhqgs2ykbLP3tn0Jkl8cUHMRjT4vMpitzrj+s8SXEU3Wyc+ZDFumMprHYs62L6ZPZ8poOOCVHIPYXO72RvYpiw1i0oOWXsTiYXSVbPTV4pKlWEk4Xgnx4CsWR3P/NyUWc9YgQLn5Gs75vttTjTnh7L/ACW8v4wOXeBibbfCiNhroreqXPWcrqe6OWdUrc0l52O7iATrr/mxDYtyktAZ6PaeVV4Z2rrgeq9IYn0wGK5s84npXVg6XDc1GXeOKiQd4Jc6zwfNa118Y4/S4evk//sS0FlPdqnsSNQCEl+1d1i+4UqrsNj3k7Em729TG6np3O47ZFJHZYRfjs598hPKZjhtW/KxC6cAwZTh04ypLo0tmAM2p/eZjLjE3RwqSs4fjRm5aIbtSrMaf7CQ6z+33dZHpuGGlua98jH3NwrHASiffjokFmRd0o/sn1u8dvraXi/uO388/XnF20w9NdStvSXyAdaqXp+1VNBu3XyWVLD/TisODbdh3QTrvYygrT+plvPbiqrt5DpdqnXqT11dqPmnOevW84Un/SaGrm+s9hThPTgXKqtBwoBv1uu+hWJxw7lq0OwoHm14Fs+tq+jxM4REBEd+4bZ/qKVxzq/AuzsZ46I1yQrfeji+h6ckv4STEAoiZyS2d/SlCG8YoxvWuDWMR3DAWCH2Kmtv/ilWzPzV5WBtL8UKOGIBWLB3CCqPwYdr/cECFWTPJPpZpzOgrpdceNlNMP7fxxorFpm50WOPYFP8qQ6gBk/eBQ2lukpkWDxDG93erEXwXQHENaspvxaqyWcpX/bTlpPi6Pjyc02rsLEGKejkToKdy1+5V84cgOeRqi4qsfUYKwxH/suvqo0iW/Vk8GPb2tqHp3X04CbHwil7X+K/DoVYOX6FIMCzsp5zTdwwGKzF7IPjsBv1syl8tZp7w4CyahCniGoinThzXH+D1egSkhV6GTksfupIfF/J//AFwei2CPXpthauM8Gb12fgs2HrI2WiodNXuVr0jxfWe8ThmJd/BMU/GZy+uCztdXY4bpaEHUXNUGrvEB515sxIGZjGebH9XmrKWoi3sVHN83E8+jpXwJ2HBcfZy3PAHuXdS/ezzTrT087pwOW4oanvAx6vnFkUvv3YGmrHv9K1YIc3gVIrysL28uO/4+vyjVDyLHRf9UPQZYzzuVO+Wttp52F668W+D9M4rPuo2lsXDOynPqICto4etshmc8KJewzHO8/rKoJGtk3ryvOFF/7FWT3OusZpFl5ndxk54dsfFNVx7oBuT1q6MLU7YFVucsL77AFZIURrclEIPaDfUfMvzGSZd75twx4Ij7VdgbtFXEKweg+auzI3PoqBA2V9woOVT1VNa16DrS2gWxujgOIwb9zeoa9etJ3qC5K9XcpISh3dLPCQoHovp1DF+cb1wGul9oNMJNZ9X4iRp8WfXol1atE5b1VuJYevftGOhnYj5deBWQ9xpXe2BZjQLY7SIK9w4B3URDwLkGznrZRX4b761u5e4RZ+d+0wQwdYNaBYrfhuNz14W5rus6zFJ8iTRPfRNHi8AdK9N5YPRVZMgbi35Ov4Y8TX3SEY+EX/7xq1ZzuowlmC37w1nO+m+H8/ROJbz8dl3cLECzLMQxAcdqXD9g49+yGb2kn6av9YECo/zCB83rJuJR4eBQN4+t4iPwEsHYbVuSldPtcNF0rMDmu19Z6Q8/2RHKZ5bmvXmiTyXQkxhOKRZekoIQAC2jh4uy/Y+m4/jPK8vwCf7RuE9b3jfs1NJFEZoP/5ogPaD6giWGdn2NwhWG8KCuKxPoOoSDnT/CS01hpAdirzPo7n6Kxg358u24Tm8kqMUyx2VgClOUjOqpYXjgq3NkjelMAj5M+1YVkp8/TtwdzdaykNqqAQlUReaW4MY9+I22/4jJ7fdNnyJt01XaCfysN29QKytVGzos17IHS4Z4gFAiT2tzYSQPF6KpWtEesBP6OvEUzuRePg3asoNMevfDebmBXekc87hOJbT8TmHXVL5KCd90BEqGD/4uPIwy2Fd8rmoQuI82sbnfO5XI1m3kfDcIvp6VeVBKIsui3Hy3eSiyX62QaHed/xkNqyyjWE44otom0Ia2Hh9D6vuhsJzMc7z+jJA92i3kJ43PELiSIz4aLdtzriY97PIEapBiwfez0IUQ3A4aoJcJfo8Tgv3Vn8+NqStRDT6RTRtUEOCiHAZLVsvKwsIioUT5wbV8Bp2wsVCg1WNf0ZVIxCNfBHb276IfRssDNxdY7By+6ewW4wwGzniAtpuXK3ZTmH5uIg5+pMcTPuWyzRuGw0KcU9GY7Js9xM3VgdeomJFZbEgWy7+xEKDVfMPomo+EB1ox/bIG9h3VDWGa3oMbMDKrtkZLUaYC/3zvYx8bfdsuIkH2ybhrS9PrBCL181bpS1IqMsWC7bMfUFdHFA/5+tveQuG5senIGZQkLaiuByWQvfGFPUsehAPFlXHYibHw3DcKk17DBW7nFqTo/HHhGFSA1rQrMSAbn5jLibdbR1n0pQ/iwO+cXbZ7llUxfesno7P+cJH+yjXjA36vTAehmMWetUpzrl+Yc4XPl71qgLj7Nu44RXvQpVTINdF3j+3GPqPaVq7tmhrLRrTvb960F6e3ncM9VJ2h+v5R1HC5Y4eb9kQSjFjaVm2l+kdI9AFsch7bZlhkexJtyZCXWWso5sMLuuVq3Ge15ebRk2Rx+vnDZf9J4WGeXcqGmnH2pXVaNajIYbq0X3Au0Xg6QE9jE0eCEQxSQT2lP6a3/iStJfjzd7P4aRS5KdoOXBRMT4rpzPcCZT9FStWXMLBoT+iu+WyKTxH12ln30O8kpOh+p4kT9yMJWn6lHnpUGJTmUYvjvrkyags+BUNocbodRwNIVRej5al8RWVA7J1L6GuaUP7evbmHIx7Zpz2b86b7hcSDBRXYUVlIw7WDaF7Qb3JK1oPS2BSwsEB41QwX2OROdAnV0n8avdc6W9ZzqDhwVZfvK443ZuRpbTsDxrDIlh5KGdaipiqJr0cCU9M+Qu/GFPkUD/61MZ8HX+sqj9rXjfq5ZcnERrohbVoj8rLqlvlTB7LevxxyVnTwI92T1bNcivTcSxrPpIWGY/Pw8BHUtd2U1wjyqK02gcd84JJvs8CylM+Mrhs+k9Bc85m3JABx7ez4WwhbmQfGgHXhSvA+fbckqYSpmntdl7QPrdXJved4Xj+Mc6aSYPV9elM62ZbkA/tJT+binK7etrQEdmnzKr1fTaRD/XSQka4eA63ZS+d4PUlwfBgM+vnDT/6jwf18ktENLINc4NJ43OopsVT47PQmwZov1rPodxZ2qKDUuLmsSljIkspvd/88POJRa404aHPtLihckFiccLtK515P8v5jNtlVX/Bg+osa+Dk5zIOo+CVHKN+fu4bb8Y4/SS2SfGW9bITHhH6AQB+3KTFy428eIWIt9o4/6Bm6B36yRC0f3UHcXD+CsWDVFLLclPI1TzPJa9NESsuuL/DMn0mB8vKVuDBSYYcLlcDj0bqFE9LseDZ7WW58fA21CCnu361e04rYVWY9EConbaYNaD1zf058n42TkEMNOPJLjmwrFUlzMdiC8zpx0/iSX0xNN0TU35AOv0k9ulJDb/5Nv4Y1EvsigfG2rtaUCPbmwPNqH7J2VRft+OPV5zhUbsngKTZyHQcc8snjRraaUfjc475ONFbT6N5H0n9TnzQUQwJubhH5IiP2w+3XvSfQuLs2bihd8L4rxecDSLzctdxP8zRdZFzSPn23OIAgOlZ4sI+tA0aMuawvZzcd0w6+/j+Je7JwXd190EDFx92TXXTYnNnuEaOH+1Veqv6HHdhH56U3guhP8P6wCQh0qN6+TXOJ/SUNkztyetLopP5ZlbPGx71n8y1zn2OSHsd5gY3JOyBofpuHGysQplDx0OnGtMA7ZSUX+lmXUZ9SBUuYiLPqfsiIpHkG5Aw/Ebav4xtdWLRvuRxNWeWe9cbYjVrYTGSXUSUv3buV7DBwf1UxJKeU3cFtrWLegRMhmWx0OGT6jpTCN3+qamDeyFHGDJWLI0bUnWDqpPfukZ/pgQZ4+5qnn1zsU1aTE+Em1j7UjWaZUfj4nqs8sUu+iFOX0j2na6jKzVdIgMRiH+u//QQAUYBDjxAhWfunDfrsC3SrukQMXg9ioVanjytCnbitXx6KGn4Ey922oIvbxg64qhZWMqndlebJfd7siFWlK6FZ0m2eySyDWvdhORxWRPTl3fhAfJuEHPe3IZ2w/UVFddcZBu2vSn6f7taolyvgLSoom5glx+Q5PPGxWjybvxRqynvCS+QhlsNXyoHNjj7iOV2/PGIs2ftLgOJb3syjrnlA8CL8dlPPhbIMjpk8j46/SRWyi/MObhH+MVHm0Is0zAYKcR9sT1chzlhw/gj5xHbWfQfXVRBcfZo3NDZJH494JyQlUcbbvuhX9fFsKOR+49QZpifWxzxsHqWCKsOJl62lxf3HVjp/ELm71+iXsq6HOI9Lhz7OB4bQ+egqNXwbuEIahaJjHUTXumt4llTvEcl7Qaxd5/Yc2adZGcQJXvZXnpNTOO8/HwqEuUg/IZn9ZKvU7kemT6H63BS/RrbU+tjo+P6SoXF7TlTP8zguc6z/uNW+Rzli2ybg2B1c9z4HEJNyyAOrvBn9rCzmAc5qvhoLEaE4ajdehn7DDGVu5rHItg81hJJza3mw+11V6I6zb2ua8NXMG6DnPcz1Hf/B1aUxa2cpZ/i9tAYdEkGZnMeOX/q7a7mL0H8U4q0zfIpHqz9T3H7M6XwSo5J8DAdEANZ7bx67JPjz4obS2vQnpUWRsC72Dtq1eMr++oxL9PoEiquwe3TV2F2aanpg4EqN7u9rp5miH+2/UfuKtEaPJguLnUgtmCh6TIxyGmZNys7xVPkbn9znOptbZFWGCfHvSudEKsj+xL7Nj/bXaq5u03NELsBXXp/jht8FabuJLvOVRraivqeYDKurDYNcQOqe2x7N1BuKK5oEkTEJml41hIkP7zEQgdskGI/xyRMUV6U8m/8MdTTsCsWHeqeflL1JOqpxpxwtz8x3z3iLKrhSbsbeCAPxjGhkhfjsy98jLxc7s+qrEfodHyWhPyi6WAmklfjvB98SstuR+holzL9WRgpTPfFcosHTZcsU2UrGM4ejhupeGVyThjsTB6Y8vOOeNZ7sUh9xspRbMts+qEf10UmXH1J6+Fzi1fjT7p66kYZ5ZlDeBRXzsKK4mRH87K9sr3vePn8o4xdAlbPBgRTPdOlA5rlecu6affqagR7rIXXGGeS+vTconnz9pjuMppS6Wb2etWfPemHORznR/v15VW7yz1fuWYzfK7zpP/IyuTRtvgotX1uUHIwFYsNNqBKtw/6oGvSvdUH4RTpjECg7C840H0RNQZPaGe5vUulG8PTqRGquYh6g1NaVlqEPkV998XsvY29kpNVZZxlDhSvwIG7zXGMLXOLBdTu9m8xRP2hJZT8QG6phn6wa6BZM5YHX0oTj1X2yNQzi1+vv3YX16DeAz4ixnX3cC86KXPyedu3dvdZ73TindZLLKZZbzTyphPu8rym013daClPN7qmKiD+wUBOYggFoL3UG69j3TNDypdP44+klu2m9uAnx4MWL1XvBmH03lEEuB5/POSshRHJtt2VWqXdcTyOueaTVgU1QYrx2ZvrQi3Osz07PrmYLhyvhB98tGvfOKvADTQ7Ppne3+3kjDjO3o0bSnPY8cmUsyJ0+Hey6Yd+XBfDTUSr07z07wS5fG5xwsQ4rV18IBWLzMl/w9Jeqe47Hr1/iT689cYUz3RiPZ0F4v4v0/B3O/Zs14Iaw/NSJqX60l7GMBy6QsYZevpxH369qZdP47xNfUfz9WWDJLvDdvdTB88b3vSf7NT3LXdHU9L4LBYbHPTX+CzqQQO0b62ZmWCxsF7jwdjifPU1nyFkvKeFPkOo5jLqW/6Exqrkl+XMSkmfOmYM/xNahA5y8nj5Ld1DONj4V1h8NJVTo2zFn9HdIgzVnyIU+kw5p+1I9Rk6eCnphW1I6ZUcg9i82NUeFH7SrS2oV1Os0IaIQyw8jcWif0NLG1EleRP4pfyUqzKUPNCM6hTxnPXBul4yuomH524HHsZllQfRvUAYCGsQMrIRahbHFkWsXxDjI3tbpKyFUVZcTsvSQS3GtdcxjlLqkicnvW73fKhW7CE8ZvhTPqwo7V2Vdhzzsi7ieqiafxCDS1tQP130a4N07ZoXC4DWo0X06/lVSgKRX5nyKc5edTtmF0nJrB6ubBYuzbfxR6qFaTM2lhjiQYvppW/MtYyfLwS4HX885+xBu9eKjxcL6mOLwxrHMFFZpV+vcDQzxS0fUZyX47PQI5vrwtRZPDog9FIWI4zL9X3xQYP+fvARswq0cUgsNmwYh8RzR/309PfpbPqPXMVC4Szq4eX4rDPyirMuL59+zkgGvgAAIABJREFUs+mHgks+jhvZ8M3H55Z09TFNaxcfh482mRYL9qK9PL3vCCO0B+9fMZ0Mz3P6ou13H0BjmT/T11O1i1iYsXFpbLF28f5lHOP15wXx/tRo4+HoRXupOloYbkUC4zOsmsnzvWzrJfL7Mc7bVXS0X192XNweF+2XzXNdtv3Hrd5+5wtUNaK7pQZ+LDZop3tgaGjI6C9ll9b18cuXL2t5x4wZ41oGM5IACfhDIDqwDXPlcCDF9WiZV2syeospGh1dK1EtL6ohQoPkqdewNqVEjvXrWygLf9rFb6mF2u5+c6N8EiABEiABEiABEiABEiABEiABEhitBK688kpXVacHtCtszEQChUOgIxyPcxmvUs10s/FZnBJf/maJ+JG+f7IqHLb5XBO2ez63DnUjARIgARIgARIgARIgARIgARIggcIhQAN04bQla0ICGRMQXsKnB9Vsze+uRftARDko0kUi27B2v2qs9jyes1Iqd/wiwHb3iyzlkgAJkAAJkAAJkAAJkAAJkAAJkAAJGAl8wXiA+yRAAqOHgB4PSVnFWsR2ftF6tWKFjAi/UTlLOcSdkUGA7T4y2olakgAJkAAJkAAJkAAJkAAJkAAJkEAhEKAHdCG0IutAAlkQKA1tRYu0UKAjUWKBxLsbTHGiHeVlorwgwHbPi2agEiRAAiRAAiRAAiRAAiRAAiRAAiRQ8AToAV3wTcwKkkBqAolVXQci6Ig04cmek8CFLnQF1Hyh4hCmlN+OW8usY0SrqbmX7wTY7vneQtSPBEiABEiABEiABEiABEiABEiABAqDQGBoaMj3JcUuX76s0RozZkxhUGMtSIAESIAESIAESIAESIAESIAESIAESIAESIAESGAUEbjyyitd1ZYhOFxhYyYSIAESIAESIAESIAESIAESIAESIAESIAESIAESIIF0BGiATkeI50mABEiABEiABEiABEiABEiABEiABEiABEiABEiABFwRoAHaFTZmIgESIAESIAESIAESIAESIAESIAESIAESIAESIAESSEeABuh0hHieBEiABEiABEiABEiABEiABEiABEiABEiABEiABEjAFYEvuMrFTCRAAiRAAiRAAiRAAgVD4Eu//CU+d/ZswdSHFSEBEshvAv957bX46+LFiH75y/mtKLUjARIgARIgARLwhEBgaGgo6omkFEIuX76snR0zZkyKVDxFAiRAAiRAAiRAAiSQSwKBv/wF//V738Pne3pyWSzLIgESIAFEv/IV/LmrC/9ZUkIaJEACJEACJEACI4TAlVde6UrTL7jNmElpugE6F2Vlotdwpr18oWM4i2fZJEACJEACJEACJIAvvPD/0PjMfkACJDAsBAJ/+hPGvPAk/r+fLBmW8lkoCZAAMOaqWcRAAiRAAjkhwBjQOcHMQkiABEiABEiABEgg/wgE+ofyTylqRAIkMGoIBP7y6aipKytKAiRAAiRAAqOZAGNAj+bWZ91JgARIgARIgARIQCLwn9/5H/jPm/6HdISbJEACJOAdgc+983/wuV//H+8EUhIJkAAJkAAJkMCIIEAD9IhoJipJAiRAAiRAAiRAAv4TEMbnvz60zP+CWAIJkMCoJPDFJ3bRAD0qW56VJgESIAESGO0EGIJjtPcA1p8ESIAESIAESIAESIAESIAESIAESIAESIAESIAEfCJAA7RPYCmWBEiABEiABEiABEiABEiABEiABEiABEiABEiABEY7ARqgR3sPYP1JgARIgARIgARIgARIgARIgARIgARIgARIgARIwCcCNED7BJZiSYAESIAESIAESIAESIAESIAESIAESIAESIAESGC0E6ABerT3ANafBEiABEiABEiABEiABEiABEiABEiABEiABEiABHwiQAO0T2AplgRIgARIgARIgARIgARIgARIgARIgARIgARIgARGOwEaoEd7D2D9SYAESIAESIAESIAESIAESIAESIAESIAESIAESMAnAjRA+wSWYkmABEiABEiABEiABEiABEiABEiABEiABEiABEhgtBOgAXq09wDWnwRIgARIgARIgARIgARIgARIgARIgARIgARI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)

    来到show.jsp页面展示

  • 在WEB-INF下创建views包,在包下创建show.jsp

    (二)分页查询

    1. 在controller层中传入要查询的页数,默认值为1

  • 在controller层中传入要查询的页数,默认值为1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @GetMapping(value = "/emps")
    @ResponseBody
    public String showEmployees(@RequestParam(defaultValue = "1", value = "pn") Integer pn,Model model) {
    //引入PageHelper分页插件
    PageHelper.startPage(pn, 5);//第一个参数是:从第几页开始查,第二个参数为:每页5个数据
    List<Employee> employees = employeeService.findAllEmployee();
    //用pageInfo封装然后交给页面,传入查询出来的数据
    PageInfo pageInfo=new PageInfo(employees,5);//连续显示5页
    model.addAttribute("pageInfo",pageInfo);
    return "show";
    }

2. 引入PageHelper分页插件

  • 搜索pagehelper插件(GitHub)–》中文文档点击进入
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //获取第1页,10条内容,默认查询总数count
    PageHelper.startPage(1, 10);
    List<Country> list = countryMapper.selectAll();
    //用PageInfo对结果进行包装
    PageInfo page = new PageInfo(list);
    //测试PageInfo全部属性
    //PageInfo包含了非常全面的分页属性
    assertEquals(1, page.getPageNum());//当前页码
    assertEquals(10, page.getPageSize());//每页有多少条记录
    assertEquals(1, page.getStartRow());//开始的记录
    assertEquals(10, page.getEndRow());//结束的记录
    assertEquals(183, page.getTotal());//总记录数
    assertEquals(19, page.getPages());//总页码数
    assertEquals(1, page.getFirstPage());//第一页
    assertEquals(8, page.getLastPage());//最后一页
    assertEquals(true, page.isFirstPage());//是否第一页
    assertEquals(false, page.isLastPage());//是否最后一页
    assertEquals(false, page.isHasPreviousPage());//是否有前一页
    assertEquals(true, page.isHasNextPage());//是否有后一页
  • 用PageInfo包装查出来的数据,查询出来的数据写到Model中
  • 在pom.xml文件中引入相应的依赖
    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.0.0</version>
    </dependency>
  • 在mybatis全局配置中注册这个插件:–》中文文档往下翻
    1
    2
    3
    4
    5
    6
    7
    <!--    引入分页插件--> 
    <plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
    <!-- 分页参数合理化,不可能<1,或者>最大页码-->
    <property name="reasonable" value="true"/>
    </plugin>
    </plugins>

    3.写单元测试(用spring的单元测试),测试利用分页插件能否取到值

  • 1.在test包下,创建一个MVCTest的测试类
  • 2.spring4测试的时候,需要servlet3.0的支持,导包的时候,需导入3.0以上的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
     @RunWith(SpringJUnit4ClassRunner.class)
    @WebAppConfiguration//获取webIoC
    @ContextConfiguration(locations= {"classpath:applicationContext.xml","classpath:springmvc.xml"})
    public class EmployeeControllerTest { //传入Springmvc的IoC
    @Autowired
    WebApplicationContext context;//传入Springmvc的ioc,为了能使它自动注入,需要@WebAppConfiguration
    MockMvc mockMvc;//虚拟mvc请求,获取到处理结果
    @Before//要想使用MockMvc,需要初始化
    public void init() {//需要被创建,才可以使用
    mockMvc=MockMvcBuilders.webAppContextSetup(context).build();
    }
    @Test
    public void testPage() throws Exception {
    //模拟请求,拿到返回值
    MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/emps").param("pn","5")).andReturn(); //请求成功后请求域中有pageInfo对象,我们取出pageInfo进行验证
    MockHttpServletRequest
    request=result.getRequest();
    PageInfo pageInfo=(PageInfo)
    request.getAttribute("pageInfo");
    System.out.println("当前页码"+pageInfo.getPageNum());
    System.out.println("总页码数"+pageInfo.getPages());
    System.out.println("总记录数"+pageInfo.getTotal());
    System.out.println("当前页面需要连续显示的页码");
    int [] nums = pageInfo.getNavigatepageNums();
    for(int i : nums) {
    System.out.print(" "+i);
    }
    //获取员工数据
    List<Employee>
    employees = pageInfo.getList();
    for(Employee employee:employees{
    System.out.println("ID"+employee.getEmpId()+"===>Name:"+employee.getEmpName());
    }
    }
    }

    五、完成查询界面构建(bootstrap)

  • 逻辑:先来到index.sjp首页,首页会直接转到的emps请求,controller收到请求后,查询到所有的数据和分页信息后,然后来到show.jsp页面,所以我们在show.jsp页面展示员工列表就行了。

    1.引入bootstrap

  • 开头代码
    1
    2
    3
    4
    5
    6
    7
    8
    <%//存入当前项目的名称找	pageContext.setAttribute("APP_PATH",request.getContextPath());
    %>
    <link rel="stylesheet"
    href="${APP_PATH}/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"></link>
    <script type="text/javascript"
    src="${APP_PATH}/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
    <script type="text/javascript"
    src="${APP_PATH}/static/jquery-3.1.1.min.js"></script>

    2.利用bootstrap搭建页面

    创建栅格点击进入

  • 根据官方文档可以得知:把div的class设为container即可设为栅格系统
  • 分成四行,第一行显示标题,第二行显示新增和删除的按钮,第三行显示查询出的数据,第四行显示分页信息
  • 每一行的class叫row,每一列的为col-md-(数字1-12)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <div class="container">
    <!--标题 -->
    <div class="row">
    <div class="col-md-12">
    <h1>SSM-CRUD</h1>
    </div>
    </div>
    <!--按钮 -->
    <div class="row"></div>
    <!--显示表格数据 -->
    <div class="row"></div>
    <!--显示分页信息-->
    <div class="row"></div>
    </div>

    3.添加删除和新增按钮

  • 设置按钮的位置(在列偏移中设置自己占4列,偏移8列)
    1
    2
    3
    4
    5
    6
    7
    <!--按钮 -->
    <div class="row">
    <div class="col-md-4 col-md-offset-8">
    <button>新增</button>
    <button>删除</button>
    </div>
    </div>
  • 美化按钮(根据官方文档,只需要给相应按钮的class增加值即可)
    1
    2
    3
    4
    5
    6
    7
    <!--按钮 -->
    <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>

    4.添加显示数据的table

  • 设置table的样式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <!--显示表格数据 -->
    <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>
    <table>
    </div>
    </div>

    5.添加分页显示

  • 组件–》分页,在里面添加分页信息
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    <!--显示分页信息-->
    <div class="row">
    <!--分页文字信息 -->
    <div class="col-md-6">

    </div>
    <!--分页条信息-->
    <div class="col-md-6">
    <nav aria-label="Page navigation">
    <ul class="pagination">
    <li><a href="#">首页</a></li>
    <li>
    <a href="#" aria-label="Previous">
    <span aria-hidden="true">&laquo;</span>
    </a>
    </li>
    <li><a href="#">1</a></li>
    <li><a href="#">2</a></li>
    <li><a href="#">3</a></li>
    <li><a href="#">4</a></li>
    <li><a href="#">5</a></li>
    <li>
    <a href="#" aria-label="Next">
    <span aria-hidden="true">&raquo;</span>
    </a>
    </li>
    <li><a href="#">末页</a></li>
    </ul>
    </nav>
    </div>
    </div>
    现在页面已经搭起来了,后面要做的就是把查出来的数据,放到页面中显示。

六、把数据插到页面中

1.引入标签库

  • 员工的数据需要遍历得到,引入相应的标签库
    1
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

    2.遍历员工数据并填充到相应的位置

  • 遍历的取出查出的数据,之前已经把数据都放到了pageInfo里面,在list里面存着
    1
    <c:forEach items="${pageInfo.list}" var="emp">
  • 把取出的数据填充到相应的位置,并为 每一行的数据添加编辑和删除按钮
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <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>
    <th>
    <button class=" btn btn-info">
    <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>编辑
    </button>
    <button class="btn btn-primary">
    <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>删除
    </button>
    </th>
    </tr>
    </c:forEach>
    这样运行就可以看到:数据已经填充好了

    3.填充分页信息

    (1) 填充文字信息

  • 写在前6列
    1
    2
    3
    4
    <!-- 分页文字信息 -->
    <div class="col-md-6">
    <p>当前第${pageInfo.pageNum}页,共${pageInfo.pages}页,共${pageInfo.total }条记录</p>
    </div>

    (2) 填充分页条信息

  • 分页条逻辑
    • 1.如果你是当前页码,让它高亮显示
    • 2.如果当前页码是第一页,首页和上一页为关闭状态
    • 3.如果当前页码是最后一页,末页和下一页为关闭状态
    • 4.点击第几页就跳到第几页(包括首页、末页、上一页、下一页)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
         <!-- 分页条信息 -->
      <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.isFirstPage }">
      <li class="disabled"><a href="#" aria-label="Previous"><spanaria-hidden="true">&laquo;</span></a></li>
      </c:if>
      <c:if test="${!pageInfo.isFirstPage }">
      <li><a href="${APP_PATH}/empspn=${pageInfo.prePage}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
      </c:if>

      <!-- 显示连续的页面并使得当前页面为高亮 -->
      <c:forEach items="${pageInfo.navigatepageNums }" var="pageNum">
      <%--判断是不是当前查的页面,如果是高亮,不是就不高亮--%>
      <c:if test="${pageNum==pageInfo.pageNum }">
      <li class="active"><a href="#">${pageNum }</a></li>
      </c:if>
      <%--判断不是当前查的页面,如点击哪页跳到哪一页--%>
      <c:if test="${pageNum!=pageInfo.pageNum }">
      <li><a href="${APP_PATH}/emps?pn=${pageNum}">${pageNum }</a></li>
      </c:if>
      </c:forEach>
      <c:if test="${pageInfo.isLastPage }">
      <li class="disabled"><a href="#" aria-label="Next">
      <span aria-hidden="true">&raquo;</span></a></li>
      </c:if>
      <c:if test="${!pageInfo.isLastPage }">
      <li><a href="${APP_PATH}/emps?pn=${pageInfo.nextPage}" 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>
    • 分页查询总结:从首页转到/emps请求,请求由控制器收到,查出分页数据以及分页信息,转发到list页面,使用c:foreach和el表达式把页面遍历展示出来。*

      七、用ajax改造查询请求

需求分析

  • 遇到的问题:此前,完成了在首页直接转到/emps请求,由控制器收到,查出分页的数据以及分页的信息,然后转发到list页面。使用<c:foreEach> 和EL表达式把页面遍历展示出来。但是,现在的做法,只能适用于浏览器/服务器的交互模型。而在移动互联网的时代,发请求的客户端不仅仅是浏览器,有可能是安卓客户端,还有可能是iOS客户端,如果是他们给服务器发请求的话,响应给他们的是一系列页面,他们很难解析。
  • 解决方法:服务器把数据,以json的形式,返回给一系列的客户端
  • 为了满足浏览器、安卓、iOS的要求,用ajax请求

    0.业务逻辑

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

1.在Empcontroller类中写一个返回json字符串的方法

  • 页面需要分页数据,可以直接返回PageInfo对象,用@ResponseBody就能直接把对象转为json字符串
  • 要想@ResposeBody正常工作需要导入jackson包
    • 去中央仓库搜索Jackson databind
    • 在pom.xml中引入相应的依赖
      1
      2
      3
      4
      5
      <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.10.0</version>
      </dependency>
  • controller中的方法为
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @GetMapping(value = "/emps")
    @ResponseBody
    public PageInfo showEmployees(@RequestParam(defaultValue = "1", value = "pn") Integer pn,Model model) {
    //引入PageHelper分页插件
    PageHelper.startPage(pn, 5);//第一个参数是:从第几页开始查,第二个参数为:每页5个数据
    List<Employee> employees = employeeService.findAllEmployee();
    //用pageInfo封装然后交给页面,传入查询出来的数据
    PageInfo pageInfo=new PageInfo(employees,5);//连续显示5页
    return pageInfo;
    }

2.创建一个专门返回json数据的类

  • 处理上面的数据后,成功失败这些状态信息,应该通知给浏览器。所以,创建一个通用的能带有状态信息的返回json数据的类
  • 在bean包下创建一个Message类,完成上面的功能
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    public class Message {
    // 状态码(自定义)200 成功 404 失败
    private int code;
    private String msg;
    // 服务器要返回给浏览器的数据
    private Map<String, Object> data = new HashMap<String, Object>();

    //请求成功
    public static Message success() {
    Message message = new Message();
    message.setCode(200);
    message.setMsg("处理成功");
    return message;
    }
    //请求失败
    public static Message fail() {
    Message message = new Message();
    message.setCode(404);
    message.setMsg("处理失败");
    return message;
    }
    //定义快捷添加信息的方法(方面链式操作)
    public Message add(String key, Object value) {
    //以下两种形式任选其一
    this.data.put(key, value);
    //this.getData().put(key, value);
    return this;
    }

    public Message() {
    super();
    // TODO Auto-generated constructor stub
    }

    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> getData() {
    return data;
    }

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

    }
  • 然后希望返回给客户端的数据带上状态信息,于是要修改EmployeeController类里的返回值
    1
    2
    3
    4
    5
    6
    7
    8
    @GetMapping(value = "/emps")
    @ResponseBody
    public Message showEmployeesWithJson(@RequestParam(defaultValue = "1", name = "pn") Integer pn, Model model) {
    PageHelper.startPage(pn, 10);// 后面紧跟的查询为分页查询,参数值为一页有多少数据。
    List<Employee> employees = employeeService.findAllEmployee();
    PageInfo pageInfo = new PageInfo(employees, 5);// 用pageInfo封装然后交给页面
    return Message.success().add("pageInfo", pageInfo);
    }
  • 此时在浏览器输入/emps请求,即可得到json的字符串如下
    1
    {"code":200,"msg":"处理成功","data":{"pageInfo":{"total":1150,"list":[{"empId":1,"empName":"selenium","gender":"M","email":"895484122@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":2,"empName":"9a1820","gender":"M","email":"9a1820@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":3,"empName":"f893d1","gender":"M","email":"f893d1@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":4,"empName":"409742","gender":"M","email":"409742@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":5,"empName":"dd0e63","gender":"M","email":"dd0e63@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":6,"empName":"270e54","gender":"M","email":"270e54@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":7,"empName":"1dc865","gender":"M","email":"1dc865@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":8,"empName":"c7a8e6","gender":"M","email":"c7a8e6@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":9,"empName":"f78987","gender":"M","email":"f78987@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}},{"empId":10,"empName":"1c8b78","gender":"M","email":"1c8b78@qq.com","dId":1,"department":{"deptId":1,"deptName":"行政部"}}],"pageNum":1,"pageSize":10,"size":10,"startRow":1,"endRow":10,"pages":115,"prePage":0,"nextPage":2,"isFirstPage":true,"isLastPage":false,"hasPreviousPage":false,"hasNextPage":true,"navigatePages":5,"navigatepageNums":[1,2,3,4,5],"navigateFirstPage":1,"navigateLastPage":5,"firstPage":1,"lastPage":5}}}
  • 以上就是我们需要解析的数据。接下来,我们就从首页出发,发一个ajax请求,拿到这些数据,对这些数据拿json解析出来,使用dom增删改的形式把数据显示出来。

1.index.jsp页面,直接发送ajax请求进行员工的分页查询

(1)准备工作

  • 原来是要发请求跳转到别的页面,现在直接在index页面中显示
  • 把原来的的index.jsp重命为index1.jsp,然后重新建一个index.jsp页面
  • 把show.jsp页面的代码全选复制到index.jsp内
  • 把分页的文字信息删掉,分页条删掉,表格数据的显示<c:foreach>也删掉 ,以后的分页文字信息、分页条信息、表格的数据显示全是对于json数据的解析。

    (2)从index.jsp发起请求

  • 页面加载完成以后,直接发送一个ajax请求,要到一个分页数据
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <script type="text/javascript">
    $(function () {
    $.ajax({
    url: "${APP_PATH}/emps",
    data: "pn=1",
    type: "get",
    success: function (result) {//请求成功的回调函数,result就是服务器响应给浏览器的数据
    console.log(result);//到控制台,查看是否有数据
    }
    });
    });
    </script>

    (3)显示查询信息

  • 查询界面的显示
  • table的细化
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <!--显示表格数据 --> 
    <div class="row">
    <div class="col-md-12">
    <table class="table table-hover">
    <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>

    (4)解析list里的员工数据

  • 把员工的数据和操作的按钮一起添加
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    function build_emps_table(result) {
    //因为发送ajax请求,页面无刷新,需要先清空table表格,避免重复出现
    $("#emps_table tbody").empty();
    //得到所有员工的数据
    var emps = result.data.pageInfo.list;
    //遍历员工数据,回调函数:第一个参数是索引,第二个是对象
    $.each(emps, function (index, item) {
    //alert(item.empName);//测试一下能不能拿到员工数据
    var checkBoxTd = $("<td><input type='checkbox' class='check_item'/></td>");
    var empIdTd = $("<td></td>").append(item.empId);
    var empNameTd = $("<td></td>").append(item.empName);
    var genderTd = $("<td></td>").append(item.gender == "M" ? "男" : "女");
    var emailTd = $("<td></td>").append(item.email);
    var deptNameTd = $("<td></td>").append(item.department.deptName);
    /* <button class=" btn btn-info">
    <span class="glyphicon glyphicon-pencil" aria-hidden="true">编辑</span>
    </button>*/
    var editBtn = $("<button></button>").addClass("btn btn-info edit_btn")
    .append("<span></span>").addClass("glyphicon glyphicon-pencil")
    .append("<span></span>").addClass("btn btn-default btn-sm")
    .append("编辑");
    //为编辑按钮添加一个自定义的属性,来表示当前员工的id
    editBtn.attr("edit-id", item.empId);
    var delBtn = $("<button></button>").addClass("btn btn-primary delete_btn")
    .append("<span></span>").addClass("glyphicon glyphicon-remove")
    .append("<span></span>").addClass("btn btn-default btn-sm")
    .append("删除");
    //为删除按钮添加一个自定义的属性,来表示当前删除的员工id
    delBtn.attr("del-id", item.empId);
    //把编辑和删除放到同一个单元格中
    var btnTd = $("<td></td>").append(editBtn).append(" ").append(delBtn);
    //可以链式操作的原因:append方法执行完成以后还是返回原来的元素
    $("<tr></tr>").append(checkBoxTd)
    .append(empIdTd)
    .append(empNameTd)
    .append(genderTd)
    .append(emailTd)
    .append(deptNameTd)
    .append(btnTd)
    .appendTo("#emps_table tbody");//把它添加到的位置
    });
    }

    (5)解析分页信息

  • 显示分页信息和分页条信息
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    //显示分页信息
    function build_page_info(result) {
    //在append之前清空分页信息
    $("#page_info_area").empty();
    $("#page_info_area").append("当前第" + result.data.pageInfo.pageNum + "页," +
    "共" + result.data.pageInfo.pages + "页," +
    "共" + result.data.pageInfo.total + "条记录");
    totalRecord = result.data.pageInfo.total;
    currentPage = result.data.pageInfo.pageNum;
    }

    //解析分页条
    function build_emps_nav(result) {
    //清空分页条
    $("#page_nav_area").empty();
    var ul = $("<ul></ul>").addClass("pagination");
    /*构建元素*/
    var firstPageLi = $("<li></li>").append($("<a></a>").append("首页").attr("href", "#"));
    var prePageLi = $("<li></li>").append($("<a></a>").append("&laquo;"));
    //判断有没有前一页,如果没有前一页,设置上一页和首页不能点
    if (result.data.pageInfo.hasPreviousPage == false) {
    firstPageLi.addClass("disabled");
    prePageLi.addClass("disabled");
    } else {//在元素没有被禁用的时候才添加绑定事件
    //为元素添加点击翻页的事件
    firstPageLi.click(function () {
    to_page(1);
    });
    prePageLi.click(function () {
    to_page(result.data.pageInfo.pageNum - 1);
    });
    }

    var nextPageLi = $("<li></li>").append($("<a></a>").append("&raquo;"));
    var lastPageLi = $("<li></li>").append($("<a></a>").append("末页").attr("href", "#"));
    //如果没有下一页,设置下一页和末页不能点
    if (result.data.pageInfo.hasNextPage == false) {
    nextPageLi.addClass("disabled");
    lastPageLi.addClass("disabled");
    } else {//在没有被禁用的时候才能发请求
    nextPageLi.click(function () {
    to_page(result.data.pageInfo.pageNum + 1);
    });
    lastPageLi.click(function () {
    to_page(result.data.pageInfo.pages);
    });
    }

    //添加首页和前一页的提示
    ul.append(firstPageLi).append(prePageLi);
    //遍历查询出的数据,并添加到ul
    $.each(result.data.pageInfo.navigatepageNums, function (index, item) {

    var numLi = $("<li></li>").append($("<a></a>").append(item));//取出来的当前元素
    if (result.data.pageInfo.pageNum == item) {
    //定位当前的也为亮色
    numLi.addClass("active");
    }
    //给numLi绑定单击事件,一点击,发ajax请求,去当前页面
    numLi.click(function () {
    to_page(item);
    });
    ul.append(numLi);
    });
    //给ul添加下一页和末页
    ul.append(nextPageLi).append(lastPageLi);
    //把ul添加到nav
    var navEle = $("<nav></nav>").append(ul);
    //最后把导航条添加到要显示的nav中
    navEle.appendTo("#page_nav_area");
    }
  • 需要把从index页面发送请求的方式改一下,改成跳转页面的方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function to_page(pn) {
    $.ajax({
    url:"${APP_PATH}/emp",
    data:"pn="+pn,
    type:"get",
    success:function(result){//请求成功的回调函数,result就是服务器响应给浏览器的数据
    //console.log(result);
    //1.解析并显示员工数据
    bulid_emps_table(result);
    //2.解析并显示分页信息
    //分页文字信息
    build_page_info(result);
    //分页条
    build_emps_nav(result);
    }
    });
    };

这样用户的信息就显示完全了,下面用ajax来完成剩下的功能

剩余项目

  • 剩余的项目都用ajax请求完成
  • 用rest风格的URI

    REST风格

  • URI*
    路径 请求类型 需求
    /em/{id} GET 查询员工
    /emp POST 保存员工
    /em/{id} PUT 修改员工
    /em/{id} DELETE 删除员工

八、用户新增

0.逻辑

  • 1.在index.jsp页面点击“新增”

  • 2.弹出新增对话框(模态框)

  • 3.去数据库查询部门列表显示在对话框中

  • 4.用户输入数据,并进行校验,完成保存

    1.点击新增,弹出模态框

    (1)在页面中引入模态框

  • 去bootstrap官网拷贝模态框

    f2bbb229f93b8f2aafe278cc863bab71.png

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <!-- 用户新增模态框 -->
    <div class="modal fade" id="myModal" 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">Modal title</h4>
    </div>
    <div class="modal-body">
    ...
    </div>
    <div class="modal-footer">
    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
    <button type="button" class="btn btn-primary">Save changes</button>
    </div>
    </div>
    </div>
    </div>
  • 为新增按钮添加id

    1
    2
    3
    <button class=" btn btn-danger">
    <span class="glyphicon glyphicon-plus" aria-hidden="true" id="emp_add_model_btn">新增</span>
    </button>

    (2)点击新增,弹出模态框

  • 为新增按钮绑事件:结合文档中模态框的用法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     //添加按钮事件,点击按钮弹出模态框
    $("#emp_add_model_btn").click(function () {
    //清除表单数据(表单重置(表单的数据,表单的样式))dom
    reset_form("#empAddModal form");
    //$("#empAddModal form")[0].reset();
    //发送ajax,查出部门信息,显示在下拉列表中
    getDepts("#dept_add_select");
    $("#empAddModal").modal({//点击背景不关闭模态框
    backdrop: "static"
    });
    });

    (3)把模态框需要的样子定义出来

  • 对标题、按钮修改,添加表单(水平表单,在bootstrap中找)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    <!-- 员工添加的模态框 -->
    <div class="modal fade" id="empAddModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
    <div class="modal-dialog" role="document">
    <div class="modal-content">
    <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span>
    </button>
    <h4 class="modal-title" id="myModalLabel">员工新增</h4>
    </div>
    <div class="modal-body">
    <form class="form-horizontal">
    <div class="form-group">
    <label for="empName_add_input" class="col-sm-2 control-label">empName</label>
    <div class="col-sm-10">
    <input type="text" name="empName" class="form-control" id="empName_add_input"
    placeholder="empName">
    <span class="help-block"></span>
    </div>
    </div>
    <div class="form-group">
    <label for="email_add_input" class="col-sm-2 control-label">email</label>
    <div class="col-sm-10">
    <input type="text" name="email" class="form-control" id="email_add_input"
    placeholder="emil@qq.com">
    <span class="help-block"></span>
    </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-3">
    <%--部门提交部门id--%>
    <select class="form-control" name="dId" 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>

    2.填充模态框中的部门信息

  • 需求:希望部门信息是从数据库中查到的

  • 分析:弹出模态框之前,发ajax请求,查出所有的部门信息,把部门信息显示到下拉列表中。

  • 在index.jsp页面写一个查询部门信息的ajax请求的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //查出所有的部门信息,并显示在下拉列表中
    function getDepts(ele) {
    //清空之前下拉列表的值
    $(ele).empty();
    $.ajax({
    url: "${APP_PATH}/depts",
    type: "GET",
    success: function (result) {
    //console.log(result);//打印在控制台,用于测试
    //显示部门信息在模态框中
    //这个是按id找的,也可以按标签找$("#empAddModal select")
    //$("#dept_add_select").append("")
    //此时需要遍历的路径是result.data.depts,函数需要的变量可以写在函数的()里,如果不写,用this也可以
    $.each(result.data.depts, function () {
    var optinEle = $("<option></option>").append(this.deptName).attr("value", this.deptId);
    optinEle.appendTo(ele);
    });
    }
    })
  • 控制器中需要有一个拦截他的方法,在controller包下创建一个DepartmentController.java,用来处理和部门有关的请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Controller
    public class DepartmentController {
    @Autowired
    private DepartmentService departmentService;

    /**
    *
    * @return返回所有的部门信息(json数据)
    */
    @GetMapping(value = "/depts")
    @ResponseBody
    public Message getAllDepts() {
    //查出的所有部门信息
    List<Department> list = departmentService.getAllDepts();
    return Message.success().add("depts",list);
    }
    }
  • 在service包下创建DepartmentService

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Service("departmentService")
    public class DepartmentService {
    @Autowired
    private DepartmentMapper departmentMapper;
    public List<Department> getAllDepts() {
    List<Department> list=departmentMapper.selectByExample(null);
    return list;
    }
    }
  • 在打开模态框之前,发送查询部门的方法
    f1f9a0d6b6c0402fd45dd8dfcdf86dac.png

3.点击保存按钮保存数据

0.逻辑

  • 点击保存,发送ajax请求,保存数据

    1.方法

  • 为保存按钮添加id
    id="emp_save_btn"

  • 为保存按钮绑事件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    //点击保存员工
    $("#emp_save_btn").click(function () {
    //1.发送ajax请求保存员工
    //jquery中提供了简单的把表单数据拿出来的方法(序列表格中的内容为字符串):serialize(),用于ajax请求,下面是个测试
    /*alert($("#empAddModal form").serialize());*/
    $.ajax({
    url: "${APP_PATH}/emp",
    type: "post",
    data: $("#empAddModal form").serialize(),
    success: function (result) {
    //测试是否保存成功
    //alert(result.msg);
    //员工保存成功后:1.关闭模态框;
    $("#empAddModal").modal('hide');
    // 2.来到最后一页,显示刚才插入的数据
    //发送ajax请求,显示最后一页(只要传一个足够大的数,分页插件都会默认到最后一页)
    //这里我吗用记录数,因为记录数,一定是大于页数的
    to_page(totalRecord);
    }
    });
    });
  • 在EmployeeContorller中添加保存的方法

    1
    2
    3
    4
    5
    6
    @PostMapping(value = "/emp")
    @ResponseBody
    public Message saveEmployee( Employee employee) {
    employeeService.saveEmployee(employee);
    return Message.success();
    }
  • 在EmployeeService中创建员工保存的方法

    1
    2
    3
    4
    5
    6
    7
    8
    /**
    * @param
    * @return void
    * @description 新增用户
    */
    public void saveEmployee(Employee employee) {
    employeeMapper.insertSelective(employee);
    }

  • 遇到的问题:如果用户名相同的时候,也是可以添加上,这样不符合业务的需求;
  • 解决的方法:保存数据之前校验

    4.保存数据之前先校验(前端校验

    (1)如果校验失败,返回false

    5d65aec1775b36be0fde953f926ad1bd.png

    (2)校验的方法

  • 拿到需要校验的元素
  • 通过正则表达式校验(jQuery文档)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    function validate_add_form() {
    //1.拿到要校验的数据,使用正则表达式
    var empName = $("#empName_add_input").val();//通过id拿到值
    var regName = /(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/
    /*alert(regName.test(empName));//先测试一下成功吗
    return false;//return false先让数据不提交*/
    //先校验用户名信息是否合法
    if(!regName.test(empName)){
    //1.如果校验失败
    // alert("用户名可以是2-5位中文或者是2-16位英文和数字的组合");
    return false;
    }
    //2.如果没有失败,校验邮箱信息
    var email = $("#email_add_input").val();
    var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
    if(!regEmail.test(email)){
    //如果email校验失败了
    alert("邮箱格式不正确");
    return false;
    }
    //都成功,返回true
    return ture;
    }
  • 这样就完成了校验,但是,这种(alert)弹出的方式不太美观*

    (3)使用bootstrap中的表单校验状态完善校验显示的功能

  • 把弹窗提示删掉
  • 给用户名和邮箱的input框里添加span标签
    <span class="help-block"></span>
  • 给父元素添加错误的样式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    //校验表单数据
    function validate_add_form() {
    //1.拿到要校验的数据,使用正则表达式
    var empName = $("#empName_add_input").val();//通过id拿到值
    var regName = /(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/
    if(!regName.test(empName)){
    //1.如果校验失败
    // alert("用户名可以是2-5位中文或者是2-16位英文和数字的组合");//美化替换他
    用show_validate_msg(ele,status,msg)方法替换他
    $("#empName_add_input").parent().addClass("has-error");
    $("#empName_add_input").next("span").text("用户名可以是2-5位中文或者是2-16位英文和数字的组合");
    return false;
    }else{
    $("#empName_add_input").parent().addClass("has-success");
    $("#empName_add_input").next("span").text("");
    }
    //2.如果没有失败,校验邮箱信息
    var email = $("#email_add_input").val();
    var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
    if(!regEmail.test(email)){
    //如果email校验失败了
    // alert("邮箱格式不正确");//美化替换他
    $("#email_add_input").parent().addClass("has-error");
    $("#email_add_input").next("span").text("邮箱格式不正确");
    return false;
    }else {
    $("#email_add_input").parent().addClass("has-success");
    $("#enum_add_input").next("span").text("");
    }
    //一切都正常,返回true
    return true;
    }
  • 这个时候:如果第一次输入的是错误的,会提示错误信息,输入框会变成红色;如果第二次输入正确,提示的文字框会消失,但是,输入框依旧是红色的;原因就是元素的class没有刷新,把has-error和has-success放在一起了*
  • 清空样式

    (4)把校验的方法抽取出来

  • 抽取公共的方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function show_validate_msg(ele, status, msg) {
    //如果第一次输入失败,即使第二次成功也是红框,所以:应该清空当前校验状态
    $(ele).parent().removeClass("has-sucess 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
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      //校验表单数据
      function validate_add_form() {
      //1.拿到要校验的数据,使用正则表达式
      var empName = $("#empName_add_input").val();//通过id拿到值
      var regName = /(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/
      /*alert(regName.test(empName));//先测试一下成功吗
      return false;*/
      /*if(!regName.test(empName)){
      //1.如果校验失败
      // alert("用户名可以是2-5位中文或者是2-16位英文和数字的组合");//美化替换他
      用show_validate_msg(ele,status,msg)方法替换他
      $("#empName_add_input").parent().addClass("has-error");
      $("#empName_add_input").next("span").text("用户名可以是2-5位中文或者是2-16位英文和数字的组合");
      return false;
      }else{
      $("#empName_add_input").parent().addClass("has-success");
      $("#empName_add_input").next("span").text("");
      }*/
      //用一个方法整合上面的
      if (!regName.test(empName)) {
      show_validate_msg($("#empName_add_input"), "error", "用户名可以是2-5位中文或者是2-16位英文和数字的组合")
      } else {
      show_validate_msg($("#empName_add_input"), "success", "")
      }
      //2.如果没有失败,校验邮箱信息
      var email = $("#email_add_input").val();
      var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
      /* if(!regEmail.test(email)){
      //如果email校验失败了
      // alert("邮箱格式不正确");//美化替换他
      $("#email_add_input").parent().addClass("has-error");
      $("#email_add_input").next("span").text("邮箱格式不正确");
      return false;
      }else {
      $("#email_add_input").parent().addClass("has-success");
      $("#enum_add_input").next("span").text("");
      }*/
      //用一个方法整合上面的
      if (!regEmail.test(email)) {
      show_validate_msg("#email_add_input", "error", "邮箱格式不正确")
      return false;
      } else {
      show_validate_msg("#email_add_input", "success", "")
      }
      //一切都正常,返回true
      return true;
      }
  • 这个时候,如果有两个相同名字的用户是可以注册成功的,这样就不对了*

    (5)ajax校验用户名是否重复

  • 逻辑:把员工姓名输入完成后,就去给服务器发送一个ajax请求,服务器来告知我们这个用户名是否可用;如果可用,可用保存;如果不可用,提示错误信息
  • 可以给员工姓名这一栏绑定一个change事件,当姓名改变时,做一些事情
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
       //校验用户名是否可用
    $("#empName_add_input").change(function () {
    //发送ajax请求校验用户名是否可用
    var empName = this.value;//this.value是输入框的值
    $.ajax({
    url: "${APP_PATH}/checkSameEmployee",
    data: "empName=" + empName,
    type: "post",
    success: function (result) {
    if (result.code == 200) {
    show_validate_msg("#empName_add_input", "success", "用户名可用");
    } else {
    show_validate_msg("#empName_add_input", "error", "用户名不可用");
    }
    }
    });
    });
  • 当员工姓名改变时,发送ajax请求,校验用户名是否可用
  • 在EmployeeController里面编写一个检验用户名是否可用的方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /**
    * @param检查用户名是否可用
    * @return Message
    * @description:检测用户是否存在 success表示用户不存在可用,fail表示用户存在不可用
    * 可以通过识别状态码来分辨用户名可用不可用
    */
    @PostMapping(value = "/checkSameEmployee")
    @ResponseBody
    public Message checkSameEmployee(@RequestParam("empName")

    String empName) {
    boolean b = employeeService.checkSameEmloyee(empName);
    if(b){
    return Msg.success();
    }else{
    return Msg.fail();
    }
    }
  • 在EmployeeService中创建相应的方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /**
    * @param
    * @return boolean
    * @description 数据库中是否有empName,有返回false.没有返回true,true代表可用
    */
    public boolean checkSameEmployee(String empName) {
    EmployeeExample example = new EmployeeExample();
    //创建查询条件
    Criteria criteria = example.createCriteria();
    //让员工的名字必须等于给定的值
    criteria.andEmpNameEqualTo(empName);
    //返回给定信息的记录数
    long count = employeeMapper.countByExample(example);
    //看返回值是否等于0(等于0,返回true,代表当前姓名可用;大于0返回false,不可用)
    return count == 0;
    }
  • 选择输入数据库中已经有的用户名,会提示错误,如果这个时候邮箱输入正确,尽管用户名提示错误,还是可以保存成功*

    (6)完善用户名校验1

  • ajax校验的信息保存起来:如果校验成功了,给保存按钮添加一个属性ajax-va;如果成功值就是success,如果失败值就是error
    1
    2
    3
    4
    5
    6
    7
    8
    if (result.code == 200) {
    show_validate_msg("#empName_add_input", "success", "用户名可用");
    //若果保存成功,就加一个属性,值为success
    $("#emp_save_btn").attr("ajax-va", "success");
    } else {
    show_validate_msg("#empName_add_input", "error","用户名不可用");
    $("#emp_save_btn").attr("ajax-va", "error");
    }
  • 点击保存按钮的时候,要先判断ajax校验用户名是否成功
    82ace4cb8fb806921012acc91bf9071c.png

这个时候可以完成要求了,但是添加成功后,再次点击新增的时候,输入框中是有数据的,这个时候再次添加还会添加成功

  • 解决方法:点击新增时,清空输入框中的数据并且情况输入框的状态
    cda450c84293b7a08750ccfb52934d64.png

  • 写一个能够清空表单内容和样式的方法

    1
    2
    3
    4
    5
    6
    7
    8
    //清空表单样式及内容
    function reset_form(ele) {
    //重置表单内容
    $(ele)[0].reset();
    //清空表单样式
    $(ele).find("*").removeClass("has-error has-success");
    $(ele).find(".help-block").text("");
    }

这个时候我们输入aaa这个用户名数据库中确实没有,提示可用,但是一点保存,就会提示不可用

(7)完善用户名校验2

  • 检查用户名是否可用之前,判断用户名是否合法(用后端来校验)
  • 对EmployeeController中的方法进行修改
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @PostMapping(value = "/checkSameEmployee")
    @ResponseBody
    public Message checkSameEmployee(@RequestParam("empName") String empName) {
    //先判断用户名是否是合法的表达式
    String regx = "(^[a-zA-Z0-9_-]{6,16}$)|(^[\\u2E80-\\u9FFF]{2,5})";
    //看用户名是否匹配正则表达式
    if(!empName.matches(regx)){//如果匹配失败
    return Message.fail().add("va_msg","用户名必须是6-16位数字和字母的组合或者2-5位中文");
    }
    //数据库用户名重复校验
    boolean b = employeeService.checkSameEmployee(empName);
    if (b) {// 用户名不存在,可用
    return Message.success();
    } else {
    return Message.fail().add("va_msg","用户名不可用");
    }
    }
  • 失败后的提示消息也是在result中取
    f222aa1db0a9ccea7ff132a593ce35d8.png

这样前端校验就算完成了,但是前端校验可以在网页中修改,所有重要的数据也要在后端校验

5.重要数据的后端校验(JSR303)

  • 以前完成了jQuery前端校验,ajax用户名重复校验(前端校验防君子,不防小人,所以后端校验很有必要),重要数据要加上后端校验(JSR303);

  • 重要的数据:前端校验+后端校验+数据库的约束

    (1)想要实现JSR303的支持,必须要导入Hibernate-Validator包

    1
    2
    3
    4
    5
    6
    7
    <!-- hibernate-validator jsr303数据校验 -->
    <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
    <dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.3.5.Final</version>
    </dependency>

    (2)后端校验的实现

  • 在bean包下的Employee类中,对需要后端校验的属性添加相应的注解
    d556d2f704154a03a4e5cd1a6b038afd.png

  • 在Controller中,员工保存的方法,对相应的对象添加@Valid注解,用BindingResult result来封装校验的结果

  • 如果校验成功,就提示成功,如果校验失败就返回失败

  • 如果校验失败,返回失败的信息,在输入框的下方显示失败的信息(在result中提取失败的信息,把错误的信息都封装到map中)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @PostMapping(value = "/emp")
    @ResponseBody
    public Message saveEmployee(@Valid Employee employee, BindingResult result) {
    // System.out.println(employee);
    if (result.hasErrors()) {// 后端校验失败,返回校验失败的信息
    Map<String, Object> map = new HashMap<>();
    List<FieldError> errors = result.getFieldErrors();
    for (FieldError error : errors) {
    System.out.println("错误的字段命名:");
    map.put(error.getField(), error.getDefaultMessage());
    }
    return Message.fail().add("errorField", map);
    } else {
    employeeService.saveEmployee(employee);
    return Message.success();
    }
    }
  • 到首页去完成逻辑的实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    //2.发送ajax请求保存员工
    //jquery中提供了简单的把数据拿出来的方法(序列表格中的内容为字符串):serialize(),用于ajax请求,下面是个测试
    /*alert($("#empAddModal form").serialize());*/
    $.ajax({
    url: "${APP_PATH}/emp",
    type: "post",
    data: $("#empAddModal form").serialize(),
    success: function (result) {
    //后端校验是否保存成功
    if (result.code == "200") {
    //alert(result.msg);
    //员工保存成功后:1.关闭模态框;
    $("#empAddModal").modal('hide');
    // 2.来到最后一页,显示刚才插入的数据
    //发送ajax请求,显示最后一页(只要传一个足够大的数,分页插件都会默认到最后一页)
    //这里我吗用记录数,因为记录数,一定是大于页数的
    to_page(totalRecord);
    } else {
    //显示失败信息
    // consol`e.log(result);
    if (undefined != result.data.errorField.email) {
    //显示邮箱的错误信息
    show_validate_msg("#email_add_input", "error", result.data.errorField.email);
    }
    if (undefined != result.data.errorField.empName) {
    show_validate_msg("#empName_add_input", "error", result.data.errorField.empName);
    }
    }
    }
    });
  • 这样就完成了员工的新增功能*


九、修改

0.逻辑

  • 1.点击编辑

  • 2.弹出用户修改的模态框(显示用户信息)

  • 3.点击更新,完成用户修改

    1.创建员工修改的模态框

  • 把员工新增的模态框复制一份,用于员工修改的模态框

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    <!-- 员工修改的模态框 -->
    <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">
    <form class="form-horizontal">
    <div class="form-group">
    <label for="empName_add_input" class="col-sm-2 control-label">empName</label>
    <div class="col-sm-10">
    <p class="form-control-static" id="empName_update_static"></p>
    </div>
    </div>
    <div class="form-group">
    <label for="email_update_input" class="col-sm-2 control-label">email</label>
    <div class="col-sm-10">
    <input type="text" name="email" class="form-control" id="email_update_input"
    placeholder="emil@qq.com">
    <span class="help-block"></span>
    </div>
    </div>
    <div class="form-group">
    <label class="col-sm-2 control-label">gender</label>
    <div class="col-sm-10">
    <label class="radio-inline">
    <input type="radio" name="gender" id="gender1_update_input" value="M" checked="checked">

    </label>
    <label class="radio-inline">
    <input type="radio" name="gender" id="gender2_update_input" value="F">
    </label>
    </div>
    </div>
    <div class="form-group">
    <label class="col-sm-2 control-label">deptName</label>
    <div class="col-sm-3">
    <%--部门提交部门id--%>
    <select class="form-control" name="dId" id="dept_update_select">

    </select>
    </div>
    </div>

    </form>
    </div>
    <div class="modal-footer">
    <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
    <button type="button" class="btn btn-primary" id="emp_update_btn">修改</button>
    </div>

    </div>
    </div>
    </div>
  • 因为编辑按钮是通过ajax请求查出数据之后创建的,所以对他用加载完页面直接帮事件的方法行不通

  • 为编辑绑事件的方法:1.创建完编辑按钮之后,为按钮绑事件;2.通过on方法(这里使用第二种)

  • 之前查出的部门信息放到了员工添加的列表中,只需要把要添加的元素传进来即可
    8d776b1ff71ab9c36a17c3b2b8a85a9b.png

  • 点击编辑,弹出模态框

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /*修改
    1.我们是按钮创建之前就绑定了click,所以绑不上
    * 解决方法:
    * 1.可以在添加按钮的时候绑定 2.绑定live()方法,jequry新版没有live方法,可以用on替换
    * */
    $(document).on("click", ".edit_btn", function () {
    // alert("edit");
    //查出员工信息,显示员工信息,
    getEmp($(this).attr("edit-id"));
    // 查出部门信息,并显示部门列表
    getDepts("#dept_update_select");
    //把员工的id传递给模态框的跟新按钮
    $("#emp_update_btn").attr("edit-id",$(this).attr("edit-id"));
    //弹出模态框
    $("#empUpdateModal").modal({
    backdrop: "static"
    });
    });

    2.点击编辑,显示员工员工信息

  • 把员工的姓名改成表单的静态控件(在bootstrap中查看)
    70c13b874485227965b9c30989a72dd9.png

  • index中查询员工信息的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    /**
    * 查出员工信息
    * */
    function getEmp(id) {
    $.ajax({
    url: "${APP_PATH}/emp/" + id,
    type: "GET",
    success: function (result) {
    // console.log(result);
    var empData = result.data.employee;
    $("#empName_update_static").text(empData.empName);//给静态文本框赋值
    $("#email_update_input").val(empData.email);//给input框赋值
    //性别定位
    $("#empUpdateModal input[name=gender]").val([empData.gender]);
    //部门定位
    $("#empUpdateModal select").val([empData.dId]);

    }
    });
    }
  • 在EmployeeController中创建相应的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**根据id查询员工
    * @param
    * @return Message
    * @description:修改前将要修改的employee查询出来表单回显
    */
    @GetMapping(value = "/emp/{id}")//等价于@RequestMapping(value = "/emp/{id}",method=RequestMethod.GET)
    @ResponseBody
    public Message getEmployee(@PathVariable("id") Integer id) {
    Employee employee = employeeService.getEmployee(id);
    return Message.success().add("employee", employee);
    }
  • 在EmployeeService中创建相应的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /**
    * 按照员工id查询员工
    * @param id
    * @return
    */
    public Employee getEmployee(Integer id) {
    Employee employee = employeeMapper.selectByPrimaryKey(id);
    return employee;
    }
  • 可以在创建编辑按钮的时候,创建一个属性,来表示当前员工的id
    dd0c2d3c2bc4777860af55399b0a8459.png

    3.点击修改,更新员工

  • 为更新按钮绑定单击事件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    //点击修改,更新员工信息
    $("#emp_update_btn").click(function () {
    //1.验证邮箱是否合法
    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", "")
    }
    //2.发送ajax请求保存更新的员工数据
    $.ajax({
    url:"${APP_PATH}/emp/"+$(this).attr("edit-id"),
    type:"PUT",
    data:$("#empUpdateModal form").serialize(),
    success:function(result){
    //alert(result.msg);
    //1.关闭模态框
    $("#empUpdateModal").modal("hide");
    //2.回到本页面
    to_page(currentPage);
    }
    });
    });
  • 因为发送的是ajax请求,要写请求的处理事件

  • 在EmployeeController中添加相应的保存方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    /**保存员工
    * 遇到的问题:请求体中有数据:
    * 但是Employee对象封装不上;
    * SQL语句变成了 update tbl_emp where emp_id = 1014;
    * 原因:
    * Tomcat:
    * 1.将请求体中的数据,封装成一个map
    * 2.request.getParameter("empName")就会从这个map中取值。
    * 3.SpringMvc封装pojo的时候会把pojo中的每个属性值,request.getParamter("email");
    * Ajax发送PUT请求引发的血案
    * PUT请求:请求体中的数据,request.getParameter("empName")拿不到的根本原因就是
    * Tomcat一看是put请求,不会封装请求体中的数据为map,只有post形式的请求才封装请求体为map
    * @param
    * @return Message
    * @description:员工更新 这里ajax请求直接发put请求而不是post请求,那么所有的参数都会获取不到,因为tomcat只会封装post的数据。也就是说request.getParameter("empId")为空,springmvc也无法封装Bean
    * 解决方法: 1.发送post方法,通过HiddenHttpMethodFilter
    * 2.发送put请求,通过HttpPutFormContentFilter(通过web.xml配置)
    */
    @PutMapping(value = "/emp/{empId}")
    @ResponseBody
    public Message saveUpdateEmployee(Employee employee) {
    System.out.println(employee);
    //logger.info(employee.toString());
    employeeService.updateEmployee(employee);
    return Message.success();
    }
  • 在EmployeeService中添加相应的保存方法

    1
    2
    3
    4
    5
    6
    7
    /**
    * 员工更新
    * @param employee
    */
    public void updateEmployee(Employee employee) {
    employeeMapper.updateByPrimaryKeySelective(employee);//根据主键有选择的更新
    }
    • 打开修改模态框的时候,把员工id的值,传给修改按钮
      13df60982c95f219aeb9c9efca631d1d.png
  • 因为tomcat不封装put请求发送的数据,spring提供了支持(HttpPutFormContentFilter

    • 在web.xml中,把这个过滤器配置上
      1
      2
      3
      4
      5
      6
      7
      8
      <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>
  • 更新成功之后关闭模态框,回到本界面

  • 把员工的当前页码也记录一下
    60c668d91ac15caf855ab99ac6e80727.png

  • 这样就可以发送成功了*

十、删除

1.单个删除

  • 逻辑
    URI:/emp/{id} DELETE形式的请求

    (1)单个删除的步骤

    • 在EmployeeController中拦截发送的请求

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      /**
      * 单个删除的方法
      * @param id
      * @return
      */
      @ResponseBody
      @RequestMapping(value = "/emp/{id}",method =RequestMethod.DELETE)
      public Message deleteEmpById(@PathVariable("id") Integer id){
      employeeService.deleteEmployee(id);
      return Message.success();
      }
    • 在EmployeeService中创建相应的方法

      1
      2
      3
      4
      5
      6
      7
      /**
      * 员工删除
      * @param empId
      */
      public void deleteEmployee(Integer empId) {
      employeeMapper.deleteByPrimaryKey(empId);
      }
    • 用on为删除按钮绑定单击事件

    • 弹出是否确认删除的对话框(拿出员工的员工的名字)

    • 删除成功后,返回当前页码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      /**
      * 删除
      * */
      $(document).on("click",".delete_btn",function(){
      //弹出是否确认删除对话框(找到当前的祖先节点的tr,tr下的第二个td的文本值)
      //alert($(this).parents("tr").find("td:eq(1)").text());
      var empName = $(this).parents("tr").find("td:eq(2)").text();
      var empId = $(this).attr("del-id");//要删除的员工id
      if(confirm("确认删除【"+empName+"】吗?")){
      //点击确认发送ajax请求,删除即可
      $.ajax({
      url:"${APP_PATH}/emp/"+empId,
      type:"DELETE",
      success:function(result){
      alert(result.msg);
      //回到本页
      to_page(currentPage);
      }
      });
      }
      });
  • 构造删除按钮的时候给按钮添加一个自定义的属性来表示员工id
    01385226a4bdf3c069287e3d6a34754a.png

  • 这样就完成了单个删除*

    2.批量删除

    (1)添加多选框

  • 给第一列第一行添加多选框
    b0aca4497647fcc366cf7508fb02586d.png

  • 解析数据的时候也需要checkbox
    622e9887a57929d0054d9647e3dd148a.png

  • 把CheckBox添加到遍历的元素中
    ce2f1949f6e96014149594b5b584b694.png

(2) 完成全选全不选功能

  • 点击最上面的按钮,让下面的按钮与最上面的按钮的checked的值保持同步

    1
    2
    3
    4
    5
    6
    7
    8
    //完成全选,全不选功能
    $("#check_all").click(function(){
    //attr获取checked是undefined;
    //我们这些dom原生的属性:attr获取自定义属性的值,用prop修改和读取dom原生属性的值;
    $(this).prop("checked");
    //让下面的选择框和第一个的值相同
    $(".check_item").prop("checked",$(this).prop("checked"));
    });
  • 当下面的选择框全部点满后,上面的选择框自动的选中

  • 为下面的选择框添加单击事件

  • 用checked选择器,查看被选中的个数

    1
    2
    3
    4
    5
    6
    //给check_item绑定单击事件
    $(document).on("click",".check_item",function(){
    //判断当前选中的元素是否全选
    var flag = ($(".check_item:checked").length == $(".check_item").length);
    $("#check_all").prop("checked",flag);
    });

    (3)批量删除

  • 给全部删除的按钮添加id
    3ce6d9a888f9db12582a0438c5e43c39.png

  • 给删除按钮绑定单击事件

  • 遍历被选中的员工,并提示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    //点击全部删除,就批量删除
    $("#emp_del_all_btn").click(function () {
    //遍历每一个被选中的,查出名字
    var empNames = "";
    var del_idstr = "";
    $.each($(".check_item:checked"),function(){
    empNames += $(this).parents("tr").find("td:eq(2)").text()+",";
    //组装员工id字符串
    del_idstr += $(this).parents("tr").find("td:eq(1)").text()+"-";
    });
    //去除多余的(最后一个)逗号:从0开始截取字符串,到导数第二个
    empNames = empNames.substring(0,empNames.length-1);
    //去除多余的(最后一个)-:从0开始截取字符串,到导数第二个
    del_idstr = del_idstr.substring(0,del_idstr.length-1);
    if(confirm("确认删除【"+empNames+"】员工吗?")){
    //发送ajax请求删除
    $.ajax({
    url:"${APP_PATH}/emp/"+del_idstr,
    type:"delete",
    success:function(result){
    alert(result.msg);
    //回到当前页面
    to_page(currentPage);
    }
    });
    }

    });
  • 把原来的方法改成单个批量二合一的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /***
    * 删除多个员工(批量)
    * @param ids
    */
    public void deleteBatch(List<Integer> ids) {

    EmployeeExample example = new EmployeeExample();
    Criteria criteria = example.createCriteria();
    //delete from xxx where emp_id in(1,2,3)
    criteria.andEmpIdIn(ids);
    employeeMapper.deleteByExample(example);

    }
  • 在EmployeeService中创建可以同时删除多个的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /**
    * @param
    * @return Message
    * @description:单个批量删除 单个删除:1 批量删除:1-2-3
    */
    @DeleteMapping(value = "/emp/{ids}")
    @ResponseBody
    public Message deleteEmployee(@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 {//不包含-就是单个删除,调用方法如下:
    Integer id = Integer.parseInt(ids);
    employeeService.deleteEmployee(id);
    }
    return Message.success();
    }

恭喜你:这样ssm框架的crud项目就完成了!!!

文章作者: Danqing
文章链接: http://yoursite.com/2019/11/17/ssm_crud%E9%A1%B9%E7%9B%AE%E8%AF%A6%E7%BB%86%E6%AD%A5%E9%AA%A4/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 DanqingBlog
打赏
  • 微信
  • 支付宝

评论