SSM-CRUD实战项目

1. 项目总览

SpringMVC + Spring + MyBatis

CRUD:增删改查

  • 功能:
  1. 分页

  2. 数据校验

    • jquery前端校验+JSR303后端校验
  3. ajax

  4. Rest风格的URI;使用HTTP协议请求方式的动词,来表示对资源的操作(GET查询, POST新增, PUT修改, DELETE删除)

  • 技术点:
  1. 基础框架-ssm
  2. 数据库-MySQL
  3. 前端框架-BootStrap:快速搭建简洁美观的界面
  4. 项目依赖管理——Maven
  5. 分页——pagehelper插件
  6. 逆向工程——MyBatis Generator(MBG)
  • 最终效果图

项目Git地址

2. SSM框架整合

  • Spring MVC 负责实现 MVC 设计模式

  • MyBatis 负责数据持久层

  • Spring负责管理SpringMVC和MyBatis所用到的相关对象的创建和依赖注入

    SSM框架整合,实际上是Spring与MyBatis的整合,因为SpringMVC是Spring的一个子模块

2.1 项目环境

  • IDEA 2021.2.3 Ultimate Edition
  • MySQL 8.0.26
  • Tomcat 10.0.11
  • Maven 3.8.3

2.2 基础环境搭建

  • 创建maven工程
  • 引入项目依赖的jar包
  • 引入bootstrap前端框架

