SSM整合
阅读原文时间:2021年06月22日阅读:1

SSM整合

功能点

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

技术点

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

基础环境搭建

在eclipse中创建maven工程

创建完成,如下

由于缺少web.xml配置文件,补上即可

点击项目名--》鼠标右键--》Properties--》Project facets

创建成功,目录结构

引入相关jar包

  • spring

  • springmvc ?

  • mybatis ?

  • 数据库连接池,驱动包

  • 其他(jstl,servlet-api,junit)

    http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    4.0.0
    com.atguigu
    ssm-crud
    0.0.1-SNAPSHOT
    war

    <!--引入项目依赖的jar包 -->
    <!-- SpringMVC、Spring -->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependencies>
    
    
    
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework&lt;/groupId&gt;
        &lt;artifactId&gt;spring-webmvc&lt;/artifactId&gt;
        &lt;version&gt;4.3.7.RELEASE&lt;/version&gt;
    &lt;/dependency&gt;
    
    &lt;!-- 返回json字符串的支持 --&gt;
    &lt;!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;com.fasterxml.jackson.core&lt;/groupId&gt;
        &lt;artifactId&gt;jackson-databind&lt;/artifactId&gt;
        &lt;version&gt;2.8.8&lt;/version&gt;
    &lt;/dependency&gt;
    
    &lt;!--JSR303数据校验支持;tomcat7及以上的服务器, 
    tomcat7以下的服务器:el表达式。额外给服务器的lib包中替换新的标准的el
    --&gt;
    &lt;!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.hibernate&lt;/groupId&gt;
        &lt;artifactId&gt;hibernate-validator&lt;/artifactId&gt;
        &lt;version&gt;5.4.1.Final&lt;/version&gt;
    &lt;/dependency&gt;
    
    
    &lt;!-- Spring-Jdbc --&gt;
    &lt;!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework&lt;/groupId&gt;
        &lt;artifactId&gt;spring-jdbc&lt;/artifactId&gt;
        &lt;version&gt;4.3.7.RELEASE&lt;/version&gt;
    &lt;/dependency&gt;
    
    &lt;!--Spring-test --&gt;
    &lt;!-- https://mvnrepository.com/artifact/org.springframework/spring-test --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework&lt;/groupId&gt;
        &lt;artifactId&gt;spring-test&lt;/artifactId&gt;
        &lt;version&gt;4.3.7.RELEASE&lt;/version&gt;
    &lt;/dependency&gt;
    
    
    &lt;!-- Spring面向切面编程 --&gt;
    &lt;!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework&lt;/groupId&gt;
        &lt;artifactId&gt;spring-aspects&lt;/artifactId&gt;
        &lt;version&gt;4.3.7.RELEASE&lt;/version&gt;
    &lt;/dependency&gt;
    
    &lt;!--MyBatis --&gt;
    &lt;!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.mybatis&lt;/groupId&gt;
        &lt;artifactId&gt;mybatis&lt;/artifactId&gt;
        &lt;version&gt;3.4.2&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;!-- MyBatis整合Spring的适配包 --&gt;
    &lt;!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.mybatis&lt;/groupId&gt;
        &lt;artifactId&gt;mybatis-spring&lt;/artifactId&gt;
        &lt;version&gt;1.3.1&lt;/version&gt;
    &lt;/dependency&gt;
    
    &lt;!-- 数据库连接池、驱动 --&gt;
    &lt;!-- https://mvnrepository.com/artifact/c3p0/c3p0 --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;c3p0&lt;/groupId&gt;
        &lt;artifactId&gt;c3p0&lt;/artifactId&gt;
        &lt;version&gt;0.9.1&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;mysql&lt;/groupId&gt;
        &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
        &lt;version&gt;5.1.41&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;!-- (jstl,servlet-api,junit) --&gt;
    &lt;!-- https://mvnrepository.com/artifact/jstl/jstl --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;jstl&lt;/groupId&gt;
        &lt;artifactId&gt;jstl&lt;/artifactId&gt;
        &lt;version&gt;1.2&lt;/version&gt;
    &lt;/dependency&gt;
    
    &lt;!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;javax.servlet&lt;/groupId&gt;
        &lt;artifactId&gt;javax.servlet-api&lt;/artifactId&gt;
        &lt;version&gt;3.0.1&lt;/version&gt;
        &lt;scope&gt;provided&lt;/scope&gt;
    &lt;/dependency&gt;
    
    
    &lt;!-- junit --&gt;
    &lt;!-- https://mvnrepository.com/artifact/junit/junit --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;junit&lt;/groupId&gt;
        &lt;artifactId&gt;junit&lt;/artifactId&gt;
        &lt;version&gt;4.12&lt;/version&gt;
    &lt;/dependency&gt;
    
    &lt;!--引入pageHelper分页插件 --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;com.github.pagehelper&lt;/groupId&gt;
        &lt;artifactId&gt;pagehelper&lt;/artifactId&gt;
        &lt;version&gt;5.0.0&lt;/version&gt;
    &lt;/dependency&gt;
    
    &lt;!-- MBG --&gt;
    &lt;!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.mybatis.generator&lt;/groupId&gt;
        &lt;artifactId&gt;mybatis-generator-core&lt;/artifactId&gt;
        &lt;version&gt;1.3.5&lt;/version&gt;
    &lt;/dependency&gt;
    </dependencies>

引入Bootstrap前端框架

官网:https://v3.bootcss.com/

在项目中引入bootstrap方法

方法一:直接使用这些 BootstrapCDN 提供的链接即可。缺点:需要联网

<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">

<!-- 可选的 Bootstrap 主题文件(一般不用引入) -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap-theme.min.css" integrity="sha384-6pzBo3FDv/PJ8r2KRkGHifhEocL+1X2rVCTTkUfGk7/0pbek5mMa1upzvWbrUbOZ" crossorigin="anonymous">

<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>

方法二:

