SSM整合-CRUD

一、创建一个Maven工程

1、引入项目依赖的jar包

srping、springMVC、mybatis、数据库连接池、数据库驱动、jstl、servlet-api、junit

2、引入bootstarp前端框架

官网查看,大致在index.jsp中配置如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link rel="stylesheet" href="css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">

    <!-- HTML5 shim 和 Respond.js 是为了让 IE8 支持 HTML5 元素和媒体查询(media queries)功能 -->
    <!-- 警告:通过 file:// 协议(就是直接将 html 页面拖拽到浏览器中)访问页面时 Respond.js 不起作用 -->
    <!--[if lt IE 9]>
    <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
    <![endif]-->
</head>
<body>

    <!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
    <script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
    <!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
    <script src="js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>

    <button class="btn btn-success">按钮</button>
</body>
</html>

3、编写ssm整合相关的配置文件

web.xml、applicationContext.xml、spring_mvc.xml、jdbc.properties、XxxMapper.xml、sqlMapConfig.xml等,使用mybatis的逆向工程生成对应的bean以及mapper

1) 首先配置web.xml文件:

① 启动spring容器,配置spring监听器,作用是项目已启动,就加载初始化参数中的配置文件;

② springMVC的前端控制器,拦截匹配的请求,Servlet拦截匹配规则要自己定义,把拦截下来的请求,依据相应的规则分发到目标Controller来处理,是配置spring MVC的第一步;

③ 字符编码过滤器,一定要放在所有过滤器之前。作用是解决乱码问题;

④ 使用Rest风格的URI(原本是发不出delete、put方式的请求),把页面的post请求转为delete、put的请求

web.xml如下:

<?xml version="1.0" encoding="UTF-8" ?>

<web-app
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
>
  <display-name>Archetype Created Web Application</display-name>

  <!--1、启动spring的容器-->
  <!--配置spring中的监听器,
      项目一启动,就加载参数中的配置文件-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!--配置spring中的全局初始化参数-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>

  <!--2、springMVC的前端控制器,拦截所有请求-->
  <servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>  <!--全局初始化参数,加载配置文件-->
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring_mvc.xml</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

  <!--3、字符编码过滤器,一定要放在所有过滤器之前-->
  <filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param> <!--初始化参数,指定初始UTF-8编码-->
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
      <param-name>forceResponseEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
    <init-param>
      <param-name>forceRequestEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <!--4、使用Rest风格的URI(原本是发不出delete、put方式的请求)
      把页面的post请求转为delete、put的请求-->
  <filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

2) 接着配置springMVC的配置文件spring-mvc.xml

作用 SpringMVC的配置文件,包含网站跳转、逻辑的控制配置。

① 组件扫描(此配置文件中,只扫描控制器controller);

② 配置内部资源视图解析器,方便页面返回(jsp文件都放入WEB-INF/views中);

③ 两个标准配置:
	开放静态资源的访问:先找匹配地址,如果找不到就交给原始容器(Tomcat)找对应的静态资源;
	mvc的注解驱动:默认底层就会继承jackson进行对象或集合的json格式字符串的转换,能支持springMVC更高级的一些功能,JSR303校验,快捷的ajax...映射动态请求。

spring-mvc.xml如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

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

    <!--配置内部资源视图解析器,方便页面返回-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--拿到前缀,视图的名称,jsp文件都放入WEB-INF/views中-->
        <property name="prefix" value="/WEB-INF/views/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!--两个标准配置-->
    <!--① 开放静态资源的访问,先找匹配地址,如果找不到就交给原始容器(Tomcat)找对应的静态资源-->
    <mvc:default-servlet-handler/>
    <!--② mvc的注解驱动,默认底层就会继承jackson进行对象或集合的json格式字符串的转换
            能支持springMVC更高级的一些功能,JSR303校验,
            快捷的ajax...映射动态请求-->
    <mvc:annotation-driven/>

</beans>

3) 接着配置spring的配置文件applicationContext.xml

作用 spring配置文件,主要配置和业务逻辑相关的,核心点:数据源、与mybatis的整合、事务控制

① 组件扫描(此配置文件中,不扫控制器,其他都扫);

② 配置数据源(使用加载外部properties文件的方式)

