ssm框架整合的crud项目详细步骤

3 篇文章 0 订阅
1 篇文章 0 订阅

文章目录


由于很多图片加载不出来,点击这,看完整版

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、启动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配置的位置

    <!--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)配置字符编码过滤器(防止乱码)
  • 代码

    <!--字符编码过滤器(所有过滤器之前) -->
    <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请求,还需要一个过滤器
  • 代码
    <!--使用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(控制器)
    <!--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包
    <!--配置视图解析器,方便页面返回-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
    
(3)两个标准配置
  • 代码
    <!--两个标准配置-->
    <!-- 支持springmvc更高级的功能,比如:,JSR303校验,快捷的ajax,映射动态请求 -->
    <mvc:annotation-driven/>
    <!-- 将springmvc不能处理的请求交给tomcat -->
    <mvc:default-servlet-handler />
    

3.applicationContext.xml的配置

  • 主要是配置和逻辑有关的
  • Spring配置文件的核心点:(数据源、与mybatis的整合、事务控制)
(1)配置数据源(c3p0)
  • 代码
    <!--配置数据源-->
    <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
    <!--================配置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)事务控制的配置
  • 要能管事务,就是控制住数据源
    <!-- 配置事务管理器 -->
    <bean id="transactionManager"    class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
    <!--控制数据源--> 
        <property name="dataSource" ref="PoolDataSource">
        </property>
    </bean>
    

  • 1.开启注解的事务,2.使用xml配置形式的事务(比较重要的都是采用配置式)
    <!--开启注解事务或者使用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的官方文档(点击进入
  • 找到表头,复制进去
    <?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)配置驼峰命名规则
  • 代码
    <!-- 是否开启自动驼峰命名规则(camel case)映射,
    即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。-->
    <settings>
    	<setting name="mapUnderscoreToCamelCase" value="true" />
    </settings>
    
(2)配置类型别名
  • 代码
    <typeAliases>  
    	<package name="com.wantao.bean" />
    </typeAliases>
    

配置mapper文件

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

4.逆向工程

  • 去网页搜索 Mybatis Generator(GitHub–>进入帮助文档)点击进入
(1)引入依赖jar
  • 在pom.xml文件中添加
    <!-- 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
<?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类

    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中新增两个能查出部门的方法

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

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

    <sql id="Base_Column_List_With_Dept"> 
    emp_id, emp_name, gender, email,d_id,dept_id,dept_name
    
``` select distinct from tb_emp left join tb_dept on tb_emp.d_id=tb_dept.dept_id order by ${orderByClause} select from tb_emp left join tb_dept on tb_emp.d_id=tb_dept.dept_id where emp_id = #{empId,jdbcType=INTEGER} ```

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

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

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

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

    //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中央仓库搜索

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

       @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中生成有参构造器
      • 注意:一旦生成有参构造构造器,一定要生产无参的构造器,因为反射要经常的使用无参的构造
               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;  
                   }
               }
      
      @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生成有参无参构造器
      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() {
      
        }
      

      在上面的测试类中直接加入下面的代码即可

        employeeMapper.insertSelective(new Employee(null,"jerry","M","23@qq.com",1));
      

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

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

    <!--配置批量的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

    @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请求

    <%@ 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的类中添加查找所有员工的方法

           @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类中,创建需要的方法

    @Service
    public class EmployeeService { 
        @Autowired
        EmployeeMapper employeeMapper;  
        public List<Employee> findAllEmployee() {  
        //查询所有的(带上部门信息),所以调节为null
        return employeeMapper.selectByExampleWithDept(null); 
    }
    
  • 查询还需要Mapper里相应的方法,自动注入(@Autowired)
    通过视图解析器,拼串后会来到/WEB-INF/views/show.jsp页面做展示

来到show.jsp页面展示
  • 在WEB-INF下创建views包,在包下创建show.jsp

(二)分页查询

1. 在controller层中传入要查询的页数,默认值为1
  • 在controller层中传入要查询的页数,默认值为1
    @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页,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文件中引入相应的依赖
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.0.0</version>
    </dependency>
    
  • 在mybatis全局配置中注册这个插件:–》中文文档往下翻
    <!--    引入分页插件--> 
    <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以上的
    @RunWith(SpringJUnit4ClassRunner.class)
    @WebAppConfiguration//获取web的IoC
    @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

  • 开头代码
    <%//存入当前项目的名称找	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)
    <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列)
     <!--按钮 -->
        <div class="row">
            <div class="col-md-4 col-md-offset-8">
                <button>新增</button>
                <button>删除</button>
            </div>
        </div>
    
  • 美化按钮(根据官方文档,只需要给相应按钮的class增加值即可)
    <!--按钮 -->
        <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的样式
      <!--显示表格数据 -->
        <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.添加分页显示

  • 组件–》分页,在里面添加分页信息
     <!--显示分页信息-->
        <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.引入标签库

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

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

  • 遍历的取出查出的数据,之前已经把数据都放到了pageInfo里面,在list里面存着
    <c:forEach items="${pageInfo.list}" var="emp">
    
  • 把取出的数据填充到相应的位置,并为 每一行的数据添加编辑和删除按钮
    <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列
    <!-- 分页文字信息 -->
    <div class="col-md-6">
        <p>当前第${pageInfo.pageNum}页,共${pageInfo.pages}页,共${pageInfo.total }条记录</p>
    </div>
    
