SSM-CRUD

一、项目简介

主界面演示

功能点

  • 分页
  • 数据校验
  • ajax
  • Rest 风格的 URI

技术点

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

二、基础环境搭建

1、创建 Maven 工程 ssm_crud_study

3、添加 web 框架

(1)右键工程,点击 Add Framework Suppor

(2)选择 Web Application

3、在 pom.xml 中引入项目依赖

  • Spring
  • SpringMVC
  • MyBatis
  • 数据库连接池,驱动包
  • 其他(jstl,servlet-api,junit)

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.chen</groupId>
<artifactId>ssm-crud</artifactId>
<version>1.0-SNAPSHOT</version> <dependencies>
<!-- pagehelper 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.0.0</version>
</dependency> <!-- MBG -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency> <!-- Spring MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency> <!-- 返回 json 字符串的支持 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.8</version>
</dependency> <!-- JS303 数据校验支持 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency> <!-- Spring Jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency> <!-- Spring Test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.7.RELEASE</version>
<scope>test</scope>
</dependency> <!-- 面向切面编程 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.7.RELEASE</version>
</dependency> <!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency> <!-- MyBatis 适配包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency> <!-- 数据库连接池 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency> <!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency> <!-- jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency> <!-- servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency> <!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency> <!-- https://mvnrepository.com/artifact/javax.servlet/jsp-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency> </dependencies> </project>

4、引入 bootstrap 前端框架

(1)前往 bootstrap 官网下载

下载地址: https://v3.bootcss.com,选择用于生产环境的 Bootstrap

(2)解压后将文件夹添加到工程中 /static

5、编写 ssm 整合的关键配置文件

  • **web.xml,主要用于配置 Filter、Listener、Servlet **

  • springmvc 配置文件,主要控制网站逻辑的跳转

  • spring 配置文件,主要配置与业务逻辑相关的文件

  • mybatis 配置文件,主要用于配置 MyBatis

(1)配置 web.xml

<?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 的容器 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param> <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <!-- 2、配置 SpringMVC 前端控制器
默认会加载 WEB-INF 下的 <servletName>-servlet.xml 配置文件
即 dispatcherServlet-servlet.xml
-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</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-app>

在 src/main/resources/ 下加入 applicationContext.xml, 防止 idea 报错

(2)配置 springmvc 配置文件

WEB-INF 下加入 dispatcherServlet-servlet.xml

src/main/java 创建包结构

**配置 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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- Spring MVC 的配置文件,主要控制网站逻辑的跳转 -->
<context:component-scan base-package="com.study" 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 name="suffix" value=".jsp"/>
</bean> <!-- 两个标准配置 -->
<!-- 将 Spring MVC 不能处理的请求交给 Tomcat -->
<mvc:default-servlet-handler/>
<!-- Spring MVC 更高级的一些功能,JSR303 检验,快捷的 ajax ... 映射动态请求 -->
<mvc:annotation-driven/>
</beans>

(3) 配置 spring 配置文件

配置 dbconfig.properties ,配置数据库连接信息

src/main/resources 下加入 dbconfig.properties

dbconfig.properties

jdbc.jdbcUrl=jdbc:mysql://localhost:3306/ssm_crud_study?useUnicode=true&characterEncoding=utf8
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.user=root
jdbc.password=root

src/main/resources 下创建 mybatis-config.xml、mapper文件夹

配置 applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <context:component-scan base-package="com.chen">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan> <!-- Spring 的配置文件,主要配置与业务逻辑相关的文件 -->
<!-- 数据源、事务控制 ... -->
<context:property-placeholder location="classpath:dbconfig.properties"/> <bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean> <!-- ================================ 配置和 MyBatis 的整合 =============================== -->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 指定 MyBatis 全局配置文件的位置 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="dataSource" ref="pooledDataSource"/>
<!-- 指定 MyBatis mapper 文件的位置 -->
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean> <!-- 配置一个执行批量的 sqlSession -->
<bean id="sessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"/>
<constructor-arg name="executorType" value="BATCH"/>
</bean> <!-- 配置扫描器,将 MyBatis 接口的实现加入到 ioc 容器中 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 扫描所有 dao 接口的实现,加到 ioc 容器中 -->
<property name="sqlSessionTemplateBeanName" value="sessionTemplate"/>
<property name="basePackage" value="com.study"/>
</bean> <!-- 事务控制的配置 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 控制住数据源 -->
<property name="dataSource" ref="pooledDataSource"/>
</bean> <!-- 开启基于注解的事务,使用 xml 配置形式的事务(必要主要的都是使用配置式) -->
<aop:config>
<!-- 切入点表达式 -->
<aop:pointcut id="txPoint" expression="execution(* com.chen.crud.service..*(..))"/>
<!-- 配置事务增强 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
</aop:config> <!-- 配置事务增强,事务如何切入 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 所有方法都是事务方法 -->
<tx:method name="*"/>
<!-- 以 get 开始的所有方法 -->
<tx:method name="get*" read-only="true"/>
</tx:attributes>
</tx:advice>
</beans>

此处目前会报错,是因为此时 mapper 文件夹目录下没有 .xml 文件,目前可忽略