③ 配置和MyBatis的整合(指定核心配置文件[sqlMapConfig.xml]的位置、mapper映射文件[mapper/*.xml]的位置)、(配置扫描器,扫描mybatis所有dao接口的实现,加入到ioc容器中)

④ 事务控制的配置(主要使用xml配置形式的事务方式)

jdbc.properties如下:

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/ssm_crud?serverTimezone=GMT
jdbc.user=root
jdbc.password=4680

applicationContext.xml如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
                        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

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

    <!--扫描组件-->
    <context:component-scan base-package="com.chu">
        <!--不扫控制器,其他都扫-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!--加载外部的properties文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--配置数据源-->
    <bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <!--===========配置和MyBatis的整合===========-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--指定mybatis核心(全局)配置文件的位置-->
        <property name="configLocation" value="classpath:sqlMapConfig.xml"></property>
        <property name="dataSource" ref="pooledDataSource"></property>
        <!--指定mybatis的mapper文件的位置(映射文件)-->
        <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
    </bean>

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

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

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

</beans>

4) 接着配置mybatis的核心配置文件sqlMapConfig.xml(也可以在applicationContext.xml中写入,但一般单独写出):

① 驼峰命名规则
② 自定义别名,顺序要在properties之后,mapping之前
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <!--驼峰命名规则-->
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <!--自定义别名,顺序要在properties之后,mapping之前-->
    <typeAliases>
        <typeAlias type="com.chu.crud.bean"/>
    </typeAliases>
</configuration>

spring的在applicationContext.xml中整合mybatis时已经指定了映射文件的位置,故不需要在核心配置文件中使用映射器加载映射文件。

例如,不需要如下配置:

<!--映射器 加载映射文件-->
<mappers>
    <mapper resource="com/chu/mapper/UserMapper.xml"></mapper>
</mappers>

5创建出mapper文件中XxxMapper.xml对应的dao接口的表

tbl_emp表:
在这里插入图片描述

tbl_dept表:
在这里插入图片描述
建立外键(两表联系):
在这里插入图片描述


6创建出tbl_emp表、tbl_dept表对应的dao、bean类、mapper文件中XxxMapper.xml配置文件

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

使用说明网址如下:

http://mybatis.org/generator/index.html

导入mybatis-generator-core的坐标

	<!--MBG-->
    <dependency>
      <groupId>org.mybatis.generator</groupId>
      <artifactId>mybatis-generator-core</artifactId>
      <version>1.3.7</version>
    </dependency>

② 在当前工程下创建一个普通xml文件:mbg.xml
在这里插入图片描述
示例:网页中左侧XML Configuration Reference

先删除<classPathEntry location=…>,生成自己的。

① 生成不带注释的
② 配置数据库连接
③ 指定javaBean生成的位置
④ 指定sql映射文件生成的位置
⑤ 指定dao接口的生成位置
⑥ table标签指定每个表的生成策略
<?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.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/ssm_crud?serverTimezone=GMT"
                        userId="root"
                        password="4680">
        </jdbcConnection>

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

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

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

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

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

    </context>
</generatorConfiguration>

生成对应的bean、dao以及mapper

网站左侧:Running MyBatis Generator→From another Java program with an XML configuration中点击Java program获取示例。

MGBTest生成: 注意代码中修改配置文件名为mbg.xml提示File错误,要手动导包即可。

public class MGBTest {

    @Test
    public void test1() throws Exception{
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        File configFile = new File("mbg.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
    }
}

7在生成的XxxMapper.xml映射文件中增加表之间的联查语句:

需求: 查询员工的同时部门信息也得到查询(一对一:一个员工对应一个部门)

Employee类中加入Department部门类的属性:

public class Employee {
    private Integer empId;

    private String empName;

    private String gender;

    private String email;

    private Integer dId;

    //希望查询员工的同时部门信息也得到查询
    //  (一对一:一个员工对应一个部门)
    private Department department;

get、set、toString...
}

EmployeeMapper接口中加入selectByExampleWithDept、selectByPrimaryKeyWithDept抽象方法:

public interface EmployeeMapper {
	...
	...
	//查询员工同时带部门信息
	List<Employee> selectByExampleWithDept(EmployeeExample example);
	
    Employee selectByPrimaryKeyWithDept(Integer empId);
	...
	...
}

EmployeeMapper.xml中的sql语句:

新增的resultMap(查询之后的返回集形式)

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

新增的公共sql语句

  <sql id="WithDept_Column_List">
    e.emp_id, e.emp_name, e.gender, e.email, e.d_id, d.dept_id, d.dept_name
  </sql>

新增的查询方法

  <!--查询员工同时带部门信息-->
  <select id="selectByExampleWithDept" resultMap="WithDeptResultMap">
    select
    <if test="distinct">
      distinct
    </if>
    <include refid="WithDept_Column_List" />
    from tbl_emp e
    left join tbl_dept d on e.`d_id`=d.`dept_id`
    <if test="_parameter != null">
      <include refid="Example_Where_Clause" />
    </if>
    <if test="orderByClause != null">
      order by ${orderByClause}
    </if>
  </select>

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

4、测试mapper

由于XxxMapper在spring配置文件(applicationContext.xml)中把其加入到容器中:

方式一:可以通过如下获取mapper对象:

		//1、创建SoringIOC容器
        ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2、从容器中获取mapper
        DepartmentMapper departmentMapper = ioc.getBean(DepartmentMapper.class);

方式二:注解方式

步骤如下:

1、导入Spring-test模块
2、@ContextConfiguration指定spring配置文件的位置
3、直接@Autowired要使用的组件即可
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class MapperTest {

    @Autowired
    DepartmentMapper departmentMapper;

    @Autowired
    EmployeeMapper employeeMapper;

    @Autowired
    SqlSession sqlSession;

    //测试DepartmentMapper
    @Test
    public void testCRUD(){

        //1、插入几个部门
        /*
        departmentMapper.insertSelective(new Department(null,"开发部"));
        departmentMapper.insertSelective(new Department(null,"测试部"));
        */

        //2、生成员工数据,测试员工插入
        //employeeMapper.insertSelective(new Employee(null,"Jerry","M","Jerry@qq.com",1));
        
        //3、批量插入多个员工:使用可以执行批量操作的sqlSession
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        for(int i=0;i<=100;i++){
            String uid = UUID.randomUUID().toString().substring(0, 5)+i;
            mapper.insertSelective(new Employee(null,uid,"M",uid+"@qq.com",1));
        }
        System.out.println("批量完成");
    }
}

二、查询展示

步骤:

① 访问index.jsp页面

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

③ EmploeeController来接受请求,查出员工数据

④ 来到list.jsp页面进行展示

①-②:请求转发到当前项目下的emps请求

<%--请求转发到当前项目下的emps请求--%>
<jsp:forward page="/emps"></jsp:forward>

③ EmploeeController来接受请求,查出员工数据:(查出的数据要分页,mybatis笔记中也有提到)

pop.xml中引入分页坐标

<!--引入PageHelper分页插件-->
    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>5.2.0</version>
    </dependency>
    <dependency>
      <groupId>com.github.jsqlparser</groupId>
      <artifactId>jsqlparser</artifactId>
      <version>4.0</version>
    </dependency>

接受请求,查询数据:

@Controller
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;

    /*查询员工该数据(分页查询)*/
    @RequestMapping("/emps")          //value传入的当前页,model用来保存分页数据
    public String getEmps(@RequestParam(value = "pn",defaultValue = "1")Integer pn, Model model){
        //这不是一个分页查询
        //引入PageHelper分页插件

        //设置分页相关参数  当前页+每页显示的条数
        PageHelper.startPage(pn,5);

        //startPage后面紧跟的这个查询就是分页查询
        List<Employee> emps=employeeService.getAll();
        //使用pageInfo包装查询后的结果,只需要pageInfo交给页面就行了
        //     封装了详细的分页信息,包括查询出的数据,和连续传入的页数
        PageInfo page = new PageInfo<>(emps,5);
        model.addAttribute("pageInfo",page);

        return "list";  //因为有视图解析器,地址就是/WEB-INF/views/list.jsp
    }
}

list页面展示:

利用遍历展示查询到的数据,同时用到判断控制页面展示
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>员工列表</title>
    <%
       pageContext.setAttribute("App_PATH",request.getContextPath());
    %>
    <%--web路径:
        不以/开始的相对路径,找资源,以当前资源的路径为基准,经常容易出问题
        以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:3306),需要加上项目名
                    http://localhost:3306/crud
    --%>
    <script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="${App_PATH}/static/bootstrap-3.4.1-dist/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
    <script src="${App_PATH}/static/bootstrap-3.4.1-dist/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
</head>
<body>
        <%--搭建显示页面--%>
        <div class="container">
            <%--标题--%>
            <div class="row"></div>
                <div class="col-md-12">
                    <h1>SSM-SRUD</h1>
                </div>
            <%--按钮--%>
            <div class="row"></div>
                <div class="col-md-4 col-md-offset-8">
                    <button class="btn btn-primary">新增</button>
                    <button class="btn btn-danger">删除</button>
                </div>
            <%--显示表格数据--%>
            <div class="row"></div>
                <div class="col-md-12">
                    <table class="table table-hover">
                        <tr>
                            <th>#</th>
                            <th>empName</th>
                            <th>gender</th>
                            <th>email</th>
                            <th>deptName</th>
                            <th>操作</th>
                        </tr>
                        <c:forEach items="${pageInfo.list}" var="emp">
                            <tr>
                                <th>${emp.empId}</th>
                                <th>${emp.empName}</th>
                                <th>${emp.gender=="M"?"男":"女"}</th>
                                <th>${emp.email}</th>
                                <th>${emp.department.deptName}</th>
                                <th>
                                    <button class="btn btn-primary btn-sm">
                                        <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
                                        编辑
                                    </button>
                                    <button class="btn btn-danger btn-sm">
                                        <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
                                        删除
                                    </button>
                                </th>
                            </tr>
                        </c:forEach>

                    </table>
                </div>
            <%--显示分页信息--%>
            <div class="row">
                <%--分页文字信息--%>
                <div class="col-md-6">
                    当前${pageInfo.pageNum}页,总${pageInfo.pages}页,总${pageInfo.total}条记录
                </div>
                <%--分页条信息--%>
                <div class="col-md-6">
                    <nav aria-label="Page navigation">
                        <ul class="pagination">
                            <li><a href="${App_PATH}/emps?pn=1">首页</a> </li>
                            <c:if test="${pageInfo.hasPreviousPage}">
                                <li>
                                    <a href="${App_PATH}/emps?pn=${pageInfo.pageNum-1}" aria-label="Previous">
                                        <span aria-hidden="true">&laquo;</span>
                                    </a>
                                </li>
                            </c:if>

                            <c:forEach items="${pageInfo.navigatepageNums}" var="page_Num">
                                <c:if test="${page_Num==pageInfo.pageNum}">
                                    <li class="active"><a href="#">${page_Num}</a> </li>
                                </c:if>
                                <c:if test="${page_Num!=pageInfo.pageNum}">
                                    <li><a href="${App_PATH}/emps?pn=${page_Num}">${page_Num}</a> </li>
                                </c:if>
                            </c:forEach>

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

                            <li><a href="${App_PATH}/emps?pn=${pageInfo.pages}">末页</a> </li>
                        </ul>
                    </nav>
                </div>
            </div>
        </div>
</body>
</html>

缺点:现阶段只支持浏览器端到服务端,安卓端和IOS不能满足。

解决方案:使用json格式数据。提升了系统的拓展性。


三、优化查询-ajax

步骤:

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

② 服务器将查出的数据,以json字符串的形式返回给浏览器

③ 浏览器收到json字符串。可以使用js对json进行解析,使用js通过dom增删改查改变页面。

④ 返回json。实现客户端的无关性。

<%--请求转发到当前项目下的emps请求--%>
<jsp:forward page="/emps"></jsp:forward>