(2) 填充分页条信息
  • 分页条逻辑
    • 1.如果你是当前页码,让它高亮显示
    • 2.如果当前页码是第一页,首页和上一页为关闭状态
    • 3.如果当前页码是最后一页,末页和下一页为关闭状态
    • 4.点击第几页就跳到第几页(包括首页、末页、上一页、下一页)
    <!-- 分页条信息 -->
    <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>
    
``` **分页查询总结:从首页转到/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中引入相应的依赖
      <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
          <version>2.10.0</version>
      </dependency>
      
  • controller中的方法为
    @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类,完成上面的功能
    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类里的返回值
    @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的字符串如下
    {"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请求,要到一个分页数据
    <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的细化
    <!--显示表格数据 --> 
    <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里的员工数据
  • 把员工的数据和操作的按钮一起添加
    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)解析分页信息
  • 显示分页信息和分页条信息
     //显示分页信息
        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页面发送请求的方式改一下,改成跳转页面的方法
    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查询员工
/empPOST保存员工
/em/{id}PUT修改员工
/em/{id}DELETE删除员工

八、用户新增

0.逻辑

  • 1.在index.jsp页面点击“新增”
  • 2.弹出新增对话框(模态框)
  • 3.去数据库查询部门列表显示在对话框中
  • 4.用户输入数据,并进行校验,完成保存

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

(1)在页面中引入模态框
  • 去bootstrap官网拷贝模态框

    f2bbb229f93b8f2aafe278cc863bab71.png

     <!-- 用户新增模态框 -->
     <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

    <button class=" btn btn-danger">
        <span class="glyphicon glyphicon-plus" aria-hidden="true" id="emp_add_model_btn">新增</span>
    </button>
    
(2)点击新增,弹出模态框
  • 为新增按钮绑事件:结合文档中模态框的用法

     //添加按钮事件,点击按钮弹出模态框
            $("#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中找)

    <!-- 员工添加的模态框 -->
    <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请求的方法
      //查出所有的部门信息,并显示在下拉列表中
        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,用来处理和部门有关的请求
    @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
    @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"

  • 为保存按钮绑事件

    //点击保存员工
        $("#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中添加保存的方法

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

    /**
                 * @param
                 * @return void
                 * @description 新增用户
                 */
                public void saveEmployee(Employee employee) {
                    employeeMapper.insertSelective(employee);
                }
    

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

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

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

5d65aec1775b36be0fde953f926ad1bd.png

(2)校验的方法
  • 拿到需要校验的元素
  • 通过正则表达式校验(jQuery文档)
    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>
  • 给父元素添加错误的样式
     //校验表单数据
        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)把校验的方法抽取出来
  • 抽取公共的方法
     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);
            }
        }
    
    • 把抽取的方法应用到校验的位置
    //校验表单数据
        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事件,当姓名改变时,做一些事情

           //校验用户名是否可用
        $("#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里面编写一个检验用户名是否可用的方法

    /**
         * @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中创建相应的方法

    /**
         * @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
        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

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

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

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

(7)完善用户名校验2
  • 检查用户名是否可用之前,判断用户名是否合法(用后端来校验)
  • 对EmployeeController中的方法进行修改
    @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包
		<!-- 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中)

    @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();
            }
        }
    
  • 到首页去完成逻辑的实现

    //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.创建员工修改的模态框

  • 把员工新增的模态框复制一份,用于员工修改的模态框
    <!-- 员工修改的模态框 -->
    <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方法(这里使用第二种)
  • 之前查出的部门信息放到了员工添加的列表中,只需要把要添加的元素传进来即可
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3CqlEK4i-1574755610962)( https://raw.githubusercontent.com/danqing-hub/images/master/8d776b1ff71ab9c36a17c3b2b8a85a9b.png )]
  • 点击编辑,弹出模态框
     /*修改
        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中查看)
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ti0jWtyq-1574755610962)( https://raw.githubusercontent.com/danqing-hub/images/master/70c13b874485227965b9c30989a72dd9.png )]

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

     /**
         * 查出员工信息
         * */
        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中创建相应的方法

    /**根据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中创建相应的方法

    /**
         * 按照员工id查询员工
         * @param id
         * @return
         */
        public Employee getEmployee(Integer id) {
            Employee employee = employeeMapper.selectByPrimaryKey(id);
            return employee;
        }
    
  • 可以在创建编辑按钮的时候,创建一个属性,来表示当前员工的id
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OcyaY6Ex-1574755610963)( https://raw.githubusercontent.com/danqing-hub/images/master/dd0c2d3c2bc4777860af55399b0a8459.png)]

3.点击修改,更新员工

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

      //点击修改,更新员工信息
        $("#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中添加相应的保存方法

    /**保存员工
         * 遇到的问题:请求体中有数据:
         * 但是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中添加相应的保存方法
    java /** * 员工更新 * @param employee */ public void updateEmployee(Employee employee) { employeeMapper.updateByPrimaryKeySelective(employee);//根据主键有选择的更新 }

    • 打开修改模态框的时候,把员工id的值,传给修改按钮
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d7Gy1axm-1574755610963)( https://raw.githubusercontent.com/danqing-hub/images/master/13df60982c95f219aeb9c9efca631d1d.png )]
  • 因为tomcat不封装put请求发送的数据,spring提供了支持(HttpPutFormContentFilter

    • 在web.xml中,把这个过滤器配置上
      <filter>
              <filter-name>HttpPutFormContentFilter</filter-name>
              <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
          </filter>
          <filter-mapping>
              <filter-name>HttpPutFormContentFilter</filter-name>
              <url-pattern>/*</url-pattern>
          </filter-mapping>
      
  • 更新成功之后关闭模态框,回到本界面

  • 把员工的当前页码也记录一下
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cY5QC0bL-1574755610963)( https://raw.githubusercontent.com/danqing-hub/images/master/60c668d91ac15caf855ab99ac6e80727.png )]

这样就可以发送成功了

十、删除

1.单个删除

  • 逻辑
    URI:/emp/{id} DELETE形式的请求
(1)单个删除的步骤
  • 在EmployeeController中拦截发送的请求

    /**
         * 单个删除的方法
         * @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中创建相应的方法

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

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

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

      /**
         * 删除
         * */
        $(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
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9aIccMdO-1574755610964)( https://raw.githubusercontent.com/danqing-hub/images/master/01385226a4bdf3c069287e3d6a34754a.png )]

这样就完成了单个删除

2.批量删除

(1)添加多选框
  • 给第一列第一行添加多选框
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gfyr40ta-1574755610964)( https://raw.githubusercontent.com/danqing-hub/images/master/b0aca4497647fcc366cf7508fb02586d.png )]

  • 解析数据的时候也需要checkbox
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SBE6tJUr-1574755610964)( https://raw.githubusercontent.com/danqing-hub/images/master/622e9887a57929d0054d9647e3dd148a.png )]

  • 把CheckBox添加到遍历的元素中
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gKEqRb8l-1574755610965)( https://raw.githubusercontent.com/danqing-hub/images/master/ce2f1949f6e96014149594b5b584b694.png )]

(2) 完成全选全不选功能
  • 点击最上面的按钮,让下面的按钮与最上面的按钮的checked的值保持同步
     //完成全选,全不选功能
        $("#check_all").click(function(){
            //attr获取checked是undefined;
            //我们这些dom原生的属性:attr获取自定义属性的值,用prop修改和读取dom原生属性的值;
            $(this).prop("checked");
            //让下面的选择框和第一个的值相同
            $(".check_item").prop("checked",$(this).prop("checked"));
        });
    
  • 当下面的选择框全部点满后,上面的选择框自动的选中
  • 为下面的选择框添加单击事件
  • 用checked选择器,查看被选中的个数
    //给check_item绑定单击事件
        $(document).on("click",".check_item",function(){
            //判断当前选中的元素是否全选
            var flag = ($(".check_item:checked").length == $(".check_item").length);
             $("#check_all").prop("checked",flag);
        });
    
(3)批量删除
  • 给全部删除的按钮添加id
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DJbvG8wp-1574755610965)( https://raw.githubusercontent.com/danqing-hub/images/master/3ce6d9a888f9db12582a0438c5e43c39.png )]

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

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

     //点击全部删除,就批量删除
        $("#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);
                  }
               });
           }
    
        });
    
  • 把原来的方法改成单个批量二合一的方法

    /***
         * 删除多个员工(批量)
         * @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中创建可以同时删除多个的方法

        /**
         * @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();
        }
    

           //组装员工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);
                }
             });
         }

      });
  ```
  • 把原来的方法改成单个批量二合一的方法
    /***
         * 删除多个员工(批量)
         * @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中创建可以同时删除多个的方法
        /**
         * @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项目就完成了!!!

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值