2.3 配置文件*

  • 第一步:pom.xml 引入依赖jar包

        <dependencies>
    <!-- SpringMVC -->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.18.RELEASE</version>
    </dependency> <!-- Spring JDBC 事务控制 -->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.18.RELEASE</version>
    </dependency> <!-- Spring AOP 面向切面编程 -->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.2.18.RELEASE</version>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.18.RELEASE</version>
    </dependency> <!-- Spring TestContext 用于注解导入Spring全局配置文件-->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.18.RELEASE</version>
    </dependency> <!-- MyBatis -->
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
    </dependency> <!-- MBG MyBatis逆向工程 -->
    <dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.7</version>
    </dependency> <!-- MyBatis整合Spring的适配包 -->
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.1</version>
    </dependency> <!-- MySQL驱动 -->
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.26</version>
    </dependency> <!-- 数据库连接池 Druid -->
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.6</version>
    </dependency> <!-- PageHelper 分页插件 -->
    <dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.3.0</version>
    </dependency> <!-- JSTL:JSP标签库-->
    <dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
    </dependency> <!-- lombok -->
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
    <scope>provided</scope>
    </dependency> <!-- 日志 -->
    <!-- 注意log4j与slf4j的版本兼容问题 -->
    <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
    </dependency>
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.30</version>
    </dependency> <!-- Junit -->
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.1</version>
    <scope>compile</scope>
    </dependency> <!-- JSP -->
    <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2</version>
    <scope>provided</scope>
    </dependency> <!-- JSR303数据校验支持 -->
    <dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>7.0.1.Final</version>
    </dependency> <!-- servlet -->
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
    </dependency> <!-- Spring5和Thymeleaf整合包 -->
    <dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
    <version>3.0.12.RELEASE</version>
    </dependency> <!-- Jackson 处理Json数据 -->
    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.0</version>
    </dependency> </dependencies>
  • 第二步:WEB-INF文件夹下,web.xml 配置拦截器拦截浏览器请求

    • 启动spring容器,加载spring全局配置文件
    • 配置SpringMVC前端控制器DispatcherServlet,对浏览器发送的请求进行统一处理,指定springMVC配置文件的地址
    • 字符编码过滤器
    • 配置HiddenHttpMethodFilter,过滤请求方式
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">
    <!--1、启动 Spring 容器-->
    <!--
    配置ContextLoaderListener, 加载Spring父容器 (父类的initWebApplicationContext()方法中)
    可以从ServletContext中根据 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 这个key来找到Spring容器
    -->
    <context-param>
    <!-- 指定Spring配置文件位置 -->
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:springApplicationConfig.xml</param-value>
    </context-param>
    <!-- 该监听器将根据contextConfigLocation参数加载Spring配置文件, 初始化Spring应用上下文 -->
    <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener> <!-- 2、配置SpringMVC的前端控制器 DispatcherServlet,对浏览器发送的请求统一进行处理 -->
    <servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 -->
    <init-param>
    <!-- contextConfigLocation为固定值 -->
    <param-name>contextConfigLocation</param-name>
    <!-- 使用classpath:表示从类路径查找配置文件,例如maven工程中的src/main/resources -->
    <param-value>classpath:springMVC.xml</param-value>
    </init-param> <!--
    作为框架的核心组件,在启动过程中有大量的初始化操作要做
    而这些操作放在第一次请求时才执行会严重影响访问速度
    因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时
    -->
    <load-on-startup>1</load-on-startup>
    </servlet> <servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <!--
    设置springMVC的核心控制器所能处理的请求的请求路径
    / 所匹配的请求可以是/login或.html或.js或.css方式的请求路径
    但是 / 不能匹配.jsp请求路径的请求
    -->
    <url-pattern>/</url-pattern> <!-- 注意,只有一个 / -->
    </servlet-mapping> <!--3、配置springMVC的字符编码过滤器,防止乱码;字符编码过滤器,一定要放在所有过滤器前面-->
    <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>
    <!-- 注意这里设置必须两个参数为true,可查看源码 -->
    <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> <!-- /* 是包含所有请求,/ 是不包括.jsp的请求 -->
    </filter-mapping> <!-- 4、使用Rest风格的URI,将页面普通的post请求转换为指定的delete或者put请求 -->
    <!--配置HiddenHttpMethodFilter,过滤请求方式,将POST请求转换为PUT、DELETE请求-->
    <filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern> <!-- 过滤所有请求 -->
    </filter-mapping>
    <!-- FormContentFilter:将PUT、PATCH、DELETE请求体解析为Servlet请求参数 -->
    <!-- 因为ajax发送put请求时,Tomcat一看是PUT,就不会封装请求体中的数据为map -->
    <!-- 可以发PUT请求 -->
    <filter>
    <filter-name>FormContentFilter</filter-name>
    <filter-class>org.springframework.web.filter.FormContentFilter</filter-class>
    </filter>
    <filter-mapping>
    <filter-name>FormContentFilter</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- 加载静态资源的另一种方式 -->
    <!-- <servlet-mapping>-->
    <!-- <servlet-name>default</servlet-name>-->
    <!-- <url-pattern>*.js</url-pattern>-->
    <!-- </servlet-mapping>-->
    <!-- <servlet-mapping>-->
    <!-- <servlet-name>default</servlet-name>-->
    <!-- <url-pattern>*.css</url-pattern>-->
    <!-- </servlet-mapping>-->
    <!-- <servlet-mapping>-->
    <!-- <servlet-name>default</servlet-name>-->
    <!-- <url-pattern>*.jpg</url-pattern>-->
    <!-- </servlet-mapping>-->
    </web-app>
  • 第三步:resources资源目录下,springMVC.xml 配置SpringMVC

    • 开启扫描组件,扫描注解配置的类
    • 配置视图解析器
    • 开放对静态资源的访问
    • 开启mvc注解驱动
    <?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 https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- SpringMVC的配置文件,包含网站跳转逻辑的控制及配置 -->
    <!--开启扫描组件-->
    <context:component-scan base-package="com.atguigu.ssmcrud"></context:component-scan> <!-- &lt;!&ndash;配置thymeleaf视图解析器,方便页面返回&ndash;&gt;-->
    <!-- <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">-->
    <!-- <property name="order" value="1"/> &lt;!&ndash;优先级设置&ndash;&gt;-->
    <!-- <property name="characterEncoding" value="UTF-8"/>-->
    <!-- <property name="templateEngine"> &lt;!&ndash;模板&ndash;&gt;-->
    <!-- <bean class="org.thymeleaf.spring5.SpringTemplateEngine">-->
    <!-- <property name="templateResolver">-->
    <!-- <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">-->
    <!-- &lt;!&ndash; 视图前缀 &ndash;&gt;-->
    <!-- <property name="prefix" value="/WEB-INF/views/"/>-->
    <!-- &lt;!&ndash; 视图后缀 &ndash;&gt;-->
    <!-- <property name="suffix" value=".html"/>-->
    <!-- <property name="templateMode" value="HTML5"/>-->
    <!-- <property name="characterEncoding" value="UTF-8" />-->
    <!-- </bean>-->
    <!-- </property>-->
    <!-- </bean>-->
    <!-- </property>-->
    <!-- </bean>--> <!-- 配置视图解析器 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"/>
    <property name="suffix" value=".jsp"/>
    </bean> <!--配置视图控制器,建立请求与跳转页面的映射关系-->
    <!-- <mvc:view-controller path="/" view-name="emps"></mvc:view-controller>--> <!--开放对静态资源的访问,将springMVC不能处理的请求交给tomcat-->
    <mvc:default-servlet-handler/> <!--能支持springmvc更高级的一些功能,JSR303校验,快捷的ajax...映射动态请求-->
    <mvc:annotation-driven /> </beans>
  • 第四步:resources文件夹下,配置数据库连接和日志文件,dbconfig.properties和log4j.properties

    dbconfig.properties

    mysql.driver=com.mysql.cj.jdbc.Driver
    mysql.url=jdbc:mysql://localhost:3306/mybatisdb?allowMultiQueries=true
    mysql.username=root
    mysql.password=wenhao

    log4j.properties

    log4j.rootLogger=DEBUG,A1
    
    log4j.appender.A1=org.apache.log4j.ConsoleAppender
    log4j.appender.A1.layout=org.apache.log4j.PatternLayout
    log4j.appender.A1.layout.ConversionPattern=[%t] [%c]-[%p] %m%n
  • 第五步:resources文件夹下,springApplicationConfig.xml 全局配置文件,对Spring进行配置,与主要配置和业务逻辑有关

    • 开启扫描组件,这里无需扫描controller

    • 整合MyBatis

      1. 引入数据库配置文件 properties文件位置,classpath——>resources目录下
    1. 配置数据源(数据库连接池),可以使用c3p0或者Druid
    2. 配置Spring和MyBatis的整合:SqlSessionFactory,会指定MyBatis全局配置文件位置
    3. 配置sqlSession
    4. 配置扫描器,扫描自定义的mapper接口
    5. 配置一个可以进行批处理的sqlSession
    • 事务控制的配置(事务管理器)

      1. 建立与数据库连接池的映射
      2. 开启基于注解的事务
      3. 配置事务增强
    <?xml version="1.0" encoding="UTF-8"?>
    <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"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    https://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- spring的配置文件,这里主要配置和业务逻辑有关的 --> <!-- spring无需扫描controller,因为spring只进行组件扫描,对于控制层的处理则是交给springMVC处理 -->
    <context:component-scan base-package="com.atguigu.ssmcrud">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan> <!-- =======================整合MyBatis======================= -->
    <!-- 引入数据库的配置文件 -->
    <context:property-placeholder location="classpath:dbconfig.properties"/> <!-- 配置数据源,这里使用druid数据库连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="username" value="${mysql.username}"></property>
    <property name="password" value="${mysql.password}"></property>
    <property name="url" value="${mysql.url}"></property>
    <property name="driverClassName" value="${mysql.driver}"></property>
    <property name="initialSize" value="5"></property>
    <property name="maxActive" value="10"></property>
    </bean> <!-- 配置和MyBatis的整合:SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--指定MyBatis全局配置文件的位置-->
    <property name="configLocation" value="classpath:mybatisConfig.xml"></property>
    <!--建立与数据库连接池的映射关系-->
    <property name="dataSource" ref="dataSource"></property>
    <!--指定mybatis,mapper文件的位置-->
    <property name="mapperLocations" value="classpath:mapper/*.xml"></property>
    </bean> <!-- 配置扫描器,将MyBatis的Dao接口实现,并加入到ioc容器中,即扫描自定义的Mapper接口 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!--扫描所有的dao接口的实现,加入到ioc容器中-->
    <property name="basePackage" value="com.atguigu.ssmcrud.dao"></property>
    </bean> <!--配置一个可以执行批量的sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    <constructor-arg name="executorType" value="BATCH"/>
    </bean> <!-- ==================事务控制的配置(事务管理器)====================== -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--控制数据库连接池-->
    <property name="dataSource" ref="dataSource"></property>
    </bean> <!--开启基于注解的事务,使用xml配置形式的事务-->
    <aop:config>
    <!--切入式表达式,service下所有的类以及所有的方法,都可作为切入点。哪些方法可能会被切入事务-->
    <aop:pointcut id="txPoint" expression="execution(* com.atguigu.ssmcrud.service..*(..))"/>
    <!--配置事务增强,切入的规则由txAdvice指定,切入的方法由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> </beans>
  • 第六步:resources文件下,mybatisConfig.xml 配置MyBatis配置文件

    • 配置一些对MyBatis的属性设置(如:settings、typeAliases等,properties属性已经在外部进行配置了,即springApplicationConfig.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">
    <!--MyBatis配置文件,配置一些MyBatis不好配置的设置-->
    <configuration>
    <settings>
    <!--开启驼峰命名映射-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <!--可以返回自动生成主键-->
    <setting name="useGeneratedKeys" value="true"/>
    </settings>
    <!--
    plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
    properties?, settings?,
    typeAliases?, typeHandlers?,
    objectFactory?,objectWrapperFactory?,
    plugins?,
    environments?, databaseIdProvider?, mappers?
    -->
    <plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
    <!-- 使用下面的方式配置参数,后面会有所有的参数介绍 -->
    <property name="param1" value="value1"/>
    </plugin>
    </plugins> </configuration>
  • 第七步:resources文件下,mbg.xml 配置逆向生成属性

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration
    PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
    "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
    <generatorConfiguration>
    <properties resource="dbconfig.properties"/>
    <!--
    targetRuntime="MyBatis3Simple":生成简单版的CRUD
    MyBatis3:豪华版 用于复杂的数据库CRUD操作
    -->
    <context id="DB2Tables" targetRuntime="MyBatis3"> <!-- 清除生成文件中的注释,该标签必须在生成文件之前定义 -->
    <commentGenerator>
    <property name="suppressAllComments" value="true"/>
    </commentGenerator> <!-- jdbcConnection:指定如何连接到目标数据库 -->
    <jdbcConnection driverClass="${mysql.driver}"
    connectionURL="${mysql.url}"
    userId="${mysql.username}"
    password="${mysql.password}">
    </jdbcConnection> <!--类型解析器-->
    <javaTypeResolver >
    <property name="forceBigDecimals" value="false" />
    </javaTypeResolver> <!-- javaModelGenerator:指定javaBean的生成策略(生成的位置)
    targetPackage="test.model":目标包名
    targetProject="\MBGTestProject\src":目标工程
    -->
    <javaModelGenerator targetPackage="com.atguigu.ssmcrud.bean"
    targetProject=".\src\main\java">
    <property name="enableSubPackages" value="true" />
    <property name="trimStrings" value="true" />
    </javaModelGenerator> <!-- sqlMapGenerator:sql语句与接口方法映射生成策略 xml配置文件 -->
    <!-- 指定sql映射文件生成的位置:生成到resources文件下的mapper文件夹中-->
    <sqlMapGenerator targetPackage="mapper"
    targetProject=".\src\main\resources">
    <property name="enableSubPackages" value="true" />
    </sqlMapGenerator> <!-- javaClientGenerator:指定mapper接口方法所在的位置 -->
    <!-- 指定dao接口生成的位置:mapper接口(业务行为接口) -->
    <javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.ssmcrud.dao"
    targetProject=".\src\main\java">
    <property name="enableSubPackages" value="true" />
    </javaClientGenerator> <!-- table标签:指定每个表的生成策略 -->
    <!-- 指定要逆向分析数据库中的哪些表:根据表要创建javaBean -->
    <table tableName="tbl_emp" domainObjectName="Employee"></table>
    <table tableName="tbl_dept" domainObjectName="Department"></table> </context>
    </generatorConfiguration>

2.4 测试

2.4.1 数据库的创建

创建一个名为ssm_crud的数据库

  • 这个数据库中有tbl_dept、tbl_emp两张表
CREATE TABLE `tbl_emp` (
`emp_id` int(11) NOT NULL AUTO_INCREMENT,
`emp_name` varchar(255) DEFAULT NULL,
`gender` char(1) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`d_id` int(11) DEFAULT NULL,
PRIMARY KEY (`emp_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `tbl_dept` (
`dept_id` int(11) NOT NULL AUTO_INCREMENT,
`dept_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`dept_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • 需要增加外键,即tbl_emp的d_id字段是tbl_dept的外键,从表tbl_dept的被引用字段dept_id是其主键

2.4.2 使用Spring单元测试对数据库进行操作

使用注解的方式导入Spring的配置文件,并且使用Spring单元测试(需添加依赖)

        <!-- Spring TestContext 用于注解导入Spring全局配置文件-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.18.RELEASE</version>
</dependency>

测试代码:

/*
* 推荐Spring项目使用Spring的单元测试,就可以自动注入我们需要的组件了
* 1. 导入SpringTest依赖,注意scope标签,要去掉test
* 2. @ContextConfiguration指定Spring配置文件的位置
* 3. autowired要使用的组件即可
* */ @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:springApplicationConfig.xml"})
public class MapperTest { /*
* ==========使用注解的方式===========
* 由于先加载了Spring配置文件,所以使用注解,自动注入,获取实例
* */
@Autowired
DepartmentMapper departmentMapper; @Autowired
EmployeeMapper employeeMapper; @Autowired
SqlSession sqlSession; /*
* 测试DepartmentMapper
* */
@Test
public void testCRUD(){
// // ===========传统方法============
// // 1. 创建Spring IOC容器
// ApplicationContext ioc = new ClassPathXmlApplicationContext("classpath:springApplicationConfig.xml");
// // 2. 从容器中获取mapper接口实例,因为在spring配置文件中,对mapper接口进行扫描并添加
// DepartmentMapper departmentMapper = ioc.getBean(DepartmentMapper.class); // 1. 新增部门信息,使用插入信息参数是可以选择的,所以选择 可选择参数插入方法,即insertSelective,因为id是自增的
departmentMapper.insertSelective(new Department(null, "开发部"));
departmentMapper.insertSelective(new Department(null, "测试部")); // 2. 生成员工数据,测试员工的插入
employeeMapper.insertSelective(new Employee(null, "Jerry", "M", "jerry@123.com", 1)); // 3. 批量插入多个员工:批量操作,需要在Spring全局配置文件中设置一个可以执行批量操作的sqlSession // for(){ // 如果直接使用自动注入的mapper对象,无论是循环多少次,都是建立了相应次数的sqlSession,并不是批量操作
// employeeMapper.insertSelective(new Employee());
// }
// 使用在spring配置文件中已经配置好的批量处理sqlSession
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
for (int i = 0; i < 200; i++) {
String uid = UUID.randomUUID().toString().substring(0, 5) + i;
mapper.insertSelective(new Employee(null, uid, "W", uid + "@123.com", 1));
}
for (int i = 0; i < 200; i++) {
String uid = UUID.randomUUID().toString().substring(0, 5) + i;
mapper.insertSelective(new Employee(null, uid, "M", uid + "@qq.com", 2));
}
for (int i = 0; i < 200; i++) {
String uid = UUID.randomUUID().toString().substring(0, 5) + i;
mapper.insertSelective(new Employee(null, uid, "M", uid + "@gmail.com", 1));
} }
}