② 借助Msg类返回状态码和提示信息,以及用链式返回的方式返回给浏览器数据(EmployeeController调用Msg类中的的函数,然后此函数的返回值,再调用其内部的函数

Msg类:

public class Msg {
    // 状态码 100-成功 200-失败
    private int code;
    //提示信息
    private String msg;

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

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

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

    public Msg add(String key,Object value){
        this.getExtend().put(key,value);
        return this;
    
get、set、toString...

处理请求方式二: - - - 使用 @ResponseBody 将返回对象自动转为json字符串

@Controller
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;

    /*方式二:导入jackson包,直接将返回的对象转为json字符串*/
    @RequestMapping("/emps")
    @ResponseBody //将返回的对象自动转为json字符串
    public Msg getEmpWithJson(@RequestParam(value = "pn",defaultValue = "1")Integer pn){
        //这不是一个分页查询
        //引入PageHelper分页插件

        //设置分页相关参数  当前页+每页显示的条数
        PageHelper.startPage(pn,5);

        //startPage后面紧跟的这个查询就是分页查询
        List<Employee> emps=employeeService.getAll();
        //使用pageInfo包装查询后的结果,只需要pageInfo交给页面就行了
        //     封装了详细的分页信息,包括查询出的数据,和连续传入的页数
        PageInfo page = new PageInfo<>(emps,5);

        //链式返回,有返回的处理的状态和数据
        return Msg.success().add("pageInfo",page);
    }

③ ajax请求得到分页数据,解析处理数据

ajxa请求中调用解析显示员工数据、解析显示分页信息、解析显示分页条的函数

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>
<head>
    <title>员工列表</title>

    <%
        pageContext.setAttribute("App_PATH",request.getContextPath());
    %>
    <%--web路径:
        不以/开始的相对路径,找资源,以当前资源的路径为基准,经常容易出问题
        以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:3306),需要加上项目名
                    http://localhost:3306/crud
    --%>
    <script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="${App_PATH}/static/bootstrap-3.4.1-dist/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
    <script src="${App_PATH}/static/bootstrap-3.4.1-dist/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>

</head>
<body>
<%--搭建显示页面--%>
<div class="container">

    <%--标题--%>
    <div class="row"></div>
    <div class="col-md-12">
        <h1>SSM-SRUD</h1>
    </div>

    <%--按钮--%>
    <div class="row"></div>
    <div class="col-md-4 col-md-offset-8">
        <button class="btn btn-primary">新增</button>
        <button class="btn btn-danger">删除</button>
    </div>

    <%--显示表格数据--%>
    <div class="row"></div>
    <div class="col-md-12">
        <table class="table table-hover" id="emps_table">
            <%--表头放在thead中--%>
            <thead>
                <tr>
                    <th>#</th>
                    <th>empName</th>
                    <th>gender</th>
                    <th>email</th>
                    <th>deptName</th>
                    <th>操作</th>
                </tr>
            </thead>
            <%--单元格数据放tbody中--%>
            <tbody>

            </tbody>
        </table>
    </div>

    <%--显示分页信息--%>
    <div class="row">
        <%--分页文字信息--%>
        <div class="col-md-6" id="page_info_area"></div>
        <%--分页条信息--%>
        <div class="col-md-6" id="page_nav_area"></div>
    </div>

</div>
    <script type="text/javascript">
        //1、页面加载完成以后,直接去发送一个ajax请求,要到分页数据。
        $(function (){
            //去首页
            to_page(1);
        });

        //跳转到对应页
        function to_page(pn){
            $.ajax({
                url:"${App_PATH}/emps",      //一个用来包含发送请求的URL字符串
                data:"pn="+pn,
                type:"GET",
                success:function (result){
                    //console.log(result);  输出获取的json字符串,下面是处理
                    //1、调用解析并显示员工数据的函数
                    build_emps_table(result);
                    //2、调用解析并显示分页信息的函数
                    build_page_info(result);
                    //3、调用解析显示分页条的函数
                    build_page_nav(result);
                }
            });
        }

        //解析显示员工数据,返回数据中extend中pageInfo中的list属性
        function build_emps_table(result){
            //每次调用清空table表格,不然会累加
            $("#emps_table tbody").empty();

            var emps = result.extend.pageInfo.list;
            $.each(emps,function(index,item){
                //以下操作是把遍历的数据加入到单元格td中,然后将数据再加入到每行tr中
                //          最后添加到id为emps_table表格的tbody中
                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-primary btn-sm">
                                        <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
                                        编辑
                                    </button>

                                    <button class="btn btn-danger btn-sm">
                                        <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
                                        删除
                                    </button>
                * */
                var editBtn=$("<button></button>").addClass("btn btn-primary btn-sm")
                                .append($("<span></span>")).addClass("glyphicon glyphicon-pencil").append("编辑");
                var delBtn=$("<button></button>").addClass("btn btn-danger btn-sm")
                                .append($("<span></span>")).addClass("glyphicon glyphicon-trash").append("删除");
                //把删除和编辑放入一个单元格中
                var btnTd=$("<td></td>").append(editBtn).append(" ").append(delBtn);
                // append方法执行完成以后还是返回原来的元素
                $("<tr></tr>").append(empIdTd)
                    .append(empNameTd)
                    .append(genderTd)
                    .append(emailTd)
                    .append(deptNameTd)
                    .append(btnTd)
                    .appendTo("#emps_table tbody");
            });
        }

        //解析显示分页信息
        function build_page_info(result){
            //清空,避免累加
            $("#page_info_area").empty();

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


        //解析显示分页条
        function build_page_nav(result){
            //清空,避免累加
            $("#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.extend.pageInfo.hasPreviousPage==false){
                firstPageLi.addClass("disabled")
                prePageLi.addClass("disabled")
            }else {
                //为元素添加点击翻页的事件
                firstPageLi.click(function (){
                    to_page(1);
                });
                prePageLi.click(function (){
                    to_page(result.extend.pageInfo.pageNum-1);
                });
            }

            var nextPageLi =$("<li></li>").append($("<a></a>").append("&raquo;"));
            var lastPageLi =$("<li></li>").append($("<a></a>").append("末页").attr("href","#"));
            //判断如果是否有后一页,是的话则禁用
            if (result.extend.pageInfo.hasNextPage==false){
                lastPageLi.addClass("disabled")
                nextPageLi.addClass("disabled")
            }else {
                //为元素添加点击翻页的事件
                lastPageLi.click(function (){
                    to_page(result.extend.pageInfo.pages);
                });
                nextPageLi.click(function (){
                    to_page(result.extend.pageInfo.pageNum+1);
                });
            }

            //添加首页和前一页的提示
            ul.append(firstPageLi).append(prePageLi);
            // 页码号遍历给ul
            $.each(result.extend.pageInfo.navigatepageNums,function (index,item){
                var numLi =$("<li></li>").append($("<a></a>").append(item));
                if(result.extend.pageInfo.pageNum==item){
                    numLi.addClass("active");
                }
                //给遍历得到的每个页码绑定单击事件,点击时发送ajax请求
                numLi.click(function (){
                    to_page(item)
                });
                ul.append(numLi);
            });
            //添加末页和下一页的提示
            ul.append(nextPageLi).append(lastPageLi);

            //把ul加入到nav
            var navEle=$("<nav></nav>").append(ul);
            navEle.appendTo("#page_nav_area");
        }
    </script>
</body>
</html>

四、增加

步骤:

1、 再index.jsp页面点击“新增”

2、 弹出新增对话框

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

4、 用户输入数据,并进行校验
	jQuery前端合法性校验,ajax用户名重复校验,重要数据[后端校验(JSR303),唯一约束]

5、 完成保存

URI:

/emp/{id}		GET查询员工
/emp			POST保存员工
/emp/{id}		PUT修改员工
/emp/{id}		DELETE删除员工

1-2、 弹出新增对话框 - - -员工添加模态框

<!-- 员工添加的模态框 -->
<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">
                        </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="email@chu.com">
                        </div>
                    </div>
                    <div class="form-group">
                        <label  class="col-sm-2 control-label">gender</label>
                        <div class="col-sm-10">
                            <%--多选--%>
                            <label class="radio-inline">
                                <input type="radio" name="gender" id="gender1_add_input" value="M" checked="checked"></label>
                            <label class="radio-inline">
                                <input type="radio" name="gender" id="gender2_add_input" value="F"></label>
                        </div>
                    </div>
                    <div class="form-group">
                        <label  class="col-sm-2 control-label">deptName</label>
                        <div class="col-sm-4">
                            <%--下拉列表 变化的,部门提交部门id即可--%>
                            <select class="form-control" name="dId">
                            </select>
                        </div>
                    </div>
                    <div class="form-group">
                        <div class="col-sm-offset-2 col-sm-10">
                            <div class="checkbox">
                                <label>
                                    <input type="checkbox"> Remember me
                                </label>
                            </div>
                        </div>
                    </div>
                </form>
            </div>

            <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
                <button type="button" class="btn btn-primary">保存</button>
            </div>
        </div>
    </div>
</div>

给页面 “新增”按钮中绑定单击事件 ,并使用javascript代码,通过id元素调用上述模态框

		//绑定新增按钮的事件
        $("#emp_add_modal_btn").click(function (){
            $("#empAddModal").modal({
               backdrop:"static"
            });
        });

3、 弹出模态框之前,给服务器发送一个ajax请求获取数据库中的部门信息,显示在对话框中:

在每一次打开新增的之前都要 清空表单的样式和内容

        //清空表单样式及内容
        function reset_form(ele){
            $(ele)[0].reset();
            //清空表单样式
            $(ele).removeClass("has-error has-success");
            $(ele).find(".help-block").text("")
        }
		//绑定新增按钮的事件
        //点击新增按钮弹出模态框
        $("#emp_add_modal_btn").click(function (){
			//清除表单数据(表单重置-表单的数据,表单的样式) 调用函数
            reset_form("#empAddModal form");

            //发送ajax请求,查出部门信息,显示在下拉列表中,调用下面的函数
            getDepts();

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

        //查出所以的部门信息并显示在下拉列表中
        function getDepts(){
            //清空,避免累加
            $("#dept_add_select").empty();
			//ajax请求去查询获取部门信息
            $.ajax({
                url:"${App_PATH}/depts",
                type:"GET",
                success:function (result){
                    //{"code":100,"msg":"处理成功!","extend":{"depts":[{"deptId":1,"deptName":"开发部"},{"deptId":2,"deptName":"测试部"}]}}
                    //显示部门信息在下拉列表中
                    //$("#dept_add_select").append(""),下面遍历后添加
                    $.each(result.extend.depts,function (){
                        var optionEle=$("<option></option>").append(this.deptName).attr("value",this.deptId);
                        //把添加部门信息后的变量添加到id为dept_add_select的下拉列表中
                        optionEle.appendTo("#dept_add_select");
                    });
                }
            });
        }

处理请求的控制器DepartmentController:

@Controller
public class DepartmentController {

    @Autowired
    private DepartmentService departmentService;

    //返回所有部门的信息
    @RequestMapping("/depts")
    @ResponseBody
    public Msg getDepts(){
        //查处的所有部门信息
        List<Department> list=departmentService.getDepts();
        return Msg.success().add("depts",list);
    }
}

4、 输入数据并校验 - 校验姓名和邮箱是否符合格式

1jQuery前端合法性校验

		//校验表单数据
        function validate_add_form(){
            //1、拿到要校验的名字数据,使用正则表达式
            var empName=$("#empName_add_input").val();
            var regName=/(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2e80-\u9FFF]{2,5}$)/;
            if (!regName.test(empName)){
                alert("用户名可以是2-5位中文或者6-16位英文和数字的组合")
                return false;
            };

            //2、校验邮箱信息
            var email=$("#email_add_input").val();
            var regEmail=/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
            if (!regEmail.test(email)){
                alert("邮箱格式不正确")
                return false;
            };
            return true;
        }

弹窗提示不够美观,使用表单的校验状态来美化校验

要在表单中加入框架中的格式来美化校验

		<%--美化校验--%>
        <span class="help-block"></span>
        //校验表单数据
        function validate_add_form(){
            //1、拿到要校验的名字数据,使用正则表达式
            var empName=$("#empName_add_input").val();
            var regName=/(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2e80-\u9FFF]{2,5}$)/;
            if (!regName.test(empName)){
                //alert("用户名可以是2-5位中文或者6-16位英文和数字的组合")
                //应该清空这个元素之前的样式
                show_validate_msg("#empName_add_input","error","用户名可以是2-5位中文或者6-16位英文和数字的组合");
                return false;
            }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)){
                //alert("邮箱格式不正确")
                // 使用框架对其进行美化,给当前控件的父元素添加需要的类
                show_validate_msg("#email_add_input","error","邮箱格式不正确");
                return false;
            }else {
                show_validate_msg("#email_add_input","success","");
            };
            return true;
        }

        /*
        * 抽取出的校验函数,ele表示当前元素,status表示状态,msg表示提示信息
        * */
        function show_validate_msg(ele,status,msg){
            //清楚当前元素的校验状态
            $(ele).parent().removeClass("has-success has-error");
            $(ele).next("span").text("");

            if ("success"==status){
                $(ele).parent().addClass("has-success");
                //如果span中有内容也清空
                $(ele).next("span").text(msg);
            }else if ("error"==status){
                // 使用框架对其进行美化,给当前控件的父元素添加需要的类
                $(ele).parent().addClass("has-error");
                //紧邻的span元素中的内容就是要提示的信息
                $(ele).next("span").text(msg)
            }
        }

2ajax用户名重复校验

ajax校验员工是否重复:

当员工姓名输入之后,发送ajxa请求给服务器,服务器告知用户名是否可用,不可用需要提示

EmployeeController中处理ajax请求:

@Controller
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;

    //校验员工名是否可用
    @RequestMapping("/checkuser")
    @ResponseBody
    public Msg checkuser(@RequestParam("empName") String empName){
		//先判断用户名是否合法
        String regx="(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2e80-\u9FFF]{2,5}$)";
        if (!empName.matches(regx)){
           return Msg.fail().add("va_msg","用户名必须是6-16位英文和数字的组合或者2-5位中文");
        }
        
        //判断用户名是否存在
        boolean b=employeeService.checkUser(empName);
        if(b){
            return Msg.success();
        }else {
            return Msg.fail().add("va_msg","用户名不可用");;
        }
    }
}