把下载好的bootstrap压缩包,解压,放在项目中,另外还需要加入jQuery的支持(自行下载)

示例

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet"
    href="static/bootstrap-3.4.1-dist/css/bootstrap.min.css">
<title>Insert title here</title>
</head>
<body>
 <jsp:forward page="/emps"/> 


    <script src="static/js/jquery-1.12.4.min.js"></script>
    <!--jquery的js引入文件一定要放在bootstrap的js引入文件之前  -->
    <script src="static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>
</body>
</html>

搭建ssm基本环境

需要编写web.xml, spring, springmvc, mybatis等相关配置文件

web.xml文件解析

1,启动Spring的容器

    <!--1、启动Spring的容器 -->
    <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>

2,springmvc的前端控制器,拦截所有请求

? 方法一:在<param-value>location</param-value>中配置springmvc.xml文件路径,

        <!--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>location</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

? 方法二:去掉以及其子标签,但是需要在web.xml的同级目录下配置springmvc配置文件:xxx-servlet.xml,此文件命名有要求,必须是servlet-name中的值-servlet,例如:dispatcherServlet-servlet.xml

        <!--2、springmvc的前端控制器,拦截所有请求  -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

3、字符编码过滤器,一定要放在所有过滤器之前

    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

4、使用Rest风格的URI,将页面普通的post请求转为指定的delete或者put请求

    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <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>

web.xml最终版

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">

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

    <!--2、springmvc的前端控制器,拦截所有请求  -->
    <!-- The front controller of this Spring Web application, responsible for handling all application requests -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- Map all requests to the DispatcherServlet for handling -->
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- 3、字符编码过滤器,一定要放在所有过滤器之前 -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 4、使用Rest风格的URI,将页面普通的post请求转为指定的delete或者put请求 -->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <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>


</web-app>

springmvc配置文件:dispatcherServlet-servlet.xml

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

    <!--SpringMVC的配置文件,包含网站跳转逻辑的控制,配置 -->
    <context:component-scan
        base-package="com.atguigu" use-default-filters="false">
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>
    <!--配置视图解析器 -->
    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!-- 能支持springmvc更高级的一些功能,JSR303校验,快捷的ajax...映射动态请求 -->
    <mvc:annotation-driven></mvc:annotation-driven>
    <!-- 将springmvc不能处理的请求交给tomcat ,主要处理静态资源,例如jsp,html,js等 -->
    <mvc:default-servlet-handler />

</beans>

在项目中创建相应的package

spring配置文件:applicationContext.xml

Spring配置文件的核心点(数据源、与mybatis的整合,事务控制)

<?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"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
        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-4.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

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

<!-- Spring的配置文件,这里主要配置和业务逻辑有关的 -->
    <!--=================== 数据源,事务控制,xxx ================-->
    <context:property-placeholder location="classpath:dbconfig.properties"/>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

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

    <!-- 配置一个可以执行批量的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>
    <!--=============================================  -->

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

    <!-- Spring配置文件的核心点(数据源、与mybatis的整合,事务控制) -->

</beans>

mybatis配置文件:mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--驼峰命名-->
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true" />
    </settings>

    <!--包的别名-->
    <typeAliases>
        <package name="com.atguigu.bean" />
    </typeAliases>
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
    <!-- 分页合理化,若要查询的页码小于0,则只会查第一页;若若要查询的页码大于最后一页,则只会查询最后一页 -->
        <property name="reasonable" value="true"/>
    </plugin>
</plugins>
</configuration>