2.5 完善项目结构

  • bean:又entity,实体层,实现对数据库表的映射,往往是一个数据对象。
  • controller:控制层,实现对浏览器发来的请求进行相应处理。
  • dao:数据访问对象层,创建接口,定义对数据库进行CRUD的操作方法,再建立与mapper xml配置文件的映射。
  • service:业务逻辑层,实现业务的真正逻辑,编写业务逻辑的代码,可以实现切面操作等

3. 使用MBG逆向工程

1. 生成mapper及接口方法等文件

MyBatis Generator(MBG配置文件):使用MyBatis3

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<properties resource="dbconfig.properties"/>
<!--
targetRuntime="MyBatis3Simple":生成简单版的CRUD
MyBatis3:豪华版 用于复杂的数据库CRUD操作
-->
<context id="DB2Tables" targetRuntime="MyBatis3"> <!-- 清除生成文件中的注释,该标签必须在生成文件之前定义 -->
<commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator> <!-- jdbcConnection:指定如何连接到目标数据库 -->
<jdbcConnection driverClass="${mysql.driver}"
connectionURL="${mysql.url}"
userId="${mysql.username}"
password="${mysql.password}">
</jdbcConnection> <!--类型解析器-->
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver> <!-- javaModelGenerator:指定javaBean的生成策略(生成的位置)
targetPackage="test.model":目标包名
targetProject="\MBGTestProject\src":目标工程
-->
<javaModelGenerator targetPackage="com.atguigu.ssmcrud.bean"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator> <!-- sqlMapGenerator:sql语句与接口方法映射生成策略 xml配置文件 -->
<!-- 指定sql映射文件生成的位置:生成到resources文件下的mapper文件夹中-->
<sqlMapGenerator targetPackage="mapper"
targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator> <!-- javaClientGenerator:指定mapper接口方法所在的位置 -->
<!-- 指定dao接口生成的位置:mapper接口(业务行为接口) -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.ssmcrud.dao"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator> <!-- table标签:指定每个表的生成策略 -->
<!-- 指定要逆向分析数据库中的哪些表:根据表要创建javaBean -->
<table tableName="tbl_emp" domainObjectName="Employee"></table>
<table tableName="tbl_dept" domainObjectName="Department"></table> </context>
</generatorConfiguration>