(4)配置 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>
<!-- 开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings> <typeAliases>
<!-- 类型别名可为 Java 类型设置一个缩写名字
每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author
-->
<package name="com.study.crud.bean"/>
</typeAliases> <plugins>
<!-- 分页插件 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
</configuration>

6、创建数据库

CREATE DATABASE ssm_crud_study;

CREATE TABLE `ssm_crud_study`.`tbl_emp` (
`emp_id` INT NOT NULL AUTO_INCREMENT,
`emp_name` VARCHAR(255) NOT NULL,
`gender` CHAR(1) NULL,
`email` VARCHAR(255) NULL,
`d_id` INT NULL,
PRIMARY KEY (`emp_id`)); CREATE TABLE `ssm_crud_study`.`tbl_dept` (
`dept_id` INT NOT NULL,
`dept_name` VARCHAR(255) NOT NULL,
PRIMARY KEY (`dept_id`)); ALTER TABLE `ssm_crud_study`.`tbl_emp`
ADD INDEX `dept_id_idx` (`d_id` ASC) VISIBLE;
;
ALTER TABLE `ssm_crud_study`.`tbl_emp`
ADD CONSTRAINT `dept_id`
FOREIGN KEY (`d_id`)
REFERENCES `ssm_crud_study`.`tbl_dept` (`dept_id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION;

7、使用 mapper 的逆向工程生成 bean、mapper

在工程中添加 mbg.xml

配置 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> <context id="DB2Tables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 去除生成的 bean、mapper 中的注释 -->
<property name="suppressAllComments" value="true" />
</commentGenerator> <!-- 配置数据库连接 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/ssm_crud_study?useUnicode=true&amp;characterEncoding=utf8"
userId="root"
password="root">
</jdbcConnection> <javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver> <!-- 指定 javaBean 生成的位置 -->
<javaModelGenerator targetPackage="com.study.crud.bean" targetProject="src/main/java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator> <!-- 指定 sql 映射文件生成的位置 -->
<sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator> <!-- 指定 dao 接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.study.crud.dao" targetProject="src/main/java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator> <!-- table 指定每个表的生成策略 -->
<table tableName="tbl_emp" domainObjectName="Employee"> </table>
<table tableName="tbl_dept" domainObjectName="Department"></table> </context>
</generatorConfiguration>

src/test/java 中创建 MBGTest 用于生成 bean、mapper

public class MBGTest {

    public static void main(String[] args) 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);
}
}

运行结果:自动生成 bean、mapper

8、测试 mapper

在数据库中添加数据

INSERT INTO `ssm_crud_study`.`tbl_dept` (`dept_id`, `dept_name`) VALUES ('1', '开发部');
INSERT INTO `ssm_crud_study`.`tbl_dept` (`dept_id`, `dept_name`) VALUES ('2', '测试部'); INSERT INTO `ssm_crud_study`.`tbl_emp` (`emp_id`, `emp_name`, `gender`, `email`, `d_id`) VALUES ('1', 'Jerry', 'M', 'jerry@163.com', '1');

src/test/java 创建 MapperTest

首先测验能否连接到获得 mapper 实例

测试能否获取数据库中信息

向数据库中插入 1000 条数据

// 使用 Spring Test
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class MapperTest { @Autowired
DepartmentMapper departmentMapper; // 可以执行批量操作的 sqlSession
@Autowired
SqlSession sqlSession; @Test
public void testCRUD() {
// System.out.println(departmentMapper);
// Department department = departmentMapper.selectByPrimaryKey(1);
// System.out.println("dept_id=" + department.getDeptId() + ",dept_name=" + department.getDeptName()); EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
for (int i = 0; i < 1000; i++) {
// 使用 UUID 工具类,随机生成用户名
String empName = UUID.randomUUID().toString().substring(0, 5);
// 需要在 Employee 中添加有参构造,!!!添加有参构造后必须添加无参构造 !!!
mapper.insertSelective(new Employee(null, empName, "M", empName + "@qq.com", 1));
}
}
}

三、CRUD开发

1、index.jsp

流程

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

index.jsp 添加标签 jsp:forward 跳转到 /emps

com.study.crud.controller 中添加 EmployeeController,并编写映射请求

/WEB-INF/viwes/ 下添加 list.jsp

启动服务器,查看是否跳转成功

我们查询员工时,希望能够查询出部门的信息,所以需要在 EmployeeMapper 中添加新的查询方法,并在 Employee 中添加 Department 属性,和 get、set 方法


EmployeeMapper.xml 中添加 SQL

<resultMap id="WithDeptResultMap" type="com.study.crud.bean.Employee">
<id column="emp_id" jdbcType="INTEGER" property="empId" />
<result column="emp_name" jdbcType="VARCHAR" property="empName" />
<result column="gender" jdbcType="CHAR" property="gender" />
<result column="email" jdbcType="VARCHAR" property="email" />
<result column="d_id" jdbcType="INTEGER" property="dId" />
<association property="department" javaType="com.study.crud.bean.Department">
<id column="dept_id" property="deptId"/>
<result column="dept_name" property="deptName"/>
</association>
</resultMap> <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);
Employee selectByPrimaryKeyWithDept(Integer empId);-->
<select id="selectByExampleWithDept" resultMap="WithDeptResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="WithDept_Column_List" />
FROM tbl_emp e
LEFT JOIN tbl_dept d ON e.d_id = d.dept_id
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<select id="selectByPrimaryKeyWithDept" resultMap="WithDeptResultMap">
select
<include refid="WithDept_Column_List" />
FROM tbl_emp e
LEFT JOIN tbl_dept d ON e.d_id = d.dept_id
where emp_id = #{empId,jdbcType=INTEGER}
</select>

EmployeeService 中添加 getAll() 方法

EmployeeController.getEmps() 中引入分页插件

src/test/ 下添加测试,测试分页插件

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {"classpath:applicationContext.xml", "file:web/WEB-INF/dispatcherServlet-servlet.xml"})
public class MVCTest { // 传入 Spring MVC 的 ioc
@Autowired
WebApplicationContext context; // 虚拟 mvc 请求,获取到处理结果
MockMvc mockMvc; @Before
public void initMockMvc() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
} @Test
public void testPage() throws Exception {
// 模拟请求拿到返回值
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/emps").param("pn", "5")).andReturn(); // 请求成功以后,请求域中会有 pageInfo
MockHttpServletRequest request = result.getRequest();
PageInfo page = (PageInfo) request.getAttribute("pageInfo");
System.out.println("当前页码:" + page.getPageNum());
System.out.println("总页码:" + page.getPages());
System.out.println("总记录数:" + page.getTotal());
System.out.println("在页面需要连续显示的页码");
int[] nums = page.getNavigatepageNums();
for (int num : nums)
System.out.print(" " + num);
System.out.println(); List<Employee> emps = page.getList();
for (Employee emp : emps)
System.out.println("ID:" + emp.getEmpId() + "==>Name:" + emp.getEmpName());
}
}

测试结果

2、list.jsp

/static 下加入 jquery

list.jsp 中引入 bootstrap、jquery

前往 Bootstrap 官网,查看文档


根据文档创建 新建 删除 按钮

查看效果

给按钮添加样式


查看样式

根据文档可以写出表格界面

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>员工列表</title>
<% pageContext.setAttribute("APP_PATH", request.getContextPath()); %> <%--
不以 / 开始的相对路径,找资源,以当前资源的路径为基准,经常容易出问题
以 / 开始的相对路径,找资源,以服务器的路径为标准 (http://localhost:8080),需要加上项目名
http://localhost:8080/crud
--%> <%-- 引入 jquery --%>
<script type="text/javascript" src="${APP_PATH}/static/js/jquery-1.12.4.min.js"></script>
<%-- 引入样式 --%>
<link rel="stylesheet" href="${APP_PATH}/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
<script src="${APP_PATH}/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<!-- 标题 -->
<!-- class="row" 为栅格系统的行 -->
<div class="row">
<!-- col-md-xx 数字代表列数 col-md-offset-xx 数字代表偏移-->
<div class="col-md-12">
<h1>SSM-CRUD</h1>
</div>
</div> <div class="row">
<div class="col-md-4 col-md-offset-8">
<button type="button" class="btn-primary">新建</button>
<button type="button" class="btn-danger">删除</button>
</div>
</div> <!-- 显示表格数据 -->
<div class="row">
<div class="col-md-12">
<!-- .table-hover 类可以让 <tbody> 中的每一行对鼠标悬停状态作出响应。
.table-striped 类可以给 <tbody> 之内的每一行增加斑马条纹样式。
-->
<table class="table table-hover table-striped">
<tr>
<th>#</th>
<th>lastName</th>
<th>email</th>
<th>gender</th>
<th>deptName</th>
<th>操作</th>
</tr>
<c:forEach items="${pageInfo.list}" var="emp">
<tr>
<th>${emp.empId}</th>
<th>${emp.empName}</th>
<th>${emp.email}</th>
<th>${emp.gender == 'M' ? '男' : '女'}</th>
<th>${emp.department.deptName}</th>
<th>
<button class="btn btn-primary btn-sm">
<!-- 铅笔图标 -->
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
编辑
</button>
<button class="btn btn-danger btn-sm">
<!-- 垃圾桶图标 -->
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
删除
</button>
</th>
</tr>
</c:forEach>
</table>
</div>
</div> </div>
</body>
</html>

查看效果

编写分页条

<!-- 显示分页信息 -->
<div class="row">
<!-- 分页文字信息 -->
<div class="col-md-6">
当前第 ${pageInfo.pageNum} 页,共有 ${pageInfo.pages} 页,总计 ${pageInfo.total} 条记录
</div>
<!-- 分页条信息 -->
<div class="col-md-6">
<nav aria-label="Page navigation">
<ul class="pagination"> <li><a href="${APP_PATH}/emps?pn=1">首页</a></li> <c:if test="${pageInfo.hasPreviousPage}">
<li>
<a href="${APP_PATH}/emps?pn=${pageInfo.prePage}" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
</c:if> <c:forEach items="${pageInfo.navigatepageNums}" var="page_Num">
<c:if test="${page_Num == pageInfo.pageNum}">
<li class="active"><a href="#">${page_Num}</a> </li>
</c:if>
<c:if test="${page_Num != pageInfo.pageNum}">
<li><a href="${APP_PATH}/emps?pn=${page_Num}">${page_Num}</a></li>
</c:if>
</c:forEach> <li>
<a href="#" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li> <li><a href="${APP_PATH}/emps?pn=${pageInfo.pages}">尾页</a></li>
</ul>
</nav>
</div>
</div>

查看效果

3、ajax

至此已经完成了基本的界面开发,但是目前的设计有着很大的缺陷,每次换页都需要重新加载整个网页。

由此引入 AJAX

AJAX = 异步 JavaScriptXML

AJAX 是一种用于创建快速动态网页的技术。

通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面。

有很多使用 AJAX 的应用程序案例:新浪微博、Google 地图、开心网等等。

基于 ajax 查询员工数据

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

(1)去除 index.jsp 中的 <jsp:forward> 标签,将 list.jsp 中的内容复制到 index.jsp

(2)删除 index.jsp 页面中带有从 EmployeeController.getEmps() 得到的 pageInfo 相关的标签

得到如下

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>员工列表</title>
<% pageContext.setAttribute("APP_PATH", request.getContextPath()); %> <%--
不以 / 开始的相对路径,找资源,以当前资源的路径为基准,经常容易出问题
以 / 开始的相对路径,找资源,以服务器的路径为标准 (http://localhost:8080),需要加上项目名
http://localhost:8080/crud
--%> <%-- 引入 jquery --%>
<script type="text/javascript" src="${APP_PATH}/static/js/jquery-1.12.4.min.js"></script>
<%-- 引入样式 --%>
<link rel="stylesheet" href="${APP_PATH}/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
<script src="${APP_PATH}/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<!-- 标题 -->
<!-- class="row" 为栅格系统的行 -->
<div class="row">
<!-- col-md-xx 数字代表列数 col-md-offset-xx 数字代表偏移-->
<div class="col-md-12">
<h1>SSM-CRUD</h1>
</div>
</div> <div class="row">
<div class="col-md-4 col-md-offset-8">
<button type="button" class="btn-primary">新建</button>
<button type="button" class="btn-danger">删除</button>
</div>
</div> <!-- 显示表格数据 -->
<div class="row">
<div class="col-md-12">
<!-- .table-hover 类可以让 <tbody> 中的每一行对鼠标悬停状态作出响应。
.table-striped 类可以给 <tbody> 之内的每一行增加斑马条纹样式。
-->
<table class="table table-hover table-striped">
<tr>
<th>#</th>
<th>lastName</th>
<th>email</th>
<th>gender</th>
<th>deptName</th>
<th>操作</th>
</tr> </table>
</div>
</div> <!-- 显示分页信息 -->
<div class="row">
<!-- 分页文字信息 -->
<div class="col-md-6"> </div>
<!-- 分页条信息 -->
<div class="col-md-6"> </div>
</div>
</div>
</body>
</html>

(3) EmployeeController 中添加返回 json 的映射请求

在浏览器中查看返回的 json 字符串

(4) 编写消息类

我们需要将返回的类封装到一个消息类中,以便获取更多的信息

com.study.crud.bean 中创建 Msg

public class Msg {

    public static final int CODE_SUCCESS = 100;
public static final int CODE_FAIL = 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(CODE_SUCCESS);
msg.setMsg("处理成功");
return msg;
} public static Msg fail() {
Msg msg = new Msg();
msg.setCode(CODE_FAIL);
msg.setMsg("处理失败");
return msg;
} // 向 Msg 中添加数据
public Msg add(String key, Object value) {
this.extend.put(key, value);
return this;
} 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;
}
}

改写 getEmpsWithJson()

查看改写后的映射请求返回的数据

(4) **编写 js **

添加 id 值

添加 js

<script type="text/javascript">
var lastPage, currentPage, totalRecord; // 1、页面加载完成以后,直接去发送一个 ajax 请求,要到分页数据
$(function () {
// 去首页
to_page(1);
}); function to_page(pn) {
$.ajax({
url:"${APP_PATH}/emps",
data:"pn=" + pn,
type:"get",
success:function (result) {
// console.log(result);
// 1、解析并显示员工数据
build_emps_table(result);
// 2、解析并显示分页信息
build_page_info(result);
// 3、解析显示分页条数据
build_page_nav(result);
}
});
} function build_emps_table(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 empIdTd = $("<td></td>").append(item.empId);
var empNameTd = $("<td></td>").append(item.empName);
var gender = item.gender == 'M' ? '男' : '女';
var genderTd = $("<td></td>").append(gender);
var emailTd = $("<td></td>").append(item.email);
var deptNameTd = $("<td></td>").append(item.department.deptName);
/**
* <button class="btn btn-primary btn-sm">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
编辑
</button>
*/
var editBtn = $("<button></button>").addClass("btn btn-primary btn-sm edit_btn")
.append($("<span></span>")).addClass("glyphicon glyphicon-pencil").append("编辑");
editBtn.attr("edit-id", item.empId);
var deleteBtn = $("<button></button>").addClass("btn btn-danger btn-sm delete_btn")
.append($("<span></span>")).addClass("glyphicon glyphicon-trash").append("删除");
deleteBtn.attr("del-id", item.empId);
var btnTd = $("<td></td>").append(editBtn).append(" ").append(deleteBtn); // append 方法执行完成以后还是返回原来的元素 所以可以链式操作
$("<tr></tr>").append(checkBoxTd)
.append(empIdTd)
.append(empNameTd)
.append(genderTd)
.append(emailTd)
.append(deptNameTd)
.append(btnTd)
.appendTo("#emps_table tbody");
})
} function build_page_info(result) {
$("#page_info_area").empty();
var pageInfo = result.extend.pageInfo;
$("#page_info_area").append("当前 " + pageInfo.pageNum + " 页,"
+ "总 " + pageInfo.pages + " 页,"
+ "总 " + pageInfo.total + " 条记录"
);
lastPage = pageInfo.pages;
totalRecord= pageInfo.total;
currentPage = pageInfo.pageNum;
} function build_page_nav(result) {
$("#page_nav_area").empty();
var pageInfo = result.extend.pageInfo;
var ul = $("<ul></ul>").addClass("pagination");
var firstPageLi = $("<li></li>").append($("<a></a>").append("首页"));
var prePageLi = $("<li></li>").append($("<a></a>").append("&laquo;"));
if (pageInfo.hasPreviousPage == false) {
firstPageLi.addClass("disabled");
prePageLi.addClass("disabled");
} else {
firstPageLi.click(function () {
to_page(1);
});
prePageLi.click(function () {
to_page(pageInfo.pageNum - 1);
});
}
ul.append(firstPageLi).append(prePageLi); $.each(result.extend.pageInfo.navigatepageNums, function (index, item) {
var numLi = $("<li></li>").append($("<a></a>").append(item));
if (pageInfo.pageNum == item) {
numLi.addClass("active");
}
numLi.click(function () {
to_page(item);
});
ul.append(numLi);
}); var nextPageLi = $("<li></li>").append($("<a></a>").append("&raquo;"));
var lastPageLi = $("<li></li>").append($("<a></a>").append("尾页"));
if (pageInfo.hasNextPage == false) {
nextPageLi.addClass("disabled");
lastPageLi.addClass("disabled");
} else {
nextPageLi.click(function () {
to_page(pageInfo.pageNum + 1);
});
lastPageLi.click(function () {
to_page(pageInfo.pages);
});
}
ul.append(nextPageLi).append(lastPageLi); var navEle = $("<nav></nav>").append(ul);
navEle.appendTo("#page_nav_area");
}
</script>

查看结果

4、业务逻辑

(1)新增员工信息

添加 员工添加的模态框

<!-- 员工添加的模态框 -->
<div class="modal fade" id="empAddModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">员工添加</h4>
</div>
<div class="modal-body">
<form class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">empName</label>
<div class="col-sm-10">
<input type="text" name="empName" class="form-control" id="empName_add_input" placeholder="empName">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">email</label>
<div class="col-sm-10">
<input type="text" name="email" class="form-control" id="email_add_input" placeholder="email@atguigu.com">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">gender</label>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="gender" id="gender1_add_input" value="M" checked="checked"> 男
</label>
<label class="radio-inline">
<input type="radio" name="gender" id="gender2_add_input" value="F"> 女
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">deptName</label>
<div class="col-sm-4">
<!-- 部门提交部门id即可 -->
<select class="form-control" name="dId">
</select>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" id="emp_save_btn">保存</button>
</div>
</div>
</div>
</div>

新建按钮处添加 id

编写 js

//清空表单样式及内容
function reset_form(ele) {
$(ele)[0].reset();
//清空表单样式
$(ele).find("*").removeClass("has-error has-success");
$(ele).find(".help-block").text("");
} function getDepts(ele) {
// 清空之前下拉列表的值
$(ele).empty();
$.ajax({
url: "${APP_PATH}/depts",
type: "get",
success:function (result) {
//{"code":100,"msg":"处理成功!",
//"extend":{"depts":[{"deptId":1,"deptName":"开发部"},{"deptId":2,"deptName":"测试部"}]}}
//console.log(result);
//显示部门信息在下拉列表中
//$("#empAddModal select").append("")
$.each(result.extend.depts,function(){
var optionEle = $("<option></option>").append(this.deptName).attr("value",this.deptId);
optionEle.appendTo(ele);
});
}
});
} // 点击新增按钮弹出模态框。
$("#emp_add_modal_btn").click(function () {
//清除表单数据(表单完整重置(表单的数据,表单的样式))
reset_form("#empAddModal form");
//s$("")[0].reset();
//发送ajax请求,查出部门信息,显示在下拉列表中
getDepts("#empAddModal select");
//弹出模态框
$("#empAddModal").modal({
backdrop:"static"
});
});

查看 添加模态框

创建 DepartmentController 映射 getDepts 请求

@Controller
public class DepartmentController { @Autowired
DepartmentService departmentService; @RequestMapping("/depts")
@ResponseBody
public Msg getDeptsWithJson() {
List<Department> depts = departmentService.getDepts();
return Msg.success().add("depts", depts);
}
} @Service
public class DepartmentService { @Autowired
DepartmentMapper departmentMapper; public List<Department> getDepts() {
return departmentMapper.selectByExample(null);
}
}

查看是否显示部门信息

(2)数据校验

**js校验 **

// 检验表单数据
function validate_add_form() {
var empName = $("#empName_add_input").val();
var regName = /(^[a-zA-Z0-9]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/;
if (!regName.test(empName)) {
show_validate_msg("#empName_add_input", "error", "用户名可以是 2-5 位中文或者 6-16 位英文和数字的组合");
return false;
} else {
show_validate_msg("#empName_add_input", "success", "");
} var email = $("#email_add_input").val();
var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
if (!regEmail.test(email)) {
show_validate_msg("#email_add_input", "error", "邮箱格式不正确");
return false;
} else {
show_validate_msg("#email_add_input", "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);
}
} // 校验用户名是否可用
$("#empName_add_input").change(function () {
var empName = this.value;
$.ajax({
url:"${APP_PATH}/checkuser",
data:"empName="+empName,
type:"post",
success:function (result) {
if (result.code == 100) {
show_validate_msg("#empName_add_input", "success", "用户名可用");
$("emp_save_btn").attr("ajax-va", "success");
} else {
show_validate_msg("#empName_add_input", "error", result.extend.va_msg);
$("emp_save_btn").attr("ajax-va", "error");
}
}
});
});

EmployeeController 中添加检验用户名映射请求

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

EmployeeService 中添加检验方法

public boolean checkUser(String empName) {
EmployeeExample example = new EmployeeExample();
EmployeeExample.Criteria criteria = example.createCriteria();
criteria.andEmpNameEqualTo(empName);
long count = employeeMapper.countByExample(example);
return count == 0;
}

查看校验结果


(3) 保存员工

添加 js

// 点击保存,保存员工
$("#emp_save_btn").click(function () {
// 1、模态框中填写的数据提交给服务器进行保存
// 1、先对要提交给服务器的数据进行校验
if (!validate_add_form()) {
return false;
}
// 1、判断之前的 ajax 用户名校验是否成功
if ($(this).attr("ajax-va") == "error") {
return false;
} // 2、发送 ajax 请求保存员工
$.ajax({
url:"${APP_PATH}/emp",
type:"post",
data:$("#empAddModal form").serialize(),
success:function(result) {
if (result.code == 100) {
// 员工保存成功
// 1、关闭模态框
$("#empAddModal").modal("hide");
// 2、来到最后一页,显示刚才保存的数据
if (totalRecord % 5 == 0) {
lastPage++;
currentPage = lastPage;
}
totalRecord++;
to_page(lastPage);
} else {
// 显示失败信息
if (undefined != result.extend.errorFields.email) {
// 显示邮箱错误信息
show_validate_msg("#email_add_input", "error", result.extend.errorFields.email);
}
if (undefined != result.extend.errorFields.empName) {
// 显示员工名字的错误信息
show_validate_msg("#empName_add_input", "error", result.extend.errorFields.empName);
}
}
}
});
});

Employee 中使用JSR303 数据校验

EmployeeController 中添加保存员工的映射请求

@RequestMapping(value = "/emp", method = RequestMethod.POST)
@ResponseBody
public Msg saveEmp(@Valid Employee employee, BindingResult result) {
if (result.hasErrors()) {
Map<String, Object> map = new HashMap<String, Object>();
List<FieldError> errors = result.getFieldErrors();
for (FieldError fieldError : errors) {
System.out.println("错误的字段名:" + fieldError.getField());
System.out.println("错误信息:" + fieldError.getDefaultMessage());
map.put(fieldError.getField(), fieldError.getDefaultMessage());
}
return Msg.fail().add("errorFields", map);
} else {
employeeService.saveEmp(employee);
return Msg.success();
}
}

EmployeeService 中添加保存员工的方法

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


(4) 修改员工信息

添加员工修改模态框

<!-- 员工修改的模态框 -->
<div class="modal fade" id="empUpdateModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">员工修改</h4>
</div>
<div class="modal-body">
<form class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">empName</label>
<div class="col-sm-10">
<p class="form-control-static" id="empName_update_static"></p>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">email</label>
<div class="col-sm-10">
<input type="text" name="email" class="form-control" id="email_update_input" placeholder="email@atguigu.com">
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">gender</label>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="gender" id="gender1_update_input" value="M" checked="checked"> 男
</label>
<label class="radio-inline">
<input type="radio" name="gender" id="gender2_update_input" value="F"> 女
</label>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">deptName</label>
<div class="col-sm-4">
<!-- 部门提交部门id即可 -->
<select class="form-control" name="dId">
</select>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" id="emp_update_btn">更新</button>
</div>
</div>
</div>
</div>

添加 js

// 1、如果在编辑和删除按钮创建之前绑定 click 是绑定不上的
// 1)可以在创建按钮的时候绑定
// 2)绑定点击 .live()
$(document).on("click", ".edit_btn", function () {
// 1、查处部门信息,并显示部门列表
getDepts("#empUpdateModal select");
// 2、查处员工信息,显示员工信息
getEmp($(this).attr("edit-id")); // 3、把员工的 id 传递给模态框的更新按钮
$("#emp_update_btn").attr("edit-id", $(this).attr("edit-id"));
$("#empUpdateModal").modal({
backdrop: "static"
});
}); function getEmp(id) {
$.ajax({
url:"${APP_PATH}/emp/" + id,
type:"get",
success:function (result) {
var empData = result.extend.emp;
$("#empName_update_static").text(empData.empName);
$("#email_update_input").val(empData.email);
$("#empUpdateModal input[name=gender]").val([empData.gender]);
$("#empUpdateModal select").val([empData.dId]);
}
});
} // 点击更新,更新员工信息
$("#emp_update_btn").click(function () {
// 验证邮箱是否合法
// 1、校验邮箱信息
var email = $("#email_update_input").val();
var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
if (!regEmail.test(email)) {
show_validate_msg("#email_update_input", "error", "邮箱格式不正确");
return false;
} else {
show_validate_msg("#email_update_input", "success", "");
} // 2、发送 ajax 请求保存更新的员工数据
$.ajax({
url:"${APP_PATH}/emp/" + $(this).attr("edit-id"),
type:"put",
data:$("#empUpdateModal form").serialize(),
success:function (result) {
$("#empUpdateModal").modal("hide");
to_page(currentPage);
}
});
});

EmployeeController 中添加按 id 获取员工的映射请求 和 更新员工的请求

@RequestMapping(value = "/emp/{id}", method = RequestMethod.GET)
@ResponseBody
public Msg getEmp(@PathVariable("id") Integer id) {
Employee employee = employeeService.getEmp(id);
return Msg.success().add("emp", employee);
} @RequestMapping(value = "/emp/{empId}", method = RequestMethod.PUT)
@ResponseBody
public Msg updateEmp(Employee employee, HttpServletRequest request) {
employeeService.updateEmp(employee);
return Msg.success();
}

EmployeeService 中添加 getEmp 方法和 update 方法

public Employee getEmp(Integer id) {
return employeeMapper.selectByPrimaryKeyWithDept(id);
} public void updateEmp(Employee employee) {
employeeMapper.updateByPrimaryKeySelective(employee);
}

查看效果

(5) 添加全选功能

添加全选按钮

添加 js

  // 完成全选、全不选功能
$("#check_all").click(function () {
$(".check_item").prop("checked", $(this).prop("checked"));
}); $(document).on("click", ".check_item", function () {
var flag = $(".check_item:checked").length == $(".check_item").length;
$("#check_all").prop("checked", flag);
});

添加删除功能

给删除按键添加 id

添加 js

  // 单个删除
$(document).on("click", ".delete_btn", function () {
var empName = $(this).parents("tr").find("td:eq(2)").text();
var empId = $(this).attr("del-id");
// 弹出是否确认删除对话框
if (confirm("确认删除【" + empName + "】吗?")) {
$.ajax({
url:"${APP_PATH}/emp/" + empId,
type:"delete",
success:function (result) {
alert(result.msg);
to_page(1);
}
});
}
}); // 点击全部删除、就批量删除
$("#emp_delete_all_btn").click(function () {
var empNames = "";
var del_ids = "";
$.each($(".check_item:checked"), function () {
empNames += $(this).parents("tr").find("td:eq(2)").text() + ",";
del_ids += $(this).parents("tr").find("td:eq(1)").text() + "-";
});
empNames = empNames.substring(0, empNames.length - 1);
del_ids = del_ids.substring(0, del_ids.length - 1);
if (confirm("确认删除【" + empNames + "】吗?")) {
$.ajax({
url:"${APP_PATH}/emp/" + del_ids,
type:"delete",
success:function (result) {
alert(result.msg);
to_page(1);
}
});
}
});

EmployeeController 中添加删除方法

@RequestMapping(value = "/emp/{ids}", method = RequestMethod.DELETE)
@ResponseBody
public Msg deleteEmp(@PathVariable("ids") String ids) {
// 批量删除
if (ids.contains("-")) {
List<Integer> del_ids = new ArrayList<Integer>();
String[] str_ids = ids.split("-");
for (String str : str_ids) {
del_ids.add(Integer.parseInt(str));
}
employeeService.deleteBatch(del_ids);
} else {
Integer id = Integer.parseInt(ids);
employeeService.deleteEmp(id);
}
return Msg.success();
}

EmployeeService中添加单个删除和多个删除的方法

public void deleteBatch(List<Integer> del_ids) {
EmployeeExample example = new EmployeeExample();
EmployeeExample.Criteria criteria = example.createCriteria();
criteria.andEmpIdIn(del_ids);
employeeMapper.deleteByExample(example);
} public void deleteEmp(Integer id) {
employeeMapper.deleteByPrimaryKey(id);
}

查看效果



四、总结

SSM-员工管理项目实战-CRUD-增删改查的更多相关文章

  1. 使用ASP.NET Core MVC 和 Entity Framework Core 开发一个CRUD(增删改查)的应用程序

    使用ASP.NET Core MVC 和 Entity Framework Core 开发一个CRUD(增删改查)的应用程序 不定时更新翻译系列,此系列更新毫无时间规律,文笔菜翻译菜求各位看官老爷们轻 ...

  2. django之创建第8-1个项目-数据库之增删改查/数据库数据显示在html页面

    1.为test.DB数据库预先创建下面数据 1    张三    16    2015-01-02    12    李四    17    2015-01-04    13    王五    14  ...

  3. 第二百七十六节,MySQL数据库,【显示、创建、选定、删除数据库】,【用户管理、对用户增删改查以及授权】

    MySQL数据库,[显示.创建.选定.删除数据库],[用户管理.对用户增删改查以及授权] 1.显示数据库 SHOW DATABASES;显示数据库 SHOW DATABASES; mysql - 用户 ...

  4. 前端的CRUD增删改查的小例子

    前端的CRUD增删改查的小例子 1.效果演示 2.相关代码: <!DOCTYPE html> <html lang="en"> <head> & ...

  5. yii2-basic后台管理功能开发之二:创建CRUD增删改查

    昨天实现了后台模板的嵌套,今天我们可以试着创建CRUD模型啦 刚开始的应该都是“套用”,不再打算细说,只把关键的地方指出来. CRUD即数据库增删改查操作.可以理解为yii2为我们做了一个组件,来实现 ...

  6. 创建支持CRUD(增删改查)操作的Web API(二)

    一:准备工作 你可以直接下载源码查看 Download the completed project.     下载完整的项目 CRUD是指“创建(C).读取(R).更新(U)和删除(D)”,它们是四个 ...

  7. 【EF6学习笔记】(二)操练 CRUD 增删改查

    本篇原文链接: Implementing Basic CRUD Functionality 说明:学习笔记参考原文中的流程,为了增加实际操作性,并能够深入理解,部分地方根据实际情况做了一些调整:并且根 ...

  8. EF6 学习笔记(二):操练 CRUD 增删改查

    EF6学习笔记总目录 ASP.NET MVC5 及 EF6 学习笔记 - (目录整理) 接上篇: EF6 学习笔记(一):Code First 方式生成数据库及初始化数据库实际操作 本篇原文链接: I ...

  9. SSM框架入门——整合SSM并实现对数据的增删改查功能(Eclipse平台)

    一.搭建框架环境 整个项目结构如下: 搭建SSM步骤如下: (1)准备好三大框架的jar包,如图所示 (2)在Eclipse中创建一个web project ,并把这些jar包粘贴到lib文件夹中. ...

  10. MybatisPlus核心功能——实现CRUD增删改查操作 (包含条件构造器)

    CRUD 官方文档:https://baomidou.com/ (建议多看看官方文档,每种功能里面都有讲解)[本文章使用的mybatisplus版本为3.5.2] 条件构造器 一般都是用service ...

随机推荐

  1. js & touch & swiper

    js & touch & swiper https://developer.mozilla.org/en/docs/Web/API/Touch_events "use str ...

  2. Baccarat凭什么能成为DeFi后时代火爆新趋势?

    在各币种经历涨涨跌跌以后,DeFi后时代已然来临.那么,当前DeFi市场中哪个项目更被市场生态建设者看好呢?毫无疑问,Baccarat会成为最被看好的DeFi项目. Baccarat采用了独特的共识算 ...

  3. NGK数字钱包的特点是什么?NGK钱包的优点和缺点是什么?

    说起区块链数字资产,那就离不开谈到数字钱包.数字钱包不仅有资产管理的功能,还可以进行资产理财.资产交易,甚至能为公链DAPP导流. 对于NGK公链而言,其数字钱包已然成为了解NGK公链的基础条件.NG ...

  4. AtCoder Beginner Contest 192 F - Potion

    题目链接 点我跳转 题目大意 给定 \(N\) 个物品和一个 \(X\) ,第 \(i\) 个物品的重量为 \(ai\),你可以从中选择任意个物品(不能不选) 假定选择了 \(S\) 个物品,物品的总 ...

  5. GridSearchCV网格搜索得到最佳超参数, 在K近邻算法中的应用

    最近在学习机器学习中的K近邻算法, KNeighborsClassifier 看似简单实则里面有很多的参数配置, 这些参数直接影响到预测的准确率. 很自然的问题就是如何找到最优参数配置? 这就需要用到 ...

  6. http server源码解析

    本文主要过下http生成服务和处理请求的主要流程,其他功能并未涉及. 使用例子 const http = require('http'); http.createServer((req, res) = ...

  7. 求幂&&快速幂&&位运算

    1.普通的求幂方法: 时间复杂度为O(n),对于比较大的数在1s限时内可能会TLE int pow(int base,int p){ int ans=1; for(int i=1;i<=p;i+ ...

  8. OAuth2.0安全设计之Authorization Code

    OAuth 2.0 有 4 种认证流程: 授权码模式(authorization code) 简化模式(implicit) 密码模式(resource owner password credentia ...

  9. 优化程序性能(CSAPP)

    [前言]虽然现在没有接触过大型项目,但是工作了会注重性能.学习一下,应该能更好更快的理解别人写的经典优化的代码.结合CSAPP和自己的理解,总结一下. 一.程序优化综述 1.高效程序的特点 (1)适当 ...

  10. Win命令行切换Python版本

    目录 安装2.x 和 3.x 的python 设置系统环境变量 pip的使用 参考 安装2.x 和 3.x 的python 我这里使用anaconda来安装两个版本的python包. conda cr ...