mybatis逆向工程:

  1. 创建数据表

    部门表:tbl_dept

    DROP TABLE IF EXISTS `tbl_dept`;
    CREATE TABLE `tbl_dept`  (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `dept_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

    员工表:tbl_employee

    DROP TABLE IF EXISTS `tbl_employee`;
    CREATE TABLE `tbl_employee`  (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `last_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      `gender` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      `email` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      `d_id` int(11) NULL DEFAULT NULL,
      `empStatus` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      PRIMARY KEY (`id`) USING BTREE,
      INDEX `fk_emp_dept`(`d_id`) USING BTREE,
      CONSTRAINT `fk_emp_dept` FOREIGN KEY (`d_id`) REFERENCES `tbl_dept` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
    ) 
  2. 导入mybatis generator依赖

            <dependency>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-core</artifactId>
                <version>1.3.7</version>
            </dependency>
  3. mybatis generator配置文件

    <?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>
    &lt;context id="DB2Tables" targetRuntime="MyBatis3"&gt;
    <!-- 取消注释 --> <commentGenerator> <property name="suppressAllComments" value="true" /> </commentGenerator> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true" userId="root" password="123123"> </jdbcConnection>
        &lt;javaTypeResolver&gt;
            &lt;property name="forceBigDecimals" value="false" /&gt;
        &lt;/javaTypeResolver&gt;
    <!-- Bean类 --> <javaModelGenerator targetPackage="com.atguigu.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> <!-- Mapper接口 --> <javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.dao" targetProject="./src/main/java/"> <property name="enableSubPackages" value="true" /> </javaClientGenerator> <!-- 关联数据库表 --> <table tableName="tbl_dept" domainObjectName="Department" /> <table tableName="tbl_employee" domainObjectName="Employee"></table>
    &lt;/context&gt;
    </generatorConfiguration>
  4. 运行

        @Test
        public void test01() 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);
        }

  5. 此时的Mapper文件中只能做一些简单的增删改查,若想用复杂的连表查询,需要在对应的Mapper文件中自定义查询语句

    需求:查询员工的时候,可以一并查询查对应的员工部门

    在EmployeeBean中加入Department属性,并添加对应的get,set方法和构造器

    package com.atguigu.bean;
    
    public class Employee {
        private Integer id;
    private String lastName;
    
    private String gender;
    
    private String email;
    
    private Integer dId;
    
    private String empstatus;
    
    private Department department;
    
    public Department getDepartment() {
        return department;
    }
    
    public void setDepartment(Department department) {
        this.department = department;
    }
    
    public Integer getId() {
        return id;
    }
    
    public void setId(Integer id) {
        this.id = id;
    }
    
    public String getLastName() {
        return lastName;
    }
    
    public void setLastName(String lastName) {
        this.lastName = lastName == null ? null : lastName.trim();
    }
    
    public String getGender() {
        return gender;
    }
    
    public void setGender(String gender) {
        this.gender = gender == null ? null : gender.trim();
    }
    
    public Employee() {
        super();
    }
    
    public Employee(Integer id, String lastName, String gender, String email, Integer dId, String empstatus,
            Department department) {
        super();
        this.id = id;
        this.lastName = lastName;
        this.gender = gender;
        this.email = email;
        this.dId = dId;
        this.empstatus = empstatus;
        this.department = department;
    }
    
    public Employee(Integer id, String lastName, String gender, String email, Integer dId, String empstatus) {
        super();
        this.id = id;
        this.lastName = lastName;
        this.gender = gender;
        this.email = email;
        this.dId = dId;
        this.empstatus = empstatus;
    }
    
    public String getEmail() {
        return email;
    }
    
    public void setEmail(String email) {
        this.email = email == null ? null : email.trim();
    }
    
    public Integer getdId() {
        return dId;
    }
    
    public void setdId(Integer dId) {
        this.dId = dId;
    }
    
    public String getEmpstatus() {
        return empstatus;
    }
    
    public void setEmpstatus(String empstatus) {
        this.empstatus = empstatus == null ? null : empstatus.trim();
    }
    
    @Override
    public String toString() {
        return "Employee [id=" + id + ", lastName=" + lastName + ", gender=" + gender + ", email=" + email + ", dId="
                + dId + ", empstatus=" + empstatus + ", department=" + department + "]";
    }
    }

    在对应的Mapper接口中添加

    //查询有Department信息
        List<Employee> selectByExampleWithDept(EmployeeExample example);
    
    //根据主键查询有Department信息
        Employee selectByPrimaryKeyWithDept(Integer id);

    EmployeeMapper.java完整结构如下

    package com.atguigu.dao;
    
    import java.util.List;
    
    import org.apache.ibatis.annotations.Param;
    
    import com.atguigu.bean.Employee;
    import com.atguigu.bean.EmployeeExample;
    
    public interface EmployeeMapper {
        long countByExample(EmployeeExample example);
    int deleteByExample(EmployeeExample example);
    
    int deleteByPrimaryKey(Integer id);
    
    int insert(Employee record);
    
    int insertSelective(Employee record);
    
    List&lt;Employee&gt; selectByExample(EmployeeExample example);
    
    Employee selectByPrimaryKey(Integer id);
    //查询有Department信息 List<Employee> selectByExampleWithDept(EmployeeExample example); //根据主键查询有Department信息 Employee selectByPrimaryKeyWithDept(Integer id);
    int updateByExampleSelective(@Param("record") Employee record, @Param("example") EmployeeExample example);
    
    int updateByExample(@Param("record") Employee record, @Param("example") EmployeeExample example);
    
    int updateByPrimaryKeySelective(Employee record);
    
    int updateByPrimaryKey(Employee record);
    }

    在对应的Mapper文件中添加SQL语句

    EmployeeMappper.xml

     <!-- 新增 --> 
      <resultMap id="WithDeptResultMap" type="com.atguigu.bean.Employee">
        <id column="id" jdbcType="INTEGER" property="id" />
        <result column="last_name" jdbcType="VARCHAR" property="lastName" />
        <result column="gender" jdbcType="CHAR" property="gender" />
        <result column="email" jdbcType="VARCHAR" property="email" />
        <result column="d_id" jdbcType="INTEGER" property="dId" />
        <result column="empStatus" jdbcType="VARCHAR" property="empstatus" />
        <association property="department" javaType="com.atguigu.bean.Department">
            <id column="id" jdbcType="INTEGER" property="id" />
            <result column="dept_name" jdbcType="VARCHAR" property="deptName"/>
        </association>
      </resultMap>  
      <!--新增   -->
      <sql id="WithDept_Column_List">
        e.id, e.last_name, e.gender, e.email, e.d_id, e.empStatus,d.id, d.dept_name
      </sql>
    
      <!--List<Employee> selectByExampleWithDept(EmployeeExample example);  -->
      <select id="selectByExampleWithDept" parameterType="com.atguigu.bean.EmployeeExample" resultMap="WithDeptResultMap">
        select
        <if test="distinct">
          distinct
        </if>
        <include refid="WithDept_Column_List" />
        from tbl_employee e left join tbl_dept on e.d_id = d.id
        <if test="_parameter != null">
          <include refid="Example_Where_Clause" />
        </if>
        <if test="orderByClause != null">
          order by ${orderByClause}
        </if>
      </select>
      <!--Employee selectByPrimaryKeyWithDept(Integer id);  -->
      <select id="selectByPrimaryKeyWithDept" parameterType="java.lang.Integer" resultMap="WithDeptResultMap">
        select 
        <include refid="WithDept_Column_List" />
        from tbl_employee e left join tbl_dept on e.d_id = d.id
        where e.id = #{id,jdbcType=INTEGER}
      </select>
  6. 测试:

        @Test
        public void test01() {
            ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
                    "applicationContext.xml");
            EmployeeMapper mapper = applicationContext.getBean(EmployeeMapper.class);
            long countByExample = mapper.countByExample(null);
            System.out.println(countByExample);
        }

    测试结果:

    推荐Spring的项目就可以使用Spring的单元测试,可以自动注入我们需要的组件

    1、导入SpringTest模块

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.6</version>
            <scope>test</scope>
        </dependency>

    2、@ContextConfiguration指定Spring配置文件的位置

    3、@RunWith(SpringJUnit4ClassRunner.class): 指定用SpringJunit的运行环境

    4、直接autowired要使用的组件即可

    测试类如下

    import java.util.List;
    import java.util.UUID;
    
    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import com.atguigu.bean.Employee;
    import com.atguigu.dao.EmployeeMapper;
    import com.github.pagehelper.PageHelper;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "classpath:applicationContext.xml" })
    public class MapperTest {
        @Autowired
        EmployeeMapper employeeMapper;
        @Autowired
        SqlSession sqlSession;
    @Test
    public void test01() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        EmployeeMapper mapper = applicationContext.getBean(EmployeeMapper.class);
        long countByExample = mapper.countByExample(null);
        System.out.println("总记录数:" + countByExample);
    }
    
    // 插入
    @Test
    public void test02() {
        employeeMapper.insertSelective(new Employee(null, "ADBCEE", "0", "126@qq.com", 2, "300"));
    
    }
    
    // 批量插入
    @Test
    public void test04() {
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        String gender = null;
        String last_name = null;
        int dId = 1;
        for (int i = 1; i &lt;= 100; i++) {
            if (i % 2 == 0) {
                gender = "0";
            } else
                gender = "1";
            last_name = UUID.randomUUID().toString().substring(0, 5);
            dId = (int) (Math.random() * 3) + 1;
            mapper.insertSelective(new Employee(null, last_name, gender, last_name + "126@qq.com", dId, "200"));
        }
        System.out.println("批量插入完成");
    }
    
    // 测试selectByExampleWithDept()
    @Test
    public void test05() {
        PageHelper.startPage(1, 5);
        List&lt;Employee&gt; list = employeeMapper.selectByExampleWithDept(null);
        for (Employee employee : list) {
            System.out.println(employee);
        }
    }
    
    // 测试selectByPrimaryKeyWithDept()
    @Test
    public void test06() {
        Employee employee = employeeMapper.selectByPrimaryKeyWithDept(1);
        System.out.println(employee);
    }
    }

    注意:

    做批量操作时,一定要配置Spring配置文件中(applicationContext.xml)配置SqlSessionTemplate对象

        <!-- 配置一个可以执行批量的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>