开启逆向工程:

    @Test
public void runMBG() throws XMLParserException, IOException, InvalidConfigurationException, SQLException, InterruptedException {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("src/main/resources/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);
}

2. 增加含外键查询的功能

​ 主要是针对EmployeeMapper.xml文件,对SQL语句进行修改,以保证在数据库中查询员工数据的同时,通过外键将对应的部门信息查询出来。

增加了两个select查询语句、一个resultMap和一段sql

节选添加部分:

	........
<!-- 查询的结果的返回类型(员工信息+部门信息) -->
<resultMap id="WithDeptResultMap" type="com.atguigu.ssmcrud.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.atguigu.ssmcrud.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> <!--
List<Employee> selectByExampleWithDept(EmployeeExample example);
查询职员信息(含部门信息)
-->
<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> <!--
Employee selectByPrimaryKeyWithDept(Integer empId);
按员工ID查询员工信息(含部门信息)
-->
<select id="selectByPrimaryKeyWithDept" resultMap="WithDeptResultMap">
select
<include refid="WithDept_Column_List" />
from tbl_emp e
left join tbl_dept d on e.'d_id'=d.'dept_id'
where emp_id = #{empId,jdbcType=INTEGER}
</select>
........

4. 查询功能

传统方式:只适用于客户端是浏览器,因为服务器会将整个页面数据回传给客户端

  1. 访问index.jsp页面
  2. index.jsp页面发送出查询员工列表请求
  3. EmployeeController来接受请求,查出员工数据
  4. 来到list.jsp页面进行展示
  5. pageHelper分页插件完成分页查询功能

URI:/emps

4.1 使用Ajax请求

Ajax特点:

  • 不刷新网页更新网页
  • 在页面加载后从服务器请求数据
  • 在页面加载后从服务器接收数据
  • 在后台向服务器发送数据

​ 为了满足不同客户端(如浏览器、Android等)向服务器发送请求,都能正确地显示页面,因为不可能返回一个页面数据给除浏览器外的其他客户端,所以服务器需要将数据以Json形式返回。提升了扩展性,与平台无关。

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

4.2 dao层

​ 定义了两个新的查询方法,这两个新方法已经与sql语句建立了映射关系

public interface EmployeeMapper {
long countByExample(EmployeeExample example); int deleteByExample(EmployeeExample example); int deleteByPrimaryKey(Integer empId); int insert(Employee record); int insertSelective(Employee record); List<Employee> selectByExample(EmployeeExample example); Employee selectByPrimaryKey(Integer empId); /*
* 定义了两个新的方法:
* 1. 按条件查询员工信息(带部门信息)
* 2. 按主键查询员工信息(带部门信息)
* */
List<Employee> selectByExampleWithDept(EmployeeExample example);
Employee selectByPrimaryKeyWithDept(Integer empId); 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);
}