EmployeeService类中添加校验方法:

//校验用户名是否可用 true代表当前姓名可用,fasle表示不可用
    public boolean checkUser(String empName) {
        EmployeeExample example = new EmployeeExample();
        EmployeeExample.Criteria criteria = example.createCriteria();
        criteria.andEmpNameEqualTo(empName);
        long count = employeeMapper.countByExample(example);
        return count == 0;
    }

index.jsp页面添加校验发送ajax校验请求
给按钮添加attr自定义属性来赋值请求得到的状态为后面保存前通过此属性的值进行判断。

		//员工姓名输入框绑定事件,用于校验用户名是否可用
        $("#empName_add_input").change(function (){
            //发送ajax求情校验用户名是否可用
            var empName =this.value;
            $.ajax({
                url:"${App_PATH}/checkuser",
                data:"empName="+empName,
                type:"POST",
                success:function (result){
                    if (result.code==100){
                        show_validate_msg("#empName_add_input","sucess","用户名可用")
                        //给按钮添加attr自定义属性来赋值请求得到的状态
                        $("#emp_save_btn").attr("ajax-va","success");
                    }else {
                        show_validate_msg("#empName_add_input","error",result.extend.va_msg)
                        $("#emp_save_btn").attr("ajax-va","error");
                    }
                }
            });
        });