开始编写CRUD

1.编写EmployeeController

在编写Controller之前,可以配置pageHelp插件

  • ? 引入pageHelp依赖

            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper</artifactId>
                <version>5.2.0</version>
            </dependency>
  • 在mybatis-config.xml配置pageHelp过滤器

    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!-- 分页合理化,若要查询的页码小于0,则只会查第一页;若若要查询的页码大于最后一页,则只会查询最后一页 -->
            <property name="reasonable" value="true"/>
        </plugin>
    </plugins>

配置Controller

@Controller
public class EmployeeController {
    @Autowired
    EmployeeService employeeService;

    @RequestMapping("/emps")
    public String list(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum, Model model) {
        PageHelper.startPage(1, 5);
        List<Employee> list = employeeService.getAll();
        // 使用pageInfo包装查询后的结果,只需要将pageInfo交给页面就行了。
        // 封装了详细的分页信息,包括有我们查询出来的数据,传入连续显示的页数
        PageInfo page = new PageInfo(list, 5);
        for (Employee employee : list) {
            System.out.println(employee);
        }
        model.addAttribute("pageInfo", list);
        return "list";
    }
}

配置Service层

@Service
public class EmployeeService {
    @Autowired
    EmployeeMapper employeeMapper;
    public List<Employee> getAll() {
        List<Employee> list = employeeMapper.selectByExampleWithDept(null);
        return list;
    }
}

2.Spring测试模块

使用Spring测试模块提供的测试请求功能,测试curd请求的正确性,Spring4测试的时候,需要servlet3.0的支持

主要作用:不用启动tomcat服务器测试业务逻辑

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml",
        "file:src/main/webapp/WEB-INF/dispatcherServlet-servlet.xml" })
@WebAppConfiguration
public class MvcTest {

    // 传入Springmvc的ioc,依赖@webAppConfiguration注解
    @Autowired
    WebApplicationContext context;
    // 虚拟mvc请求,获取到处理结果。
    MockMvc mockMvc;

    @Before
    public void initMockMvc() {
        mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
    }

    @Test
    public void testPage() throws Exception {
        ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get("/emps").param("pageNum", "1"));
        //模拟请求拿到返回值
        MvcResult result = resultActions.andReturn();

        //请求成功以后,请求域中会有pageInfo;我们可以取出pageInfo进行验证
        MockHttpServletRequest request = result.getRequest();
        PageInfo pi = (PageInfo) request.getAttribute("pageInfo");
        System.out.println("当前页码:" + pi.getPageNum());
        System.out.println("总页码:" + pi.getPages());
        System.out.println("总记录数:" + pi.getTotal());
        System.out.println("在页面需要连续显示的页码");
        int[] nums = pi.getNavigatepageNums();
        for (int i : nums) {
            System.out.print(" " + i);
        }

        // 获取员工数据
        List<Employee> list = pi.getList();
        for (Employee employee : list) {
            System.out.println(employee);
        }
    }

}

测试结果

3.编写一个index.jsp

引入bootstrap

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<%
    pageContext.setAttribute("basePath", request.getContextPath());
%>

<link rel="stylesheet"
    href="${basePath }/static/bootstrap-3.4.1-dist/css/bootstrap.min.css">
<title>index</title>

<script src="${basePath }/static/js/jquery-1.12.4.min.js"></script>
<script
    src="${basePath }/static/bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>