4.3 service层

接口 EmployeeService.java

public interface EmployeeService {
public List<Employee> getAll(); Employee getEmp(Integer id);
}

在EmployeeServiceImpl.java文件中添加业务处理逻辑:

@Service
public class EmployeeServiceImpl implements EmployeeService{
@Autowired
EmployeeMapper employeeMapper; @Override
public List<Employee> getAll() {
return employeeMapper.selectByExampleWithDept(null); // 传入null值,即没有额外的查询的条件
} /*
* 按照员工的id查询员工
* */
@Override
public Employee getEmp(Integer id) {
Employee employee = employeeMapper.selectByPrimaryKey(id);
return employee;
}
}

4.4 controller层

@Controller
public class EmployeeController { @Autowired
EmployeeService employeeService;
/*
* 查询员工数据(分页查询)
* url: http://localhost:8080/ssm_crud/emps
* pn:分页查询的页码,这里使用RequestParam注解,如果不传pn的值,那么pn默认值为1
* */
@RequestMapping("/emps")
public String getEmps(@RequestParam(value = "pn", defaultValue = "1")Integer pn, Model model){
// 使用分页查询,引入PagerHelper分页插件
// 在查询之前只需要调用,并传入页码以及每页的数量
PageHelper.startPage(pn, 5);
// 在startPage后面紧跟查询,且这个查询就是一个分页查询,即查询返回的结果已经被分页了
List<Employee> empsList = employeeService.getAll();
// 当然也可以使用PageInfo对查询结果进行封装,以获取更多详细信息,包括查询出来的数据,传入连续显示的页数
PageInfo pageInfo = new PageInfo(empsList, 5); // 传入连续显示的页数,这里是5页,就能调用方法显示连续5页的页码
model.addAttribute("pageInfo", pageInfo); // 请求域中存放进行封装过的查询结果数据,用于在前端JSP页面进行显示 return "emps";
} @RequestMapping("/empsJson")
@ResponseBody
// 用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器
public Msg getEmpsWithJson(@RequestParam(value = "pn", defaultValue = "1")Integer pn){ // 需要Jackson包支持
// 使用分页查询,引入PagerHelper分页插件
// 在查询之前只需要调用,并传入页码以及每页的数量
PageHelper.startPage(pn, 5);
// 在startPage后面紧跟查询,且这个查询就是一个分页查询,即查询返回的结果已经被分页了
List<Employee> empsList = employeeService.getAll();
// 当然也可以使用PageInfo对查询结果进行封装,以获取更多详细信息,包括查询出来的数据,传入连续显示的页数
PageInfo pageInfo = new PageInfo(empsList, 5); // 传入连续显示的页数,这里是5页,就能调用方法显示连续5页的页码 return Msg.success().add("pageInfo", pageInfo);
} // 查询指定id员工的信息
@RequestMapping(value = "/emp/{id}", method = RequestMethod.GET)
@ResponseBody
public Msg getEmp(@PathVariable("id")Integer id) { // 指定id的值是从路径中取出
Employee emp = employeeService.getEmp(id);
return Msg.success().add("emp", emp);
}
}