3重要数据[后端校验(JSR303)]

导入JSR303数据校验的包

	<!--JSR303数据校验支持,tomacat7及以上的服务器,
      tomacat7一下的服务器:el表达式,额外给服务器的lib包中替换新的标准的el
    -->
    <dependency>
      <groupId>org.hibernate.validator</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>6.1.5.Final</version>
    </dependency>

给要校验的属性自定义校验规则 Employee的属性:empName、email…

public class Employee {
    private Integer empId;

    // JSR303,给要校验的属性自定义校验规则
    @Pattern(regexp = "(^[a-zA-Z0-9_-]{6,16}$)|(^[\\u2e80-\\u9FFF]{2,5}$)"
            ,message = "用户名必须是6-16位英文和数字的组合或者2-5位中文")
    private String empName;

    private String gender;

    //@Email 可用用本身存在的注解,为了保持和前端校验的一致,使用自定义
    @Pattern(regexp = "^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$"
            ,message = "邮箱格式不正确")
    private String email;
    
......
}

EmployeeController处理请求时,对传入的对象属性进行校验,result封装校验结果:

@Controller
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;
    
    //员工数据保存
    /*
    * 1、要支持JSR303校验,需要导入Hibernate-Validator包
    * */
    @RequestMapping(value = "/emp",method = RequestMethod.POST)
    @ResponseBody
    //                  Valid代表此属性要校验,result封装校验的结果
    public Msg saveEmp(@Valid Employee employee, BindingResult result){
        if (result.hasErrors()){
            //校验失败,返回失败,在模态框中显示校验失败的错误信息
            Map<String,Object> map =new HashMap<>();
            List<FieldError> errors = result.getFieldErrors();
            for (FieldError fieldError:errors){
                System.out.println("错误的字段名:"+fieldError.getField());
                System.out.println("错误信息:"+fieldError.getDefaultMessage());
                map.put(fieldError.getField(),fieldError.getDefaultMessage());
            }
            return Msg.fail().add("errorFields",map);
        }else {
            employeeService.saveEmp(employee);
            return Msg.success();
        }
    }
    
...
}

在发送ajax请求保存员工时,进行后端JSR303校验下面代码的第3步

        //给保存输入数据的按钮绑定单击事件
        $("#emp_save_btn").click(function (){
            // 模态框中填写的表单数据提交给服务器进行保存
            //1、先对要提交给服务器的数据进行前端校验
            if (!validate_add_form()){
                return false;
            };
            //2、再判断之前的ajax用户名校验是否成功,利用请求后attr自定义属性添加的状态进行判断
            if ($(this).attr("ajax-va")=="error"){
                return false;
            }

            //3、发送ajax请求保存员工  表单中只有一个form表单,序列化得到输入的数据
            $.ajax({
                url:"${App_PATH}/emp",
                type:"POST",
                data: $("#empAddModal form").serialize(),
                success:function (result){
                    // 根据JSR303校验封装的校验结果中的状态码,判断是否合法
                    if (result.code==100){
                        //员工保存成功
                        //1、关闭模态框
                        $("#empAddModal").modal('hide');

                        //2、来到最后一页,显示刚才保存的数据
                        //  发送ajax请求显示最后一页数据即可,总页数+1
                        to_page(totalPages+1);
                    }else {
                        //显示失败信息
                        //console.log(result);
                        //有哪个字段的错误信息就显示哪个字段的
                        if (undefined != result.extend.errorFields.email){
                            //显示邮箱错误信息 调用校验函数
                            show_validate_msg("#email_add_input","error",result.extend.errorFields.email)
                        }

                        if (undefined != result.extend.errorFields.empName){
                            //显示员工名字的错误信息
                            show_validate_msg("#empName_add_input","error",result.extend.errorFields.empName)
                        }
                    }
                }
            });
        });

5、 保存数据

利用按钮id绑定单击事件: 要定义一个全局变量保存总页数,调用两步校验:先对提交给服务器的数据进行合法性的校验,再发送ajax请求校验用户名是否存在

		//给保存输入数据的按钮绑定单击事件
        $("#emp_save_btn").click(function (){
            //1、模态框中填写的表单数据提交给服务器进行保存
            
            //1、先对要提交给服务器的数据进行校验
            if (!validate_add_form()){
                return false;
            };
            //2、再判断之前的ajax用户名校验是否成功,利用请求后attr自定义属性添加的状态进行判断
            if ($(this).attr("ajax-va")=="error"){
                return false;
            }

            //3、发送ajax请求保存员工  表单中只有一个form表单,序列化得到输入的数据
            $.ajax({
                url:"${App_PATH}/emp",
                type:"POST",
                data: $("#empAddModal form").serialize(),
                success:function (result){
                    //员工保存成功
                    //1、关闭模态框
                    $("#empAddModal").modal('hide');
                    //2、来到最后一页,显示刚才保存的数据
                    //  发送ajax请求显示最后一页数据即可,总页数+1
                    to_page(totalPages+1);
                }
            });
        });

EmployeeController处理请求: 请求方法要是POST表示保存数据

@Controller
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;

    @RequestMapping(value = "/emp",method = RequestMethod.POST)
    @ResponseBody
    public Msg saveEmp(Employee employee){
        employeeService.saveEmp(employee);
        return Msg.success();

    }
}

五、修改

步骤:

1、点击编辑

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

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