</head>
<body>
    <div class="container">
        <!-- 页头 -->
        <div class="row">
            <div class="page-header">
                <h1>
                    Example page header <small>Subtext for header</small>
                </h1>
            </div>
        </div>
        <!-- 按钮 -->
        <div class="row">
            <div class="col-md-2 col-md-offset-10">
                <button class="btn btn-primary" id="emp_add_modal_btn">增加</button>
                <button class="btn btn-danger" id="emp_delete_modal_btn">删除</button>
            </div>
        </div>
        <!-- 表格 -->
        <div class="row">
            <div class="col-md-12">
                <table class="table table-hover" id="emps_table">
                    <!--表头  -->
                    <thead>
                        <tr>
                            <th><input type="checkbox" id="check_all"></th>
                            <th>empId</th>
                            <th>empName</th>
                            <th>gender</th>
                            <th>email</th>
                            <th>empstatus</th>
                            <th>deptName</th>
                            <th>操作</th>
                        </tr>
                    </thead>
                    <!--表体  -->
                    <tbody>

                    </tbody>
                </table>
            </div>
        </div>
        <!-- 分页 -->
        <div class="row">
            <div class="col-md-6" id="page_info_area"></div>
            <div class="col-md-6" id="page_nav_area"></div>
        </div>
    </div>
</body>
</html>

访问服务器返回数据,一般比较通用的方法:从后台返回一个字符串,而不是一个页面

重新编写Controller层

    @RequestMapping("/emps")
    @ResponseBody
    public PageInfo getEmpsWithJson(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum) {
        PageHelper.startPage(1, 5);
        List<Employee> list = employeeService.getAll();
        // 使用pageInfo包装查询后的结果,只需要将pageInfo交给页面就行了。
        // 封装了详细的分页信息,包括有我们查询出来的数据,传入连续显示的页数
        PageInfo page = new PageInfo(list, 5);
        return page;
    }

还需要添加能解析和封装json字符串的工具,比如

<!-- json插件 -->        
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.3</version>
        </dependency>

在用户栏中输入访问地址:http://localhost:8080/ssm/emps,结果会返回一个json字符串,如下

但是上面返回的字符串没有通用姓,客户不知道返回的数据是否是想要的还是不想要的,也不知道正确与否

我们需要编写一个通用的类,封装好json字符串

编写Msg类:

package com.atguigu.bean;

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

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

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

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

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

    public Msg add(String key, Object value) {
        this.getExtend().put(key, value);
        return this;
    }

    public Msg(int code, String msg, Map<String, Object> extend) {
        super();
        this.code = code;
        this.msg = msg;
        this.extend = extend;
    }

    public Msg() {
        super();
    }

    public int getCode() {
        return code;
    }

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

    public String getMsg() {
        return msg;
    }

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

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

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

}

再次修改Controller方法

    // 导入jackson依赖
    // 返回json字符串
    @RequestMapping("/emps")
    @ResponseBody
    public Msg getEmpsWithJson(@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum) {
        PageHelper.startPage(1, 5);
        List<Employee> list = employeeService.getAll();
        // 使用pageInfo包装查询后的结果,只需要将pageInfo交给页面就行了。 // 封装了详细的分页信息,包括有我们查询出来的数据,传入连续显示的页数
        PageInfo page = new PageInfo(list, 5);
        return Msg.success().add("pageInfo", page);
    }

测试:在用户栏中输入访问地址:http://localhost:8080/ssm/emps,结果会返回一个json字符串,如下

查询

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

现在可以把返回的json字符串进行Dom的拼装,然后显示在页面上,关键是写js代码,过程很繁琐,需要有点耐心

步骤1:

思路:当访问首页的时候,自动显示数据(即json字符串拼装Dom,放在页面的相应位置)

? 页面布局:主要分为四个模块:模态框,表格数据,分页,分页导航条

下列方法介绍:

to_page(pageNum):跳转到第pageNum页

build_emps_table(result) :显示表格中的数据

function build_page_info(result):显示分页数据

build_page_nav(result):显示分页导航条数据,点击能去到相应的页面

<script type="text/javascript">
    //默认调转到第一页
    var currentPage;
    $(function() {
        to_page(1);
    });
    //跳转页面
    function to_page(pageNum) {
        $.ajax({
            url : "${basePath}/emps",
            data : "pageNum=" + pageNum,
            type : "GET",
            success : function(result) {
                //console.log(result);
                //1、解析并显示员工数据
                build_emps_table(result);
                //2、解析并显示分页信息
                build_page_info(result);
                //3、解析显示分页条数据
                build_page_nav(result);
            }
        });
    }
    //1、解析并显示员工数据
    function build_emps_table(result) {
        console.log(result);
        //获取数据前,需要先清空原先的数据
        $("#emps_table tbody").empty();
        var emps = result.extend.pageInfo.list;
        $.each(emps,function(index,item){
            var checkBoxTd = $("<td><input type=‘checkbox‘ class=‘check_item‘/></td>");
            var empId = $("<td></td>").append(item.id);
            var empName = $("<td></td>").append(item.lastName);
            var gender = $("<td></td>").append(item.gender == "0" ? "男" : "女");
            var email = $("<td></td>").append(item.email);
            var empstatus = $("<td></td>").append(item.empstatus);
            var deptName = $("<td></td>").append(item.department.deptName);
    //      <button type="button" class="btn btn-primary"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> 修改</button>
            var edit_btn = $("<button></button>").addClass("btn btn-primary btn-sm ").append($("<span></span>").addClass("glyphicon glyphicon-pencil")).append("编辑");
            var delete_btn = $("<button></button>").addClass("btn btn-danger btn-sm ").append($("<span></span>").addClass("glyphicon glyphicon-trash")).append("删除");
            var btnTd = $("<td></td>").append(edit_btn).append(" ").append(delete_btn);
            $("<tr></tr>").append(checkBoxTd).append(empId).append(empName).append(gender).append(email).append(empstatus).append(deptName).append(edit_btn)
            .append(delete_btn).append(btnTd).appendTo("#emps_table tbody");
        });

    }
    //2、解析并显示分页信息
    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+"条记录");
        currentPage = result.extend.pageInfo.pageNum;
    }
    //3、解析显示分页条数据,点击能去到相应的页面
    function build_page_nav(result) {
        //获取数据前,需要先清空原先的数据
        $("#page_nav_area").empty();
        var nav = $("<nav aria-label=‘Page navigation‘></nav>");
        var ul = $("<ul></ul>").addClass("pagination");
        var firstPageLi = $("<li></li>").append($("<a></a>").attr({"href":"#"})
                .append($("<span></span>").attr("aria-hidden","true").append("首页")));
        var prePageLi = $("<li></li>").append($("<a></a>").attr({"href":"#","aria-label":"true"})
                .append($("<span></span>").attr("aria-hidden","true").append("&laquo;")));
        if(!result.extend.pageInfo.hasPreviousPage){
            firstPageLi.addClass("disabled");
            prePageLi.addClass("disabled");
        }else{
            firstPageLi.click(function(){
                to_page(1);
            });
            prePageLi.click(function(){
                to_page(result.extend.pageInfo.pageNum - 1);
            });
        }

        //添加首页和前一页 的提示
        ul.append(firstPageLi).append(prePageLi);

        $.each(result.extend.pageInfo.navigatepageNums,function(index,item){
            var li = $("<li></li>").append($("<a></a>").attr("href","#").append(item));
            //如果等于当前页,添加样式
            if(currentPage == item){
                li.addClass("active");
            }
            //给每个li绑定点击事件
            li.click(function(){
                to_page(item);
            });
            ul.append(li);
        });

        var nextPageLi = $("<li></li>").append($("<a></a>").attr({"href":"#","aria-label":"true"})
                .append($("<span></span>").attr("aria-hidden","true").append("&raquo;")));
        var lastPageLi = $("<li></li>").append($("<a></a>").attr({"href":"#"})
                .append($("<span></span>").attr("aria-hidden","true").append("末页")));
        if(!result.extend.pageInfo.hasNextPage){
            nextPageLi.addClass("disabled");
            lastPageLi.addClass("disabled");
        }else{
            nextPageLi.click(function(){
                to_page(result.extend.pageInfo.pageNum + 1);
            });
            lastPageLi.click(function(){
                to_page(result.extend.pageInfo.pages);
            });
        }
        //添加末页和下一页 的提示
        ul.append(nextPageLi).append(lastPageLi);
        nav.append(ul).appendTo("#page_nav_area");
    }