4.5 效果图

5. 新增功能

  1. 在index.jsp页面点击“新增”
  2. 弹出新增对话框
  3. 去数据库查询部门列表,显示在对话框中
  4. 用户输入数据完成保存
    • Jquery前端校验
    • Ajax用户名重复校验
    • 重要数据加上后端校验(JSR303),唯一约束
  5. 完成保存
  • URI-restful风格

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

5.1 dao层

public interface EmployeeMapper {
long countByExample(EmployeeExample example); int deleteByExample(EmployeeExample example); int deleteByPrimaryKey(Integer empId); int insert(Employee record); int insertSelective(Employee record); List<Employee> selectByExample(EmployeeExample example); Employee selectByPrimaryKey(Integer empId); /*
* 定义了两个新的方法:
* 1. 按条件查询员工信息(带部门信息)
* 2. 按主键查询员工信息(带部门信息)
* */
List<Employee> selectByExampleWithDept(EmployeeExample example); Employee selectByPrimaryKeyWithDept(Integer empId); 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);
}

5.2 service层

​ 增加两个方法,addEmp和checkUser

public interface EmployeeService {
public List<Employee> getAll(); void addEmp(Employee employee); boolean checkUser(String empName); Employee getEmp(Integer id);
}
@Service
public class EmployeeServiceImpl implements EmployeeService{
@Autowired
EmployeeMapper employeeMapper; @Override
public List<Employee> getAll() {
return employeeMapper.selectByExampleWithDept(null); // 传入null值,即没有额外的查询的条件
} @Override
public void addEmp(Employee employee) {
employeeMapper.insertSelective(employee); // insertSelective() 不需要插入id
} /*
* 检验用户名是否可用
* @return true:代表当前姓名可用;false:当前姓名不可用
* */
@Override
public boolean checkUser(String empName) {
// 创建我们的查询条件对象
EmployeeExample employeeExample = new EmployeeExample();
EmployeeExample.Criteria criteria = employeeExample.createCriteria(); // 创建拼接查询条件的对象
criteria.andEmpNameEqualTo(empName);
long count = employeeMapper.countByExample(employeeExample); // 进行查询,返回符合查询条件的记录数
return count == 0;
} /*
* 按照员工的id查询员工
* */
@Override
public Employee getEmp(Integer id) {
Employee employee = employeeMapper.selectByPrimaryKey(id);
return employee;
}
}

5.3 controller层

后端校验:

  • 使用JSR303校验,需要在pom.xml中导入Hibernate-Validator依赖
<!-- JSR303数据校验支持 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>7.0.1.Final</version>
</dependency>
  • 在EmployeeController.java文件中,saveEmp()方法中进行JSR303:
    /*
员工保存(支持JSR303校验,导入Hibernate-Validator)
使用restful风格的URI请求
- /emp/{id} GET 查询员工
- /emp POST 保存员工
- /emp/{id} PUT 修改员工
- /emp/{id} DELETE 删除员工
* */
@RequestMapping(value = "/emp", method = RequestMethod.POST)
@ResponseBody
// valid代表数据需要被校验,BindingResult 校验结果对象
public Msg saveEmp(@Valid Employee employee, BindingResult result){ // 因为页面提交来的数据与Employee对象的属性相同,会自动封装
if(result.hasErrors()){ // 校验失败
// 校验失败,返回失败,并在模态框中显示校验失败的错误信息
HashMap<String, Object> map = new HashMap<>();
List<FieldError> errors = result.getFieldErrors();
for (FieldError error : errors) {
System.out.println("错误的字段名:" + error.getField());
System.out.println("错误信息" + error.getDefaultMessage());
map.put(error.getField(), error.getDefaultMessage());
}
return Msg.fail().add("errorFields", map);
} else { // 校验通过,用户名可以使用
employeeService.addEmp(employee);
return Msg.success();
}
}

​ 校验前需要在Employee的bean对象中进行注解:

public class Employee {
private Integer empId; @Pattern(regexp = "(^[a-zA-Z0-9_-]{6,16}$)|(^[\\u2E80-\\u9FFF]{2,5})"
, message = "用户名必须是2-5位中文或者6-16位英文和数字的组合")
private String empName; private String gender; @Email // 使用@Email注解,JSR303会对这个属性进行邮箱格式校验
private String email; private Integer dId; ........ }
  • 在EmployeeController.java文件中,checkUser()方法中对传入的用户名进行检查,到数据库中检查是否该用户已经存在
    // 检查新增加的用户名是否可用
@RequestMapping("/checkuser")
@ResponseBody
public Msg checkuser(@RequestParam("empName")String empName){
// 先判断用户名是否是合法的用户名(后端校验)
String regx = "(^[a-zA-Z0-9_-]{6,16}$)|(^[\\u2E80-\\u9FFF]{2,5})";
if(!empName.matches(regx)) {
return Msg.fail().add("va_msg", "后端校验出用户名格式非法!");
}
boolean b = employeeService.checkUser(empName); // 后端校验
if (b){
return Msg.success();
} else {
return Msg.fail().add("va_msg", "后台服务器查询出数据库中已经有该用户名!");
}
}

​ employeeService.checkUser(empName); // 后端校验

    @Override