1-2、点击编辑,弹出用户修改的模态框

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>
                            <%--美化校验--%>
                            <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_update_input" placeholder="email@chu.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-4">
                            <%--下拉列表 变化的,部门提交部门id即可--%>
                            <select class="form-control" name="dId" id="dept_add_select">
                            </select>
                        </div>
                    </div>
                    <div class="form-group">
                        <div class="col-sm-offset-2 col-sm-10">
                            <div class="checkbox">
                                <label>
                                    <input type="checkbox"> Remember me
                                </label>
                            </div>
                        </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>

2)给每行数据(解析显示员工数据时)的编辑和删除按钮添加标识(用于后面的事件绑定)

	...
 	// 给编辑按钮添加一个edit_btn标识,给删除按钮添加一个delete_btn标识,用于下面绑定单击事件
  	 var editBtn=$("<button></button>").addClass("btn btn-primary btn-sm edit_btn")
                	 .append($("<span></span>")).addClass("glyphicon glyphicon-pencil").append("编辑");
     var delBtn=$("<button></button>").addClass("btn btn-danger btn-sm delete_btn")
                     .append($("<span></span>")).addClass("glyphicon glyphicon-trash").append("删除");   
     ...         

3)普通的绑定方式是绑定不上的,因为按钮创建之前就绑定了click,所以绑定不上,采用下面方式② on

解决方式一:可用在创建按钮的时候绑定
解决方式二:绑定单击事件.live(), 但jQuery新版没有live,使用on进行替代,选文档中,给其后代绑定

编辑按钮绑定后,执行数据清除、①、②以及模态框的弹出

		$(document).on("click",".edit_btn",function (){

            //① 查出部门信息,并显示部门列表,上面新增时的ajax请求函数
            getDepts("#empUpdateModal select")

            //② 查出员工信息,显示员工信息,传入编辑按钮的自定义属性(其值为员工id)
            getEmp($(this).attr("edit-id"));

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

① 查询显示部门信息,调用函数getDepts

	//查出所有的部门信息并显示在下拉列表中
        function getDepts(ele){
            //清空对应下拉列表,避免累加
            $(ele).empty();

            $.ajax({
                url:"${App_PATH}/depts",
                type:"GET",
                success:function (result){
                    //{"code":100,"msg":"处理成功!","extend":{"depts":[{"deptId":1,"deptName":"开发部"},{"deptId":2,"deptName":"测试部"}]}}
                    //显示部门信息在下拉列表中
                    //$("#dept_add_select").append(""),下面遍历后添加
                    $.each(result.extend.depts,function (){
                        var optionEle=$("<option></option>").append(this.deptName).attr("value",this.deptId);
                        //把添加部门信息后的变量添加到id为dept_add_select的下拉列表中
                        optionEle.appendTo(ele);
                    });
                }
            });
        }

② 显示员工信息的发出ajax请求函数getEmp: 根据员工id查询获取数据,

编辑按钮的自定义属性edit-id(其值为员工id)

	...
 	// 给编辑按钮添加一个edit_btn标识,给删除按钮添加一个delete_btn标识,用于下面绑定单击事件
  	 var editBtn=$("<button></button>").addClass("btn btn-primary btn-sm edit_btn")
                	 .append($("<span></span>")).addClass("glyphicon glyphicon-pencil").append("编辑");
                	 
     //为编辑按钮添加一个自定义的属性,来表示当前员工id
      editBtn.attr("edit-id",item.empId);
      
     var delBtn=$("<button></button>").addClass("btn btn-danger btn-sm delete_btn")
                     .append($("<span></span>")).addClass("glyphicon glyphicon-trash").append("删除");   
     ...         

发出ajax请求获取对应id的数据:

		//获取员工信息的ajax请求
        function getEmp(id){
            $.ajax({
                url:"${App_PATH}/emp/"+id,
                type:"GET",
                success:function (result){
                    //console.log(result);
                    //拿到员工数据,给对应的表单赋值
                    var empData = result.extend.emp;
                    $("#empName_update_static").text(empData.empName);
                    $("#email_update_input").val(empData.email);
                    $("#empUpdateModal input[name=gender]").val([empData.gender]);
                    $("#empUpdateModal select").val([empData.dId]);
                }
            });
        }

EmployeeController处理ajxa请求获取员工数据并返回:

@Controller
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;


    //处理ajax请求以获取员工数据
    @RequestMapping(value = "/emp/{id}",method = RequestMethod.GET)
    @ResponseBody
    public Msg getEmp(@PathVariable("id") Integer id){

        Employee employee=employeeService.getEmp(id);
        return Msg.success().add("emp",employee);
    }

......
}

EmployeeService类中使用employeeMapper执行sql语句根据id获取员工数据:

@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;

    //按照员工id查询员工信息
    public Employee getEmp(Integer id) {
        Employee employee = employeeMapper.selectByPrimaryKey(id);
        return employee;
    }
    
	......
}

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

1)给模态框的==“更新”按钮绑定单击事件==(验证邮箱合法后发送ajxa请求):

要先把员工的id传递给模态框的更新按钮:

$("#emp_update_btn").attr("edit-id",$(this).attr("edit-id"));

绑定单击事件,验证邮箱合法性,发送ajax请求

ajax请求方式一 POST请求转为PUT请求

	//点击更新,更新员工信息
        $("#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)){
                //alert("邮箱格式不正确")
                // 使用框架对其进行美化,给当前控件的父元素添加需要的类
                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:"POST",
                data:$("#empUpdateModal form").serialize()+"&_method=PUT",
                success:function (result){
                    //alert(result.msg);
                    //1、关闭模态框
                    $("#empUpdateModal").modal("hide");
                    //2、回到本页面,使用到全局变量
                    to_page(currentPage);
                }
            });
        });

上述的ajax请求是将POST请求转为PUT请求来进行的(利用到了web.xml配置文件中的Rest风格的URI):

  <!--4、使用Rest风格的URI(原本是发不出delete、put方式的请求)
      把页面的post请求转为delete、put的请求-->
  <filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

ajax请求方式二 直接使用PUT请求

	//点击更新,更新员工信息
        $("#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)){
                //alert("邮箱格式不正确")
                // 使用框架对其进行美化,给当前控件的父元素添加需要的类
                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);
                 }
	       });       
      });

直接使用PUT发送请求,tomcat不会资助的封装PUT请求的参数,需要在web.xml中配置HttpPutFormContentFilter过滤器,这样SpringMVC才能得到前台发送来的请求。