</script>

结果

注意:在分页导航中,点击页码跳转的时候,一定要注意页码的超出范围

在mybatis-config.xml配置文件中配置一下插件就非常安全了

<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
    <!-- 分页合理化,若要查询的页码小于0,则只会查第一页;若若要查询的页码大于最后一页,则只会查询最后一页 -->
        <property name="reasonable" value="true"/>
    </plugin>
</plugins>
  • 当前页码是第一页,如果在点击上一页,页面中显示的还是第一页(pageNum<=1,显示第一页)
  • 当前页码是末页,如果在点击下一页,页面中显示的还是末页(pageNum>=末页,显示最后一页)

添加

思路:

  1. 在index.jsp页面点击”新增”
  2. 弹出新增对话框
  3. 去数据库查询部门列表,显示在对话框中
  4. 用户输入数据,并进行校验 ? jquery前端校验,ajax用户名重复校验,重要数据(后端校验(JSR303),唯一约束);
  5. 完成保存

关键代码

    //为‘添加模态框‘中的保存按钮绑定单击事件
        //数据校验
            //姓名格式校验
            //email 校验
            //ajax校验用户名是否已经存在
        //通过ajax上传数据
            //后台JSR303校验
                //成功
                    //关闭模态框
                    //跳转到最后一页
                //失败
                    //提示错误消息
    $("#emp_save_btn").click(function() {
        //1.表单数据校验,主要验证用户名和email
        if(!validate_add_form()){
            return false;
        }  

        //2.ajax校验用户名,会在#emp_save_btn按钮添加属性"ajax-va"="error"或者"ajax-va"="success",由这个条件判断是否需要向服务器提交表单
        if($("#emp_save_btn").attr("ajax-va") == "error"){
            return false;
        }
        //3.以上条件均满足,可以给服务器发送表单数据
         $.ajax({
            url:"${basePath}/saveEmp",
            data:$("#empAddModal form").serialize(),
            type:"POST",
            success:function(result){
                //返回错误消息
                if(result.code == 200){
                    if(undefined != result.extend.errorFields.lastName){
                        show_validate_msg("#empName_add_input","error",result.extend.errorFields.lastName);
                    }
                    if(undefined != result.extend.errorFields.email){
                        show_validate_msg("#email_add_input","error",result.extend.errorFields.email);
                    }
                }else{
                    //返回正确消息
                    alert(result.msg);
                    to_page(totalRecord);
                    $("#empAddModal").modal("hide");
                }
            }
        }); 
    });

    //校验数据的合法性
    function validate_add_form() {
        //校验用户名
        var flag1 = checkName();
        //校验email
        var flag2 = checkEmail("#email_add_input")
        if(flag1 == true && flag2 == true ){
            return true;
        }else{
            return false;
        }
    }

    //输入框内容改变时,ajax校验
    $("#empName_add_input").change(function(){
        //校验用户名,满足正则才进行ajax进行验证
        if(checkName()){
            //用户名符合正则之后,ajax访问数据库校验,查看该用户名是否已经存在
            var empName = this.value;
            $.ajax({
                url:"${basePath}/checkUser1",
                data:"lastName="+empName,
                type:"POST",
                success:function(result){
                    var code = result.code;
                    //存在该用户名的记录,给模态框中的提交按钮添加自定义属性,此按钮在提交数据的时候可以判断是否满足提交条件
                    if(code == "200"){
                        $("#emp_save_btn").attr("ajax-va", "error");
                        show_validate_msg("#empName_add_input","error","用户名已存在!!!");
                        return false;
                    }else{
                    //不存在该用户名的记录,给模态框中的提交按钮添加自定义属性,此按钮在提交数据的时候可以判断是否满足提交条件
                        $("#emp_save_btn").attr("ajax-va", "success");
                        show_validate_msg("#empName_add_input","success","用户名可用!!!");
                        return true;
                    }
                }
            });

        }
    });
    //校验用户名
    function checkName(){
        //校验之前,清除先前的样式和数据
        var lastName = $("#empName_add_input").val();
        var regName = /(^[a-zA-Z0-9_-]{5,16}$)|(^[\u2E80-\u9FFF]{2,5}$)/;
        if(!regName.test(lastName)){
            show_validate_msg("#empName_add_input","error","用户名可以是2-5位中文或者5-16位英文和数字的组合");
            return false;
        }else{
            show_validate_msg("#empName_add_input","success","");
            return true;
        }
    }
    //校验email
    function checkEmail(ele){
        var email = $(ele).val();
        var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
        if(!regEmail.test(email)){
            show_validate_msg(ele,"error","邮箱格式不正确");
            return false;
        }else{
            $(ele).parent().addClass("has-success");
            show_validate_msg(ele,"success","");
            return true;
        } 
    }

    //显示校验结果的提示信息
    function show_validate_msg(ele, status, msg) {
        //校验之前,清除先前的样式和数据
        $(ele).parent().removeClass("has-success has-error");
        $(ele).next("span").text("");
        if("success" == status){
            $(ele).parent().addClass("has-success");
            $(ele).next("span").text(msg);
        }else{
            $(ele).parent().addClass("has-error");
            $(ele).next("span").text(msg);
        }
    }