public boolean checkUser(String empName) {
// 创建我们的查询条件对象
EmployeeExample employeeExample = new EmployeeExample();
EmployeeExample.Criteria criteria = employeeExample.createCriteria(); // 创建拼接查询条件的对象
criteria.andEmpNameEqualTo(empName);
long count = employeeMapper.countByExample(employeeExample); // 进行查询,返回符合查询条件的记录数
return count == 0;
}

5.4 效果图

6. 修改功能

/emp/{id} PUT 修改员工

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

6.1 service层

public interface EmployeeService {
public List<Employee> getAll(); void addEmp(Employee employee); boolean checkUser(String empName); Employee getEmp(Integer id); void updateEmp(Employee employee);
}
@Service
public class EmployeeServiceImpl implements EmployeeService{
@Autowired
EmployeeMapper employeeMapper; @Override
public List<Employee> getAll() {
return employeeMapper.selectByExampleWithDept(null); // 传入null值,即没有额外的查询的条件
} @Override
public void addEmp(Employee employee) {
employeeMapper.insertSelective(employee); // insertSelective() 不需要插入id
} /*
* 检验用户名是否可用
* @return true:代表当前姓名可用;false:当前姓名不可用
* */
@Override
public boolean checkUser(String empName) {
// 创建我们的查询条件对象
EmployeeExample employeeExample = new EmployeeExample();
EmployeeExample.Criteria criteria = employeeExample.createCriteria(); // 创建拼接查询条件的对象
criteria.andEmpNameEqualTo(empName);
long count = employeeMapper.countByExample(employeeExample); // 进行查询,返回符合查询条件的记录数
return count == 0;
} /*
* 按照员工的id查询员工
* */
@Override
public Employee getEmp(Integer id) {
Employee employee = employeeMapper.selectByPrimaryKey(id);
return employee;
} /*
* 更新员工的信息
* */ @Override
public void updateEmp(Employee employee) {
employeeMapper.updateByPrimaryKeySelective(employee);
}
}

6.2 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
*/
// 更新员工信息(注意是empId)
@RequestMapping(value = "/emp/{empId}", method = RequestMethod.PUT)
@ResponseBody
public Msg updateEmp(Employee employee){
System.out.println("将要更新的员工数据:" + employee);
employeeService.updateEmp(employee);
return Msg.success();
}

​ 由于ajax发送PUT请求会导致,Tomcat一看是PUT不会封装请求体中的数据为map,只有POST形式的请求才封装请求体为map。因此有两种解决方案:

  • 第一种方法:
    // 编辑模态框中:点击更新,更新员工信息
$("#emp_update_btn").click(function(){
//验证邮箱是否合法
//1、校验邮箱信息
var email = $("#email_update_input").val();
var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
if(!regEmail.test(email)){
show_validate_msg("#email_update_input", "error", "邮箱格式不正确");
return false;
}else{
show_validate_msg("#email_update_input", "success", "");
}
//2、发送ajax请求保存更新的员工数据(验证成功后)
// 第一种方法:通过拦截器HiddenHttpMethodFilter将POST转换为PUT
$.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);
}
});
});
  • 第二种方法:使用拦截器FormContentFilter
    // 编辑模态框中:点击更新,更新员工信息
$("#emp_update_btn").click(function(){
//验证邮箱是否合法
//1、校验邮箱信息
var email = $("#email_update_input").val();
var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
if(!regEmail.test(email)){
show_validate_msg("#email_update_input", "error", "邮箱格式不正确");
return false;
}else{
show_validate_msg("#email_update_input", "success", "");
}
//2、发送ajax请求保存更新的员工数据(验证成功后)
// 第二种方法:使用拦截器FormContentFilter
$.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);
}
});
});

6.3 效果图

7. 删除功能

/emp/{id} DELETE 删除员工

​ 实现单个及批量删除的方法

7.1 service层

public interface EmployeeService {
public List<Employee> getAll(); void addEmp(Employee employee); boolean checkUser(String empName); Employee getEmp(Integer id); void updateEmp(Employee employee); void deleteEmp(Integer empId); void deleteEmps(List<Integer> ids);
}
    /*
* 删除员工的信息
* */
@Override
public void deleteEmp(Integer empId) {
employeeMapper.deleteByPrimaryKey(empId);
} @Override
public void deleteEmps(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);
}

7.2 controller层

    /*
* 删除员工信息(注意是empId)
* 单个批量二合一
* 批量删除:1-2-3
* 单个删除:1
* */
@RequestMapping(value = "/emp/{empIds}", method = RequestMethod.DELETE)
@ResponseBody
public Msg deleteEmp(@PathVariable("empIds")String empIds){ //PathVariable取请求路径中指定占位符的值
if(empIds.contains("-")) { // 批量删除
String[] splits = empIds.split("-");
ArrayList<Integer> del_ids = new ArrayList<>();
// 组装del_ids数组,需要批量删除员工数据的id集合
for (String split : splits) {
Integer id = Integer.parseInt(split);
del_ids.add(id);
}
employeeService.deleteEmps(del_ids);
} else { // 单个删除
Integer i = Integer.parseInt(empIds);
System.out.println("将要删除的员工数据:" + employeeService.getEmp(i));
employeeService.deleteEmp(i);
}
return Msg.success();
}

7.3 效果图

8. 总结

8.1 使用maven打包

  • 运行打包,web项目生成war包

  • 将生成的war包放到Tomcat目录下的webapp下,运行tomcat,会把项目在本地服务器上进行部署,就能进行访问了

​ 启动服务器,访问SSM_CRUD项目,完毕!

8.2 思路图