具体解析:

配置上HttpPutFormContentFilter,它的作用是将请求体中的数据解析包装成一个map,request被重新包装,request。getParameter()被重写,就会从自己封装的map中取数据。
  <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>

2EmployeeController处理ajxa请求:

要是PUT请求,且参数名为empId与表名的一致,不然获取不到。

@RequestMapping(value = "/emp/{empId}",method = RequestMethod.PUT)
@Controller
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;


    //保存更新的员工数据
    @RequestMapping(value = "/emp/{empId}",method = RequestMethod.PUT)
    @ResponseBody
    public Msg updateEmp(Employee employee){
        employeeService.updateEmp(employee);
        return Msg.success();
    }
	......
}

六、删除

步骤:

1、单个删除
	URI:/emp/{id}	请求方式DELETE

1、单个删除

1)给每个员工数据的删除按钮绑定单击事件:

① 首先要获取此删除按钮对应的员工姓名

② 弹出是否删除的对话框

③ 判断是否确认删除,删除则发送ajax请求根据id(要给删除按钮自定义属性del-id获取要被删除的员工id)进行删除,删除后回到本页。
   //给每个员工数据的删除按钮绑定单击事件
   $(document).on("click",".delete_btn",function (){
       //1、弹出确认删除对话框
       //  找到要被删除的员工名,this标识当前点击的删除按钮的祖先tr下的第二个td里的内容
       // alert($(this).parents("tr").find("td:eq(1)").text());
       var empName=$(this).parents("tr").find("td:eq(1)").text();

       //拿到要被删除的员工id,(是删除按钮自定义属性 del-id)
       var empId=$(this).attr("del-id")

       if (confirm("确认删除【"+empName+"】吗?")){
           //确认,则发送ajax请求根据员工id删除
           $.ajax({
               url:"${App_PATH}/emp/"+empId,
               type:"DELETE",
               success:function (result){
                   alert(result.msg);
                   //回到本页
                   to_page(currentPage);
               }
           });
       }
   });

2)EmployeeController处理ajax请求,根据id删除员工数据:

public class EmployeeController {

    @Autowired
    EmployeeService employeeService;

    // 处理ajax请求,按照id去删除员工数据
    @RequestMapping(value = "/emp/{id}",method = RequestMethod.DELETE)
    @ResponseBody
    public Msg deleteEmpById(@PathVariable("id") Integer id){

        employeeService.deleteEmp(id);
        return Msg.success();
    }
    ......
}

2、批量删除

1)给表头添加复选框 id=“check_all”

<%--表头放在thead中--%>
 <thead>
      <tr>
          <th>
              <input type="checkbox" id="check_all"/>
          </th>
          <th>#</th>
          <th>empName</th>
          <th>gender</th>
          <th>email</th>
          <th>deptName</th>
          <th>操作</th>
      </tr>
  </thead>

2)每行数据添加复选框class=‘check_item’,后添加到tr标签中

	var checkBoxTd = $("<td><input type='checkbox' class='check_item'/></td>")
	var xxx
	...
...
	...
	$("<tr></tr>").append(checkBoxTd)
				.append(xxx)
				...       

3)完成全选/全不选功能

给表头的复选框绑定单击事件,使员工的复选框与表头一致:

    $("#check_all").click(function (){
        //当前复选框的是否选中的状态,attr获取checked是undefined;
        //   dom原生的属性使用prop来修改和获取,attr获取自定义属性的值
        //alert($(this).prop("checked"));  选中是true,不选中是flase
        // 设置每个员工的复选框的值与表头的一致
        $(".check_item").prop("checked",$(this).prop("checked"));
    });

当全选中员工复选框时,表头的复选框也要保存一致:

绑定单击事件,因为是后面创建的,需要使用on进行绑定
	//当每页的员工都被选中,表头的复选框也要被选中
    $(document).on("click",".check_item",function(){
        //判断当前选中的元素是否是此页的全部员工个数
        var flag=$(".check_item:checked").length == $(".check_item").length;
        $("#check_all").prop("checked",flag);
    });

4)给批量删除的按钮绑定单击事件:

注:

① 找到每个被选中的复选框,this代表当前遍历的被选中的复选框

② 在遍历过程中组装员工名,用于弹出提示删除窗口;组装员工id用于根据id批量删除

③ 确认删除则发送ajax请求
   //点击全部删除,就批量删除,绑定单击事件
   $("#emp_delete_all_btn").click(function (){
       var empNames="";
       var del_idstr="";
       //找到每个被选中的复选框,this代表当前遍历的被选中的复选框
       $.each($(".check_item:checked"),function(){
           // 当前复选框的祖先tr的第三个td里的内容就是当前行的员工姓名
           empNames += $(this).parents("tr").find("td:eq(2)").text()+",";
           //组装员工id字符串
           del_idstr+=$(this).parents("tr").find("td:eq(1)").text()+"-";
       });
       //取出  empNames多余的 ,   id字符串多余的 -
       empNames=empNames.substring(0,empNames.length-1);
       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);
               }
           });
       }
   });

5)EmployeeController中修改上面的单个删除,使其也可以批量删除:

@Controller
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;

    // 处理ajax请求,按照单个/批量删除员工数据
    /*  批量删除:1-2-3
        单个删除:1
    * */
    @RequestMapping(value = "/emp/{ids}",method = RequestMethod.DELETE)
    @ResponseBody
    public Msg deleteEmpById(@PathVariable("ids") String ids){
        //判断是否是批量删除
        if(ids.contains("-")){
            //批量删除
            List<Integer> del_ids = new ArrayList<>();
            String[] str_ids = ids.split("-");
            //组装id的集合,遍历转换成Integer类型后添加到list集合中
            for (String string:str_ids){
                del_ids.add(Integer.parseInt(string));
            }
            employeeService.deleteBatch(del_ids);
        }else {
            //单个删除
            Integer id=Integer.parseInt(ids);
            employeeService.deleteEmp(id);
        }
        return Msg.success();
    }
    ......
}

Service层的EmployeeService类中的单个删除、批量删除的方法:

@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;

    ......

    //员工单个删除
    public void deleteEmp(Integer id) {
        employeeMapper.deleteByPrimaryKey(id);
    }

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

七、总结

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值