Controller层关键代码

@RequestMapping(value = "/saveEmp", method = RequestMethod.POST)
    @ResponseBody
    public Msg saveEmp(@Valid Employee employee, BindingResult result) {
        if (result.hasErrors()) {
            // 校验失败,应该返回失败,在模态框中显示校验失败的错误信息
            List<FieldError> errors = result.getFieldErrors();
            Map<String, Object> map = new HashMap<String, Object>();

            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 {
            System.out.println(employee);
            employeeService.saveEmp(employee);
            return Msg.success();
        }
    }

Service层

    public void saveEmp(Employee employee) {
        employeeMapper.insertSelective(employee);
    }

    /**
     * 
     * @Description 检验用户名是否可用
     * @param lastName
     * @return true:可用 false:不可用
     */
    public boolean checkUser(String lastName) {
        EmployeeExample example = new EmployeeExample();
        Criteria criteria = example.createCriteria();
        criteria.andLastNameEqualTo(lastName);
        long count = employeeMapper.countByExample(example);
        return count == 0;
    }

JSR303校验

由于前端主要用js进行数据的校验,安全性不高,容易被别人禁用js而破坏数据,因此还需要后端校验

后端校验,这里用到了JSR303

使用JSR303插件步骤

1.引入依赖

<!-- JSR303数据校验 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.4.1.Final</version>
        </dependency>

2.在Bean类中添加@Pattern注解

public class Employee {
    private Integer id;

    @Pattern(regexp = "(^[a-zA-Z0-9_-]{5,16}$)|(^[\\u2E80-\\u9FFF]{2,5}$)", message = "用户名可以是2-5位中文或者5-16位英文和数字的组合")
    private String lastName;

    private String gender;

    /**
     * regexp:校验规则,一般是正则表达式,
     * message:校验不匹配时,返回的字符串内容
     */
    @Pattern(regexp = "^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$", message = "邮箱格式不正确")
    private String email;

3.在Controller层添加 @Valid注解

@RequestMapping(value = "/saveEmp", method = RequestMethod.POST)
    @ResponseBody
    public Msg saveEmp(@Valid Employee employee, BindingResult result) {
        if (result.hasErrors()) {
            // 校验失败,应该返回失败,在模态框中显示校验失败的错误信息
            List<FieldError> errors = result.getFieldErrors();
            Map<String, Object> map = new HashMap<String, Object>();

            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 {
            System.out.println(employee);
            employeeService.saveEmp(employee);
            return Msg.success();
        }
    }

修改

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

js关键代码

    //给编辑按钮绑定单击事件
    //此时的按钮是在ajax数据中返回后才渲染出来的,ajax数据是当前页面Dom文档加载完成后才加载的,
    //若此时直接给$(".edit_btn").click(funciotn(){});
    //事件无法生效,因为绑定的时候,对应的Dom按钮还没有生成,所以绑定事件的时候需要注意,此时需要用 ON函数绑定单击事件
    $(document).on("click",".edit_btn",function(){
        //1、查出部门信息,并显示部门列表
        getDepts("#empUpdateModal select");
        var id = $(this).attr("item_id");

        //2.查出员工信息,显示员工信息
        getEmp(id);
        //3、把员工的id传递给模态框的更新按钮
        $("#emp_update_btn").attr("item_id",id);

        $("#empUpdateModal").modal({
            backdrop:true
        });
    });

    //查出员工信息,显示员工信息
    function getEmp(id){
        $.ajax({
            url:"${basePath}/getEmpById/"+id,
            type:"GET",
            success:function(result){
                console.log(result);
                $("#empName_update_static").append(result.extend.emp.lastName);
                $("#email_update_input").val(result.extend.emp.email);
                $("#empUpdateModal input[name=gender]").val([result.extend.emp.gender]);
                $("#empstatus_update_input").val(result.extend.emp.empstatus);
                $("#depart_update_select").val([result.extend.emp.dId]);
            }
        });
    }

    //给模态框的更新按钮绑定单击事件
    $("#emp_update_btn").click(function(){
        //验证邮箱格式
        if(!checkEmail("#email_update_input")){
            return false;
        }
        var item_id = $(this).attr("item_id");
        $.ajax({
            url:"${basePath}/updateEmp/"+item_id,
//这种方法需要依赖HiddenHttpMethodFilter过滤器                    
            type:"POST",    
            data:$("#empUpdateModal form").serialize()+"&_method=PUT",

//下面这种方法需要依赖HttpPutFormContentFilter过滤器            
//            data:$("#empUpdateModal form").serialize(),
//            type:"PUT", 
            success:function(result){
                to_page(currentPage);
                //关闭模态框
                $("#empUpdateModal").modal(‘hide‘);
            }
        });
    });

注意:在数据更新的时候,需要注意post请求和put请求的细节,注意上方的注释

    <!-- 4、使用Rest风格的URI,将页面普通的post请求转为指定的delete或者put请求 -->
    <filter>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
<!-- 如果是put请求,会将请求体中的数据解析包装成一个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>

controller层

    /**
     * 如果直接发送ajax=PUT形式的请求
     * 封装的数据
     * Employee 
     * [empId=1014, empName=null, gender=null, email=null, dId=null]
     * 
     * 问题:
     * 请求体中有数据;
     * 但是Employee对象封装不上;
     * 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
     * org.apache.catalina.connector.Request--parseParameters() (3111);
     * 
     * protected String parseBodyMethods = "POST";
     * if( !getConnector().isParseBodyMethod(getMethod()) ) {
                success = true;
                return;
            }
     * 
     * 
     * 解决方案;
     * 我们要能支持直接发送PUT之类的请求还要封装请求体中的数据
     * 1、配置上HttpPutFormContentFilter;
     * 2、他的作用;将请求体中的数据解析包装成一个map。
     * 3、request被重新包装,request.getParameter()被重写,就会从自己封装的map中取数据
     * 员工更新方法
     * @param employee
     * @return
     */
/**
     * 
     * @Description 根据id查询Employee
     * @author CGQ
     * @date 2021年6月17日下午5:56:37
     * @param id
     * @return
     */
    @RequestMapping("/getEmpById/{id}")
    @ResponseBody
    public Msg getEmpById(@PathVariable("id") Integer id) {
        Employee employee = employeeService.getEmpById(id);
        return Msg.success().add("emp", employee);
    }

    /**
     * 根据Id更新员工
     * 
     * @Description
     * @author CGQ
     * @date 2021年6月17日下午10:42:12
     * @param employee
     * @return
     */
    @RequestMapping(value = "/updateEmp/{id}", method = RequestMethod.PUT)
    @ResponseBody
    public Msg updateEmp(Employee employee) {
        // System.out.println(id);
        System.out.println("将要更新的员工数据:" + employee);
        employeeService.updateEmp(employee);
        return Msg.success();
    }

service层

//根据id返回employee对象    
public Employee getEmpById(Integer id) {
        Employee employee = employeeMapper.selectByPrimaryKeyWithDept(id);

        return employee;
    }

//更新
    public void updateEmp(Employee employee) {
        employeeMapper.updateByPrimaryKeySelective(employee);
    }

删除

js关键代码

//删除员工    
    $(document).on("click",".delete_btn",function(){
        var id = $(this).attr("item_id");
        var lastName = $(this).parent().parent().find("td:eq(2)").text();
        if(confirm("确定要删除【"+lastName+"】吗?")){
            $.ajax({
                url:"${basePath}/deleteEmpById/"+id,
                type:"delete",
                success:function(result){
                    to_page(currentPage);
                }
            });
        }
    });

    $("#check_all").click(function(){
//用prop获取Dom的原生属性
//用attr获取自定义的Dom属性
        $(".check_item").prop("checked",$(this).prop("checked"))
    });

//给表格中的每一个checkBox绑定事件
    $(document).on("click",".check_item",function(){
        var flag = $(".check_item:checked").length == $(".check_item").length;
        $("#check_all").prop("checked",flag);
    });

//给删除按钮绑定事件,点击全部删除,就批量删除
    $("#emp_delete_modal_btn").click(function(){
        var empNames="";
        var ids="";
        //var items = $(".check_item:checked");
        $.each($(".check_item:checked"),function(index,item){
            //alert($(item).parent().parent("tr").find("td:eq(2)").text());
            empNames += $(item).parent().parent("tr").find("td:eq(2)").text()+",";
            ids += $(item).parent().parent("tr").find("td:eq(1)").text()+"-";
        });
        empNames = empNames.substring(0,empNames.length-1);
        ids = ids.substring(0,ids.length-1);
        //alert(empNames);
        //alert(ids);
        if(confirm("确定要删除【"+empNames+"】吗?")){
            $.ajax({
                url:"${basePath}/deletes/"+ids,
                type:"DELETE",
                success:function(result){
                    to_page(currentPage);
                }
            });
        }
    });

controller层

    /**
     * 根据id删除
     * 
     * @Description
     * @author CGQ
     * @date 2021年6月17日下午11:51:09
     * @param id
     * @return
     */
    @RequestMapping(value = "/deleteEmpById/{id}", method = RequestMethod.DELETE)
    @ResponseBody
    public Msg deleteEmpById(@PathVariable("id") Integer id) {
        employeeService.deleteEmpById(id);
        return Msg.success();
    }

    /**
     * 批量删除
     * 
     * @Description
     * @author CGQ
     * @date 2021年6月17日下午11:44:59
     * @return
     */
    @RequestMapping(value = "/deletes/{ids}", method = RequestMethod.DELETE)
    @ResponseBody
    public Msg deletes(@PathVariable("ids") String ids) {
        if (ids.contains("-")) {
            List<Integer> list = new ArrayList<>();
            String[] strings = ids.split("-");
            for (String string : strings) {
                list.add(Integer.parseInt(string));
            }
            employeeService.deleteBatch(list);
        } else {
            employeeService.deleteEmpById(Integer.parseInt(ids));
        }
        return Msg.success();
    }

service层

    public void deleteEmpById(Integer id) {
        employeeMapper.deleteByPrimaryKey(id);
    }

    public void deleteBatch(List<Integer> list) {
        EmployeeExample example = new EmployeeExample();
        Criteria criteria = example.createCriteria();
        criteria.andIdIn(list);
        employeeMapper.deleteByExample(example);

    }

总结