尚硅谷SSM-CRUD实战Demo的更多相关文章

  1. 2018年尚硅谷《全套Java、Android、HTML5前端视频》

    全套整合一个盘里:链接:https://pan.baidu.com/s/1nwnrWOp 密码:h4bw 如果分类里没有请下载下边那些小项教程链接 感谢尚硅谷提供的视频教程:http://www.at ...

  2. 尚硅谷《全套Java、Android、HTML5前端视频》

    尚硅谷<全套Java.Android.HTML5前端视频> (百万谷粉推荐:史上最牛.最适合自学的全套视频.资料及源码) [尚硅谷官网资料导航] 谷粒学院在线学习:http://www.g ...

  3. 尚硅谷全套课件整理:Java、前端、大数据、安卓、面试题

    目录 Java 尚硅谷 IT 精英计划 JavaSE 内部学习笔记.pdf 尚硅谷 Java 基础实战之银行项目.pdf 尚硅谷 Java 技术之 JDBC.pdf 尚硅谷 Java 技术之 Java ...

  4. 3、尚硅谷_SSM高级整合_使用ajax操作实现修改员工的功能

    当我们点击编辑案例的时候,我们要弹出一个修改联系人的模态对话框,在上面可以修改对应的联系人的信息 这里我们我们要编辑按钮添加点击事件弹出对话框 第一步:在页面中在新增一个编辑联系人的模态对话框 第二步 ...

  5. 2、尚硅谷_SSM高级整合_创建Maven项目.avi

    第一步我们新建立一个web工程 这里首先要勾选上enable的第一个复选框 这里要勾选上add maven support 我们在pom.xml中添加sevlet的依赖 创建java web项目之后, ...

  6. 3、尚硅谷_SSM高级整合_使用ajax操作实现删除的功能

    点击删除的时候,要删除联系人,这里同点击编辑按钮一样给删除按钮添加点击事件的时候不能使用 $(".delete_btn").click(function(){ }); 这种方式,因 ...

  7. 3、尚硅谷_SSM高级整合_使用ajax操作实现增加员工的功能

    20.尚硅谷_SSM高级整合_新增_创建员工新增的模态框.avi 1.接下来当我们点击增加按钮的时候会弹出一个员工信息的对话框 知识点1:当点击新增的时候会弹出一个bootstrap的一个模态对话框 ...

  8. 2、尚硅谷_SSM高级整合_使用ajax操作实现页面的查询功能

    16.尚硅谷_SSM高级整合_查询_返回分页的json数据.avi 在上一章节的操作中我们是将PageInfo对象存储在request域中,然后list页面解析request域中的对象实现信息的显示. ...

  9. Spring学习笔记 1. 尚硅谷_佟刚_Spring_HelloWorld

    1,准备工作 (1)安装spring插件 搜索https://spring.io/tools/sts/all就可以下载最新的版本 下载之后不用解压,使用Eclipse进行安装.在菜单栏最右面的Help ...

  10. 尚硅谷spring_boot课堂笔记

    尚硅谷spring_boot课堂笔记

随机推荐

  1. [loj3156]回家路线

    令$dp[i]$表示经过第$i$条边后的最小烦躁值,有$且dp[i]=\min_{y_{j}=x_{i}且q_{j}\le p_{i}}dp[j]+f(p_{i}-q_{j})$,其中$f(x)=Ax ...

  2. [loj2478]林克卡特树

    原题等价于选择恰好$k+1$条不相交(无公共点)的路径使得边权和最大 证明:对于原题中的最优解,一定包含了k条0边权的边(否则可以将未使用的边删掉,然后将这条路径的末尾与不在同一个连通块内的点连边), ...

  3. 一些需要记住的linux命令

    1. 系统帮助命令                      ----man 2. 数据同步写入磁盘               ----sync 3. 查找文件命令                  ...

  4. HTML四种定位-粘滞定位

    粘滞定位 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset=&q ...

  5. NCBI SRA数据如何进行md5校验?

    下了一些sra数据库中的公共数据,因为pretech和aspera不稳定,稍微大点的文件经常传断,部分文件我只能通过本地下载再上传. 那么问题来了,sra没有md5校验,我怎么知道我数据的完整性,尤其 ...

  6. Excel-判断一个文本字符串中是否包含数字! 判断一个文本字符串是否是纯汉字!

    0.判断一个文本字符串中是否包含数字!/判断一个文本字符串是否是纯汉字! 公式=IF(LENB(A1)=2*LEN(A1),"都是汉字","含有非汉字字符") ...

  7. Oracle-除了会排序,你对ORDER BY的用法可能一无所知!

    导读 为什么只有ORDER BY后面可以使用列别名 为什么不推荐使用ORDER BY后接数字来排序 为什么视图和子查询里面不能使用ORDER BY -- ​小伙伴们在进行SQL排序时,都能很自然的使用 ...

  8. 【JAVA开发】浅析双亲委派机制

    双亲委派机制存在的意义 双亲委派只是一种说法,个人觉得叫单亲委派更合适,因为向上传递的父类只有一个,估计只是翻译过来的,形成的一种习惯,大家可以当做单亲委派 四种加载器都是用于类的加载,只是加载的对象 ...

  9. Web安全学习二

    目录 常见漏洞攻防 SQL注入 注入分类 按技巧分类 按获取数据的方式分类 注入检测 权限提升 数据库检测 绕过技巧 CheatSheet SQL Server Payload MySQL Paylo ...

  10. 转Android Canvas和Paint基本使用

    Android Canvas和Paint基本使用   这篇文章主要介绍下画笔Paint和画布Canvas的基本使用  1.Paint 创建对象Paint mPaint = new Paint(); 常 ...