SSM-CRUD
ssm:SpringMVC+Spring+Mybatis
CRUD:Create(创建)、Retrieve(查询)、Update(更新)、Delete(删除)
功能点:
- 分页
- 数据校验
- JQuery前端校验+JSR303后端校验
- ajax
- Rest风格的URI;使用HTTP协议请求方式的动词,来表示对资源的操作(GET(查询)、POST(新增)、PUT(修改)、DELETE(删除))
技术点
- 基础框架-SSM(SpringMVC+Spring+Mybatis)
- 数据库-MySQL
- 前端框架-BootStrap快速搭建简洁美观的界面
- 项目的依赖管理-Maven
- 分页-pagehelper
- 逆向工程-Mybatis Generator
路径问题:
不以/开始的相对路径,找资源,以当前资源的路径为准,经常容易出现问题
以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:8080/)需要加上项目名
http://localhost:8080/ssm_crud
基础环境搭建
创建一个maven工程
可以看到我们的 dreamland-web 子工程没有 java、resources 和 test 目录,我们需要手动创建一下,操作过程如下:
main -> New -> Directory ==> 创建 java
main -> New -> Directory ==> 创建 resources
src -> New -> Directory ==> 创建 test
test -> New -> Directory ==> 创建 java
test -> New -> Directory ==> 创建 resources
然后对创建好的 java、resources 和 test/java、test/resourcs 目录均右键选择 Mark Diretory as,然后分别进行如下操作:
java -> Sources Root //java源码根目录
resources -> Resources Root//java 配置文件目录
test/java -> Test Sources Root//java 测试源码目录
test/java -> Test Sources Root//java 测试配置文件目录
引入项目依赖的jar包
- spring
- springmvc
- mybatis
- 数据库连接池,驱动包
- 其他(jstl、servlet-api、junit)
<dependencies>
<!--引入项目依赖的jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.2</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--Mybatis整合Spring的适配包-->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!--数据库连接池、驱动-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!--jstl、servlet-api、junit-->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
引入BootStrap前端框架
-
在官方网站下载BookStrap 样式所需css、js
-
下载jQuery,因jQuery官网访问一般容易出现问题,可以在这个网站下载jQuery
-
引入Bootstrap
-
<% pageContext.setAttribute("APP_PATH",request.getContextPath()); %> <%-- 不以/开始的相对路径,找资源,以当前资源的路径为准,经常容易出现问题 以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:8080/)需要加上项目名 http://localhost:8080/ssm_crud --%> <link type="text/css" rel="stylesheet" href="${APP_PATH}/static/bootstrap/css/bootstrap.css"> <%--引入jquery--%> <script src="${APP_PATH}/static/js/jquery-3.5.1.js"></script> <script src="${APP_PATH}/static/bootstrap/js/bootstrap.js"></script>
-
测试结果
可以看到这个按钮已经发生变化了
-
可能会出现以下错误:
bootstrap.min.js:6 Uncaught Error: Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4
原因:Bootstrap的JavaScript要求jQuery 1.9.1或更高版本,但低于版本4
编写SSM整合的关键配置文件
web.xml、spring、springmvc、mybatis
web.xml(web.xml)
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.1"
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_3_1.xsd">
<display-name>Archetype Created Web Application</display-name>
<!--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的前端控制器,拦截所有请求-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<!-- <init-param>-->
<!-- <param-name>contextConfigLocation</param-name>-->
<!--不指定位置时,默认就是类路径下servlet-name后加上-servlet-->
<!-- <param-value>classpath:</param-value>-->
<!-- </init-param>-->
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--字符编码过滤器,放到其他过滤器前面-->
<!--3、配置字符编码器-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<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>
</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/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/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--SpringMVC的配置文件,包含网站跳转逻辑,配置,use-default-filters="false"禁用默认行为,只扫描Controller注解-->
<context:component-scan base-package="com.qykhhr.crud" 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不能处理的请求交给Tomcat-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
<!--能支持SpringMVC更高级的一些功能,比如JSR303校验,快捷的ajax请求,映射动态请求-->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
Spring(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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--Spring的配置文件,这里主要配置和业务逻辑有关的-->
<!--Spring配置文件的核心点:数据源、与Mybatis的整合、事务控制-->
<context:component-scan base-package="com.qykhhr">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<context:property-placeholder location="classpath:dbconfig.properties"></context:property-placeholder>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--==============配置和mybatis的整合==============-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--指定Mybatis全局配置文件的位置-->
<property name="configLocation" value="classpath: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.qykhhr.crud.dao"></property>
</bean>
<!--==========================================================================-->
<!--==============事务控制的配置==============-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--控制住数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启基于注解的事务,使用xml配置形式的事务(必要主要的都是使用配置式)-->
<aop:config>
<!--切入点表达式-->
<aop:pointcut id="txPoint" expression="execution(* com.qykhhr.crud.service..*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"></aop:advisor>
</aop:config>
<!--配置事务增强,事务如何切入-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--所有方法都是事务方法-->
<tx:method name="*"/>
<!--以get开始的所有方法-->
<tx:method name="get*" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
<!--========================================================-->
</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.qykhhr.crud.bean"/>
</typeAliases>
</configuration>
使用mybatis的逆向工程生成对应的bean以及mapper
首先导包mybatis-generator-core
<!--MBG,mybatis逆向工程-->
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.4.0</version>
</dependency>
在当前工程路径下编写generatorConfig.xml(mgb.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">
<!--逆向生成的bean以及mapper没有注释-->
<commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!--配置数据库连接-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/ssm_crud?useSSL=false"
userId="root"
password="614310">
</jdbcConnection>
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!--指定javaBean生成位置-->
<javaModelGenerator targetPackage="com.qykhhr.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接口生成的位置:mapper接口-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.qykhhr.crud.dao"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!--指定每个表的生成策略-->
<table tableName="tbl_emp" domainObjectName="Employee"></table>
<table tableName="tbl_dept" domainObjectName="Department"></table>
</context>
</generatorConfiguration>
在编写一个测试类,运行
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
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就会自动生成
Department.java、DepartmentExample.java、Employee.java、EmployeeExample.java
DepartmentMapper.java、EmployeeMapper.java
DepartmentMapper.xml、EmployeeMapper.xml
5.在执行main方法的时候出现了报错
javax.net.ssl.SSLException MESSAGE: closing inbound before receiving peer's close_notify
解决办法:
设置useSSL为false
<!--配置数据库连接--> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/ssm_crud?useSSL=false" userId="root" password="614310"> </jdbcConnection>
修改mapper文件,添加自己需要的
Employee.java添加
//希望查询员工的同时,部门信息也是查询好的
private Department department;
EmployeeMapper.java添加
List<Employee> selectByExampleWithDept(EmployeeExample example);
Employee selectByPrimaryKeyWithDept(Integer empId);
EmployeeMapper.xml添加
<resultMap id="WithDeptResultMap" type="com.qykhhr.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.qykhhr.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>
测试mapper
package com.qykhhr.crud.test;
import com.qykhhr.crud.dao.DepartmentMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* 测试dao层工作
* 推荐Spring的项目就可以使用Spring的单元测试,可以自动注入我们需要的组件
*
* 1、导入SpringTest模块
* 2、@ContextConfiguration指定Spring配置文件的位置
* 3、直接autowired要使用的组件即可
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class MapperTest {
@Autowired
DepartmentMapper departmentMapper;
/**
* 测试DepartmentMapper
*/
@Test
public void testCRUD(){
// //1、创建SpringIOC容器
// ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
// //2、从容器中获取mapper
// DepartmentMapper bean = ioc.getBean(DepartmentMapper.class);
System.out.println(departmentMapper);
}
}
测试出现的问题:Caused by: java.lang.IllegalArgumentException: Result Maps collection already contains value for com.qykhhr.crud.dao.EmployeeMapper.BaseResultMap
原因:使用MyBatis Generator逆向生成mapper的时候,如果多次生成会造成mapper内容重复生成,然后mapper里面的id出现了冲突
解决:在mapper文件中找到相同的id,会发现后面有一半内容和前面相同,将后面相同的内容删除就行了
查询
- 访问index.jsp
- index.jsp页面发送查询员工列表的请求
- EmployeeController来接收请求,查出员工数据
- 来到list.jsp页面进行展示
- 引入PageHelper分页插件
-
导包
-
<!--pagehelper分页插件--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.2.0</version> </dependency>
-
-
在mybatis配置文件(mybatis-config.xml)中进行注册
-
<!--注册分页插件--> <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> </plugin> </plugins>
-
-
在Controller中使用
-
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.qykhhr.crud.bean.Employee; import com.qykhhr.crud.service.EmployeeService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Repository; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import java.util.List; /** * 处理员工CRUD请求 */ @Controller public class EmployeeController { @Autowired EmployeeService employeeService; /** * 查询员工数据(分页查询) * @return */ @RequestMapping("/emps") public String getEmps(@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo, Model model){ //引入PageHelper分页插件 //在查询之前只需要调用:传入页码以及每页的大小 PageHelper.startPage(pageNo,5); //startPage后面紧跟的这个查询就是一个分页查询 List<Employee> emps = employeeService.getAll(); //使用PageInfo包装查询后的结果,只需要将pageInfo交给页面就行了 //封装了详细的分页信息,包括我们查询出来的数据,传入连续显示的页数 PageInfo page = new PageInfo(emps,5); model.addAttribute("pageInfo",page); return "list"; } }
-
list页面的编写
路径问题:
不以/开始的相对路径,找资源,以当前资源的路径为准,经常容易出现问题
以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:8080/)需要加上项目名
http://localhost:8080/ssm_crud
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
Created by IntelliJ IDEA.
User: 雨林
Date: 2021/1/12
Time: 13:16
To change this template use File | Settings | File Templates.
--%>
<%@ 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/ssm_crud
--%>
<link type="text/css" rel="stylesheet" href="${APP_PATH}/static/bootstrap/css/bootstrap.css">
<%--引入jquery--%>
<script src="${APP_PATH}/static/js/jquery-3.5.1.js"></script>
<script src="${APP_PATH}/static/bootstrap/js/bootstrap.js"></script>
</head>
<body>
<%--搭建显示页面--%>
<div class="container">
<%--标题SSM-CRUD--%>
<div class="row">
<div class="col-md-12">
<h1>SSM-CRUD</h1>
</div>
</div>
<%--按钮--%>
<div class="row">
<div class="col-md-4 col-md-offset-8">
<button class="btn btn-primary">新增</button>
<button class="btn btn-danger">删除</button>
</div>
</div>
<%--显示表格数据--%>
<div class="row">
<div class="col-md-12">
<table class="table table-hover">
<tr>
<th>#</th>
<th>empName</th>
<th>gender</th>
<th>email</th>
<th>deptName</th>
<th>操作</th>
</tr>
<c:forEach items="${pageInfo.list}" var="emp">
<tr>
<th>${emp.empId}</th>
<th>${emp.empName}</th>
<th>${emp.gender=="M"?"男":"女"}</th>
<th>${emp.email}</th>
<th>${emp.department.deptName}</th>
<th>
<button class="btn btn-primary btn-sm">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
编辑
</button>
<button class="btn btn-danger btn-sm">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
删除
</button>
</th>
</tr>
</c:forEach>
</table>
</div>
</div>
<%--显示分页信息--%>
<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?pageNo=1">首页</a></li>
<c:if test="${pageInfo.hasPreviousPage}">
<li>
<a href="${APP_PATH}/emps?pageNo=${pageInfo.pageNum-1}" aria-label="Previous">
<span aria-hidden="true">«</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?pageNo=${page_Num}">${page_Num}</a></li>
</c:if>
</c:forEach>
<c:if test="${pageInfo.hasNextPage}">
<li>
<a href="${APP_PATH}/emps?pageNo=${pageInfo.pageNum+1}" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</c:if>
<li><a href="${APP_PATH}/emps?pageNo=${pageInfo.pages}">末页</a></li>
</ul>
</nav>
</div>
</div>
</div>
</body>
</html>
展示效果
查询-ajax
- index.jsp页面直接发送Ajax请求进行员工分页数据的查询
- 服务器将查出的数据,以json字符串的形式返回给浏览器
- 浏览器收到json字符串。可以使用js对json进行解析,使用js通过dom增删改改变页面
- 返回json。实现客户端的无关性
-
可以使用@ResponseBody注解将数据以json形式返回到页面
需要导入jackson支持json包
<!--@RequestBody将数据转为json需要包--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.3</version> </dependency>
-
编写一个通用的返回json的类
package com.qykhhr.crud.bean; import java.util.HashMap; import java.util.Map; /** * 通用的返回json的类 */ public class Msg { private int code;//状态码:100-成功 200-失败 private String msg;//提示信息 private Map<String,Object> extend = new HashMap<>();//装载要发送到页面的json数据 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);//将获取到的数据添加到map中 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; } }
-
在控制器中将数据以json形式发送到前端页面
/** * 将数据以json的形式传到页面 * 需要导入jackson包 * @param pageNo * @param model * @return */ @RequestMapping("/emps") @ResponseBody public Msg getEmpWithJson(@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo, Model model){ //引入PageHelper分页插件 //在查询之前只需要调用:传入页码以及每页的大小 PageHelper.startPage(pageNo,5); //startPage后面紧跟的这个查询就是一个分页查询 List<Employee> emps = employeeService.getAll(); //使用PageInfo包装查询后的结果,只需要将pageInfo交给页面就行了 //封装了详细的分页信息,包括我们查询出来的数据,传入连续显示的页数 PageInfo page = new PageInfo(emps,5); return Msg.success().add("pageInfo",page); }
-
前端对数据进行处理
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%-- Created by IntelliJ IDEA. User: 雨林 Date: 2021/1/12 Time: 13:16 To change this template use File | Settings | File Templates. --%> <%@ 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/ssm_crud --%> <link type="text/css" rel="stylesheet" href="${APP_PATH}/static/bootstrap/css/bootstrap.css"> <%--引入jquery--%> <script src="${APP_PATH}/static/js/jquery-3.5.1.js"></script> <script src="${APP_PATH}/static/bootstrap/js/bootstrap.js"></script> </head> <body> <%--搭建显示页面--%> <div class="container"> <%--标题SSM-CRUD--%> <div class="row"> <div class="col-md-12"> <h1>SSM-CRUD</h1> </div> </div> <%--按钮--%> <div class="row"> <div class="col-md-4 col-md-offset-8"> <button class="btn btn-primary">新增</button> <button class="btn btn-danger">删除</button> </div> </div> <%--显示表格数据--%> <div class="row"> <div class="col-md-12"> <table class="table table-hover" id="emps_table"> <thead> <tr> <th>#</th> <th>empName</th> <th>gender</th> <th>email</th> <th>deptName</th> <th>操作</th> </tr> </thead> <tbody> </tbody> </table> </div> </div> <%--显示分页信息--%> <div class="row"> <%--分页文字信息--%> <div class="col-md-6" id="page_info_area"> </div> <%--分页条信息--%> <div class="col-md-6" id="page_nav_area"> </div> </div> </div> <script type="text/javascript"> //页面加载完成之后直接去第1页 //1、页面加载完成以后,直接去发送ajax请求,要到分页数据 $(function(){ $.ajax({ url:"${APP_PATH}/emps", data:"pageNo=1", type:"GET", success:function (result) { //console.log(result); //1.解析并显示员工数据 build_emps_table(result); //2.解析并显示分页信息 build_page_info(result); //3、解析显示分页条 build_emps_nav(result); } }); }); //跳转到指定页面 function to_page(pageNo) { $.ajax({ url:"${APP_PATH}/emps", data:"pageNo="+pageNo, type:"GET", success:function (result) { //console.log(result); //1.解析并显示员工数据 build_emps_table(result); //2.解析并显示分页信息 build_page_info(result); //3、解析显示分页条 build_emps_nav(result); } }); } /*解析显示分页数据*/ function build_emps_table(result){ //在发送每个Ajax请求的时候都清空表格内容 $("#emps_table tbody").empty(); var emps = result.extend.pageInfo.list;//分页的员工的信息 $.each(emps,function (index,item) { var empIdTd = $("<td></td>").append(item.empId); var empNameTd = $("<td></td>").append(item.empName); var genderTd = $("<td></td>").append(item.gender=='M'?"男":"女"); var emailTd = $("<td></td>").append(item.email); var deptNameTd = $("<td></td>").append(item.department.deptName); var editBtn = $("<button></button>").addClass("btn btn-primary btn-sm") .append($("<span></span>").addClass("glyphicon glyphicon-pencil")).append("编辑"); var delBtn = $("<button></button>").addClass("btn btn-danger btn-sm") .append($("<span></span>").addClass("glyphicon glyphicon-trash")).append("删除"); var btnTd = $("<td></td>").append(editBtn).append(" ").append(delBtn); //append方法执行完成以后还是返回原来的元素,这里每次append都会产生新的<tr></tr> $("<tr></tr>").append(empIdTd).append(empNameTd) .append(genderTd).append(emailTd).append(deptNameTd) .append(btnTd) .appendTo("#emps_table tbody"); }) } /*解析显示分页信息*/ function build_page_info(result){ //在发送Ajax的请求时要清空里面的内容,要不然就会追加内容 $("#page_info_area").empty(); $("#page_info_area").append("当前第"+result.extend.pageInfo.pageNum+"页,总" +result.extend.pageInfo.pages+"页,总"+result.extend.pageInfo.total+"条记录") } /*解析显示分页条*/ function build_emps_nav(result){ $("#page_nav_area").empty(); //$("#page_nav_area") var ul = $("<ul></ul>").addClass("pagination"); //构建元素 var firstPageLi = $("<li></li>").append($("<a></a>").append("首页").attr("href","#")); var prePageLi = $("<li></li>").append($("<a></a>").append("«")); //判断如果没有上一页,就不能点击 if(result.extend.pageInfo.hasPreviousPage == false){ firstPageLi.addClass("disabled"); prePageLi.addClass("disabled"); }else{ //为元素添加点击翻页的事件 firstPageLi.click(function(){ to_page(1); }); prePageLi.click(function(){ to_page(result.extend.pageInfo.pageNum-1) }); } var nextPageLi = $("<li></li>").append($("<a></a>").append("»")); var lastPageLi = $("<li></li>").append($("<a></a>").append("末页").attr("href","#")); //判断如果没有下一页,就不能点击 if(result.extend.pageInfo.hasNextPage == false){ nextPageLi.addClass("disabled"); lastPageLi.addClass("disabled"); }else{ lastPageLi.click(function(){ to_page(result.extend.pageInfo.pages); }); nextPageLi.click(function(){ to_page(result.extend.pageInfo.pageNum+1) }); } //添加首页和前一页的提示 ul.append(firstPageLi).append(prePageLi); //遍历给ul中添加页码提示 $.each(result.extend.pageInfo.navigatepageNums,function(index,item){ var numLi = $("<li></li>").append($("<a></a>").append(item)); if(result.extend.pageInfo.pageNum == item){ numLi.addClass("active"); } numLi.click(function(){ to_page(item); }); ul.append(numLi); }) //添加末页的提示 ul.append(nextPageLi).append(lastPageLi); //把ul加入到nav var navEle = $("<nav></nav>").append(ul); navEle.appendTo("#page_nav_area"); } </script> </body> </html>
reasonable
:分页合理化参数,默认值为false
。当该参数设置为true
时,pageNum<=0
时会查询第一页,pageNum>pages
(超过总数时),会查询最后一页。默认false
时,直接根据参数进行查询。<!--注册分页插件--> <plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!--分页参数合理化,--> <property name="reasonable" value="true"/> </plugin> </plugins>
新增
- 在index.jsp页面点击“新增”
- 弹出新增对话框
- 去数据库查询部门列表,显示在对话框中
- 用户输入数据,并进行校验
-
创建员工添加模态框
员工信息添加的表单
<%--员工添加的模态框--%> <!-- Modal --> <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">×</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"> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">email</label> <div class="col-sm-10"> <input type="email" name="email" class="form-control" id="email_add_input" placeholder="email@qykhhr.com"> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">gender</label> <div class="col-sm-10"> <label class="radio-inline"> <input type="radio" name="gender" id="gender1_add_input" value="M" checked="checked"> 男 </label> <label class="radio-inline"> <input type="radio" name="gender" id="gender2_add_input" value="F"> 女 </label> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">deptName</label> <div class="col-sm-4"> <%--部门提交部门id即可--%> <select name="dId" class="form-control" id="dept_add_select"> </select> </div> </div> </form> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button> <button type="button" class="btn btn-primary" id="emp_save_btn">保存</button> </div> </div> </div> </div>
<script> $("#emp_add_modal_btn").click(function(){ $("#empAddModal").modal({ backdrop:"static" }); }); </script>
-
从数据库中获取部门信息,显示到添加员工的模态框中
- 先创建一个service
import com.qykhhr.crud.bean.Department; import com.qykhhr.crud.dao.DepartmentMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class DepartmentService { @Autowired private DepartmentMapper departmentMapper; public List<Department> getDepts() { List<Department> departments = departmentMapper.selectByExample(null); return departments; } }
-
再创建一个Controller
import com.qykhhr.crud.bean.Department; import com.qykhhr.crud.bean.Msg; import com.qykhhr.crud.service.DepartmentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; /** * 处理和部门有关的请求 */ @Controller public class DepartmentController { @Autowired private DepartmentService departmentService; /** * 返回所有的部门信息 */ @RequestMapping("/depts") @ResponseBody public Msg getDepts(){ //查出的所有部门信息 List<Department> list = departmentService.getDepts(); return Msg.success().add("depts",list); } }
-
在index.jsp发送Ajax请求
<script> //点击新增按钮弹出模态框 $("#emp_add_modal_btn").click(function(){ $("#dept_add_select").empty(); //发送Ajax请求,查出部门信息,显示在下拉列表中 getDepts(); //弹出模态框 $("#empAddModal").modal({ backdrop:"static" }); }); //查出所有的部门信息并显示在下拉列表中 function getDepts(){ $.ajax({ url:"${APP_PATH}/depts", type:"GET", success:function (result) { //console.log(result); $.each(result.extend.depts,function(){ var optionEle = $("<option></option>").append(this.deptName).attr("value",this.deptId); optionEle.appendTo("#dept_add_select"); }); } }); } </script>
URI:
/emp/{id} GET 查询员工
/emp POST 保存员工
/emp/{id} PUT 修改员工
/emp/{id} DELETE 删除员工
保存员工信息
在index.jsp中发送Ajax请求
<script>
$("#emp_save_btn").click(function(){
//1、模态框中填写的表单数据提交给服务器进行保存
//2、发送Ajax请求保存员工
$.ajax({
url:"${APP_PATH}/emp",
type:"POST",
data:$("#empAddModal form").serialize(),
success:function () {
//员工保存之后
//1、关闭模态框
$("#empAddModal").modal('hide');
// $("#empAddModal input").empty();
//2、来到最后一页,显示刚才保存的数据
//发送Ajax请求显示最后一页数据即可
to_page(totalRecord);
}
})
});
</script>
在EmployeeController中保存员工信息
/**
* 定义员工返回
* @return
*/
@RequestMapping(value = "/emp",method = RequestMethod.POST)
public String saveEmp(Employee employee){
employeeService.saveEmp(employee);
return "/**
* 定义员工返回
* @return
*/
@ResponseBody
@RequestMapping(value = "/emp",method = RequestMethod.POST)
public Msg saveEmp(Employee employee){
employeeService.saveEmp(employee);
// return "lastPage";
return Msg.success();
}lastPage";
}
对输入的员工信息进行正则表达式校验
- 进行校验,并以弹窗的形式输出错误信息
//校验表单数据
function validate_add_form(){
//1、拿到要校验的数据,使用正则表达式进行校验
var empName = $("#empName_add_input").val();
var regName = /(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/;
if(!regName.test(empName)){//如果校验失败,提示
alert("用户名可以是2-5位中文或者6-16位英文和数字的组合");
return false;
}
//校验邮箱格式
var email = $("#email_add_input").val();
var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
if(!regEmail.test(email)){
alert("邮箱格式不正确");
return false;
}
return true;
}
//点击保存员工的Ajax请求
$("#emp_save_btn").click(function(){
//1、模态框中填写的表单数据提交给服务器进行保存
//还需要对将要提交给服务器的数据进行校验
if( !validate_add_form()){
return false;
}
//2、发送Ajax请求保存员工
$.ajax({
url:"${APP_PATH}/emp",
type:"POST",
data:$("#empAddModal form").serialize(),
success:function () {
//员工保存之后
//1、关闭模态框
$("#empAddModal").modal('hide');
// $("#empAddModal input").empty();
//2、来到最后一页,显示刚才保存的数据
//发送Ajax请求显示最后一页数据即可
to_page(totalRecord);
}
})
});
- 使用正则表达式校验信息,将错误信息放到输入框下面,并将输入框变成红色
//校验表单数据
function validate_add_form(){
//1、拿到要校验的数据,使用正则表达式进行校验
var empName = $("#empName_add_input").val();
var regName = /(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/;
if(!regName.test(empName)){//如果校验失败,提示
// alert("用户名可以是2-5位中文或者6-16位英文和数字的组合");
show_validate_msg("#empName_add_input","error","用户名可以是2-5位中文或者6-16位英文和数字的组合");
// $("#empName_add_input").parent().addClass("has-error");
// $("#empName_add_input").next("span").text("用户名可以是2-5位中文或者6-16位英文和数字的组合");
return false;
}else {
// $("#empName_add_input").parent().addClass("has-success");
// $("#empName_add_input").next("span").text("");
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)){
//alert("邮箱格式不正确");
// $("#email_add_input").parent().addClass("has-error");
// $("#email_add_input").next("span").text("邮箱格式不正确");
show_validate_msg("#email_add_input","error","邮箱格式不正确");
return false;
}else {
// $("#email_add_input").parent().addClass("has-success");
// $("#email_add_input").next("span").text("");
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 if("error"==status){
$(ele).parent().addClass("has-error");
$(ele).next("span").text(msg);
}
}
-
使用Ajax校验用户名是否重复,从数据库中查询
使用$("#empAddModal form")[0].reset();//清除表单数据(表单重置)
-
在index.jsp页面当输入框发生变化,就发送Ajax请求,到数据库中查看新增用户名是否可用
//当用户名输入框发生变化,发送Ajax请求校验新增用户名是否可用 $("#empName_add_input").change(function(){ //发送Ajax请求校验用户名是否可用 var empName = this.value; $.ajax({ url:"${APP_PATH}/checkuser", data:"empName="+empName, type:"POST", success:function (result) { if(result.code==100){ show_validate_msg("#empName_add_input","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"); } } }); })
-
Controller接收/checkuser请求,去service层调用方法
/** * 用于查看用户名是否重复 * @param empName * @return */ @ResponseBody @RequestMapping("/checkuser") public Msg checkuser(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","用户名不可用"); } }
-
在Service层创建一个方法,用于查看数据库中是否有该用户名
/** * 校验用户名是否可用 * @param empName * @return true 表示可用 false 表示不可用 */ 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;//如果count == 0 表示可用 }
清除表单样式
//清除表单数据(表单完整重置(表单的数据,表单的样式))
function reset_form(ele){
$(ele)[0].reset();
//清空表单样式
$(ele).find("*").removeClass("has-error has-success");
$(ele).find(".help-block").text("");
}
//点击新增按钮弹出模态框
$("#emp_add_modal_btn").click(function(){
$("#dept_add_select").empty();
//清除表单数据(表单重置)
reset_form("#empAddModal form");
// $("#empAddModal form").reset();
//发送Ajax请求,查出部门信息,显示在下拉列表中
getDepts();
//弹出模态框
$("#empAddModal").modal({
backdrop:"static"
});
});
问题
前端校验可以通过修改页面内容或者通过禁用js功能,跳过js校验,所以我们需要进行后端校验
重要数据(后端校验(JSR303),唯一约束)
通过修改页面内容,实现跳过前端校验
使用JSR303校验
-
导包,导入Hibernate-Validator
<!--JSR303校验支持:tomcat7及以上的服务器 tomcat7及以下的服务器:el表达式。额外给服务器的lib包中替换新的标准的el --> <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator --> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>7.0.0.Final</version> </dependency>
-
在bean中修改属性
@Pattern(regexp = "(^[a-zA-Z0-9_-]{6,16}$)|(^[\\u2E80-\\u9FFF]{2,5})" ,message = "用户名必须是2-5位中文或者6-16位英文和数字的组合") private String empName; //@Email @Pattern(regexp = "^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$" ,message = "邮箱格式不正确") private String email;
-
对前端传来的数据进行JSR303校验
@Valid Employee employee, BindingResult result
/** * 员工保存 * 1、支持JSR303校验 * 2、导入Hibernate-Validator * @return */ @ResponseBody @RequestMapping(value = "/emp",method = RequestMethod.POST) public Msg saveEmp(@Valid Employee employee, BindingResult result){ if(result.hasErrors()){ Map<String,Object> map = new HashMap<>(); //校验失败,在模态框中显示校验失败的错误信息 List<FieldError> fieldErrors = result.getFieldErrors(); for (FieldError fieldError : fieldErrors){ 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 "lastPage"; return Msg.success(); } }
-
进行校验,判断是否有效
//点击保存员工的Ajax请求 $("#emp_save_btn").click(function(){ //1、模态框中填写的表单数据提交给服务器进行保存 //还需要对将要提交给服务器的数据进行校验 if( !validate_add_form()){ return false; } //判断之前的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'); // $("#empAddModal input").empty(); //2、来到最后一页,显示刚才保存的数据 //发送Ajax请求显示最后一页数据即可 to_page(totalRecord); }else{ //显示失败信息 // console.log(result); //有哪个字段的错误就显示哪个字段的 if(undefined != result.extend.errorFields.email){ //显示邮箱错误信息 show_validate_msg("#email_add_input","error",result.extend.errorFields.email); } if(undefined != result.extend.errorFields.empName){ //显示员工名字的错误信息 show_validate_msg("#empName_add_input","error",result.extend.errorFields.empName); } } } })
修改员工信息
- 点击编辑
- 弹出用户修改的模态框(显示用户信息)
- 点击更新,完成用户修改
-
点击编辑,弹出用户模态框
//我们是按钮创建之前就绑定了click,所以绑定不上 //1、可以在创建按钮的时候绑定 2、绑定点击.live() //jquery新版没有live,使用on方法替代 $(document).on("click",".edit_btn",function(){ // alert("edit"); //1、查出员工信息,显示员工信息 getDepts("#empUpdateModal select"); //2、查出部门信息,并显示部门列表 getEmp($(this).attr("edit-id")); //把员工id传递给模态框的更新按钮 $("#emp_update_btn").attr("edit-id",$(this).attr("edit-id")); //3、弹出模态框 $("#empUpdateModal").modal({ backdrop: "static" }); }) //根据id查询员工数据 function getEmp(id){ $.ajax({ url:"${APP_PATH}/emp/"+id, type:"GET", success:function (result) { // console.log(result); var empData = result.extend.emp; $("#empName_update_static").text(empData.empName); $("#email_update_input").val(empData.email); $("#empUpdateModal input[name=gender]").val([empData.gender]); $("#empUpdateModal select").val([empData.dId]); } }); }
- Controller根据id查询员工
/** * 根据id查询员工 * @param id * @return */ @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); }
- Service层从数据库查询员工
/** * 安装员工ID查询员工信息 * @param id * @return */ public Employee getEmp(Integer id) { return employeeMapper.selectByPrimaryKey(id); }
-
点击更新,更新数据
//点击更新,更新员工数据 $("#emp_update_btn").click(function(){ //验证邮箱是否合法 //校验邮箱格式 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",""); } //发送Ajax请求保存更新的员工数据 $.ajax({ url:"${APP_PATH}/emp/"+$(this).attr("edit-id"), type:"PUT", // data:$("#empUpdateModal form").serialize()+"&_method=PUT",//type可以用POST提交 data:$("#empUpdateModal form").serialize(),//直接使用Ajax发送PUT请求 success:function (result) { // alert(result.msg); //1、关闭模态框 $("#empUpdateModal").modal("hide"); //2、回到本页面 to_page(currentPage); } }) })
-
Controller封装前端传来的数据封装成Employee对象
/** * 员工更新方法 * 如果直接发ajax=PUT形式的请求 * 封装的数据 * Employee * [empId=1014,empName=null,gender=null,email=null,dId=null] * * 问题: * 请求体中有数据,但是Employee对象封装不上 * sql拼串就发产生异常 update tbl_emp where emp_id = 1014; * * 原因: * Tomcat: * 1、将请求体中的数据,封装成一个map * 2、request.getParameter("empName")就会从这个map中取值 * 3、SpringMVC封装POJO对象的时候 * 会把POJO中每个属性的值:request.getParameter("email"); * Ajax发送PUT请求引发的问题 * PUT请求,请求体中的数据,request.getParameter("empName");拿不到数据 * Tomcat一看是PUT,不会封装请求体中的数据为map,只有POST形式的请求才封装请求体为map * * 解决方案 * 1、我们要能支持直接发送PUT之类的请求还要封装请求体中的数据 * 2、配置上HttpPutFormContentFilter * 3、它的作用:将请求体中的数据解析包装成一个map,request被重新包装 * request.getParameter()被重写,就会从自己封装的map中取数据 * @param employee * @return */ @RequestMapping(value = "/emp/{empId}",method = RequestMethod.PUT) @ResponseBody public Msg saveEmp(Employee employee){ System.out.println("将要更新的员工数据:" + employee); employeeService.updateEmmp(employee); return Msg.success(); }
-
Service层更新数据数据
public void updateEmmp(Employee employee) { employeeMapper.updateByPrimaryKeySelective(employee); }
-
配置HttpPutFormContentFilter
在web.xml中进行配置
<filter> <filter-name>HttpPutFormContentFilter</filter-name> <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class> </filter> <filter-mapping> <filter-name>HttpPutFormContentFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
-
删除单一员工
发送/emp/id,请求类型是DELETE
//单个删除
$(document).on("click",".delete_btn",function(){
//1、弹出是否确认删除对话框
// alert($(this).parents("tr").find("td:eq(1)").text());//当前元素父元素的tr下的第二个td标签中的内容
var empName = $(this).parents("tr").find("td:eq(1)").text();
var empId = $(this).attr("del-id");
if(confirm("确认删除【"+empName+"】吗?")){
//确认,发送Ajax请求删除即可
$.ajax({
url:"${APP_PATH}/emp/" + empId,
type:"DELETE",
success:function (result) {
alert(result.msg);
//回到本页
to_page(currentPage);
}
})
}
})
Controller接收该请求,调用Service层的方法
@RequestMapping(value = "/emp/{id}",method = RequestMethod.DELETE)
@ResponseBody
public Msg deleteEmpById(@PathVariable("id") Integer id){
employeeService.deleteEmp(id);
return Msg.success();
}
Service层调用方法,从数据库中删除数据
/**
* 删除员工
* @param id
*/
public void deleteEmp(Integer id) {
employeeMapper.deleteByPrimaryKey(id);
}
删除批量数据
全选和全不选
-
先将表格样式改变
<tr> <th> <input type="checkbox" id="check_all" /> </th> <th>#</th> <th>empName</th> <th>gender</th> <th>email</th> <th>deptName</th> <th>操作</th> </tr>
-
显示分页数据的时候加上checkbox,单选框这一项
/*解析显示分页数据*/ function build_emps_table(result){ $.each(emps,function (index,item) { var checkBoxTd = $("<td><input type='checkbox' class='check_item'/></td>"); //append方法执行完成以后还是返回原来的元素,这里每次append都会产生新的<tr></tr> $("<tr></tr>").append(checkBoxTd) .append(empIdTd).append(empNameTd) .append(genderTd).append(emailTd).append(deptNameTd) .append(btnTd) .appendTo("#emps_table tbody"); }) }
-
完成全选和全不选
//完成全选/全不选 $("#check_all").click(function(){ //attr获取checked是undefined //我们这些dom原生的属性,attr获取自定义属性的值 //prop修改和读取dom原生属性的值 // alert($(this).attr("checked")); // alert($(this).prop("checked")); // $(this).prop("checked"); $(".check_item").prop("checked",$(this).prop("checked")); }) //check_item, $(document).on("click",".check_item",function () { //判断当前选中的元素是否5个 // alert($(".check_item:checked").length); var flag = $(".check_item:checked").length == $(".check_item").length; $("#check_all").prop("checked",flag); })
-
点击删除按钮,删除多个员工信息
//点击全部删除,就批量删除 $("#emp_delete_all_btn").click(function () { var empNames = ""; var del_idstr = ""; $.each($(".check_item:checked"),function(){ // alert($(this).parents("tr").find("td:eq(2)").text()); //组装员工名字字符串 empNames += $(this).parents("tr").find("td:eq(2)").text() + ","; //组装员工id字符串 del_idstr += $(this).parents("tr").find("td:eq(1)").text() + "-" }); empNames = empNames.substring(0,empNames.length-1); del_idstr = del_idstr.substring(0,del_idstr.length-1); if(confirm("确认删除【"+empNames+"】吗?")){ $.ajax({ url:"${APP_PATH}/emp/" + del_idstr, type:"DELETE", success:function (result) { alert(result.msg); //回到当前页面 to_page(currentPage); } }) } });
-
Controller接收请求,将字符串进行区分,并进行类型转换
/** * 单个批量二合一 * 批量删除:1-2-3 * 单个删除:1 * @param * @return */ @RequestMapping(value = "/emp/{ids}",method = RequestMethod.DELETE) @ResponseBody public Msg deleteEmp(@PathVariable("ids") String ids){ if(ids.contains("-")){ //批量删除 String[] str_ids = ids.split("-"); List<Integer> del_ids = new ArrayList<>(); //组装id的集合 for (String id : str_ids){ del_ids.add(Integer.parseInt(id)); } employeeService.deleteBatch(del_ids); }else{ //单个删除 Integer id = Integer.parseInt(ids); employeeService.deleteEmp(id); } return Msg.success(); }
-
Service层进行批量删除
/** * 批量删除 * @param ids */ public void deleteBatch(List<Integer> ids) { EmployeeExample employeeExample = new EmployeeExample(); EmployeeExample.Criteria criteria = employeeExample.createCriteria(); criteria.andEmpIdIn(ids); //delete from xxx where emp_id in (1,2,3); employeeMapper.deleteByExample(employeeExample); }
all_btn").click(function () {
var empNames = “”;
var del_idstr = “”;
.
e
a
c
h
(
.each(
.each((".check_item:checked"),function(){
// alert($(this).parents(“tr”).find(“td:eq(2)”).text());
//组装员工名字字符串
empNames += $(this).parents(“tr”).find(“td:eq(2)”).text() + “,”;
//组装员工id字符串
del_idstr += $(this).parents(“tr”).find(“td:eq(1)”).text() + “-”
});
empNames = empNames.substring(0,empNames.length-1);
del_idstr = del_idstr.substring(0,del_idstr.length-1);
if(confirm(“确认删除【”+empNames+"】吗?")){
KaTeX parse error: Expected '}', got 'EOF' at end of input: … url:"{APP_PATH}/emp/" + del_idstr,
type:“DELETE”,
success:function (result) {
alert(result.msg);
//回到当前页面
to_page(currentPage);
}
})
}
});
- Controller接收请求,将字符串进行区分,并进行类型转换
```java
/**
* 单个批量二合一
* 批量删除:1-2-3
* 单个删除:1
* @param
* @return
*/
@RequestMapping(value = "/emp/{ids}",method = RequestMethod.DELETE)
@ResponseBody
public Msg deleteEmp(@PathVariable("ids") String ids){
if(ids.contains("-")){
//批量删除
String[] str_ids = ids.split("-");
List<Integer> del_ids = new ArrayList<>();
//组装id的集合
for (String id : str_ids){
del_ids.add(Integer.parseInt(id));
}
employeeService.deleteBatch(del_ids);
}else{
//单个删除
Integer id = Integer.parseInt(ids);
employeeService.deleteEmp(id);
}
return Msg.success();
}
-
Service层进行批量删除
/** * 批量删除 * @param ids */ public void deleteBatch(List<Integer> ids) { EmployeeExample employeeExample = new EmployeeExample(); EmployeeExample.Criteria criteria = employeeExample.createCriteria(); criteria.andEmpIdIn(ids); //delete from xxx where emp_id in (1,2,3); employeeMapper.deleteByExample(employeeExample); }
[外链图片转存中…(img-lcUnSKjP-1611281390431)]
至此,SSM整合结束