电脑环境:macOS
电脑软件:IDEA、Navicat Premium
基础框架:ssm(Spring+SpringMVC+MyBatis)
前端框架:bootstrap
数据库:MySQL8.0.18
服务器:Tomcat8.5.51
依赖管理:Maven3.5.4
分页工具:pagehelper
逆向工程:MyBatis Generator
项目地址:https://github.com/angenin/ssm_crud/
创建一个maven项目,选用webapp模板,文件名为ssm_crud。
在src下新建test目录,test下再新建java和resources目录,在main目录下也添加java和resources目录。
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.2.4.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.2.4.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.2.4.RELEASEversion>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.2version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.2version>
dependency>
<dependency>
<groupId>com.mchangegroupId>
<artifactId>c3p0artifactId>
<version>0.9.5.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.18version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>jsp-apiartifactId>
<version>2.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>jstlgroupId>
<artifactId>jstlartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>4.0.1version>
<scope>providedscope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
dependencies>
进入官网下载:https://www.bootcss.com/
在项目的webapp目录下新建static目录,并把下载解压好的文件放入,再在static目录下新建一个js目录,用于存放JQuery文件。
- 在index.jsp中的head标签里写入
html
create database ssm_crud;
use ssm_crud;
create table tbl_dept(
dept_id int(11) PRIMARY KEY auto_increment,
dept_name varchar(255) not null
);
create table tbl_emp(
emp_id int(11) PRIMARY KEY auto_increment,
emp_name varchar(255) not null,
gender char(1),
email varchar(255) not null,
d_id int(11),
FOREIGN KEY(d_id) REFERENCES tbl_dept(dept_id)
);
在main目录下的resources目录添加Spring的配置文件,名为applicationContext.xml。
在main目录下的resources目录添加三个file文件,名为jdbcConfig.properties、springmvc.xml和mybatis-config.xml。
在resources目录下添加mapper目录。
在WEB-INF目录下新建一个名为views的目录。
在WEB-INF目录下新建一个名为dispatcherServlet-Servlet.xml的配置文件。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
beans>
<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_3_1.xsd"
version="3.1">
<display-name>Archetype Created Web Applicationdisplay-name>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationContext.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<servlet>
<servlet-name>dispatcherServletservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>dispatcherServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<filter>
<filter-name>CharacterEncodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
<init-param>
<param-name>forceRequestEncodingparam-name>
<param-value>trueparam-value>
init-param>
<init-param>
<param-name>forceResponseEncodingparam-name>
<param-value>trueparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>CharacterEncodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<filter>
<filter-name>HiddenHttpMethodFilterfilter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilterfilter-class>
filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
web-app>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ontext="http://www.springframework.org/schema/context"
xsi:schemaLocation=" http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<ontext:component-scan base-package="com.angenin" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
ontext:component-scan>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
bean>
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
beans>
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/ssm_crud
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.user=root
jdbc.password=123456
<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.angenin">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
context:component-scan>
<context:property-placeholder location="classpath:jdbcconfig.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>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="dataSource" ref="pooledDataSource"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.angenin.crud.dao"/>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="pooledDataSource"/>
bean>
<aop:config>
<aop:pointcut id="txPoint" expression="execution(* com.angenin.crud.service..*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
<tx:method name="get*" read-only="true"/>
tx:attributes>
tx:advice>
beans>
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>
<typeAliases>
<package name="com.angenin.crud.bean"/>
typeAliases>
configuration>
在pop.xml中的
里引入
<dependency>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-coreartifactId>
<version>1.3.7version>
dependency>
然后在resources目录下新建generatorConfig.xml文件,并写入
(在
、
和
标签里的targetProject
属性,因为我是macOS,所有用/
,如果是Windows系统,需要把/
改为\
。)
<generatorConfiguration>
<properties resource="jdbcConfig.properties"/>
<context id="mbg" targetRuntime="MyBatis3">
<commentGenerator>
<property name="suppressAllComments" value="true"/>
commentGenerator>
<jdbcConnection connectionURL="${jdbc.jdbcUrl}"
driverClass="${jdbc.driverClass}"
userId="${jdbc.user}"
password="${jdbc.password}"/>
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
javaTypeResolver>
<javaModelGenerator targetPackage="com.angenin.crud.bean"
targetProject="./src/main/java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
javaModelGenerator>
<sqlMapGenerator targetPackage="mappers"
targetProject="./src/main/resources">
<property name="enableSubPackages" value="true" />
sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.angenin.crud.dao"
targetProject="./src/main/java">
<property name="enableSubPackages" value="true" />
javaClientGenerator>
<table tableName="tbl_emp" domainObjectName="Employee"/>
<table tableName="tbl_dept" domainObjectName="Department"/>
context>
generatorConfiguration>
在test/java目录下新建MBGTest.java,写入
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("src/main/resources/generatorConfig.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);
}
}
在dao/EmployeeMapper.java中添加:
// 因为逆向工程生成的查询只生成d_id,没有dept表的信息,所以自定义两个查询方法。
// 自定义,查询时带上dept表里信息
List<Employee> selectByExampleWithDept(EmployeeExample example);
Employee selectByPrimaryKeyWithDept(Integer empId);
然后在bean/Employee.java里添加:
private Department department;
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
最后往resources/mappers/EmployeeMapper.xml中添加:
<!-- 自定义返回集合-->
<resultMap id="WithDeptResultMap" type="com.angenin.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.angenin.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>
<!-- 自定义查询,查询员工同时带上部门信息-->
<select id="selectByExampleWithDept" parameterType="com.angenin.crud.bean.EmployeeExample" 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" parameterType="java.lang.Integer" 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>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.2.4.RELEASEversion>
<scope>testscope>
dependency>
//Department
public Department() {
}
public Department(Integer deptId, String deptName) {
this.deptId = deptId;
this.deptName = deptName;
}
//==========================
//Employee
public Employee() {
}
public Employee(Integer empId, String empName, String gender, String email, Integer dId) {
this.empId = empId;
this.empName = empName;
this.gender = gender;
this.email = email;
this.dId = dId;
}
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
<constructor-arg name="executorType" value="BATCH"/>
bean>
import com.angenin.crud.bean.Department;
import com.angenin.crud.bean.Employee;
import com.angenin.crud.dao.DepartmentMapper;
import com.angenin.crud.dao.EmployeeMapper;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.UUID;
/*测试DepartmentMapper
* 推荐Spring的项目使用Spring的单元测试,可以自动注入我们需要的组件
* 1.导入SpringTest模块
* 2.@ContextConfiguration(locations = {"classpath:applicationContext.xml"}) 指定spring配置文件的位置
*/
// - @RunWith(SpringJUnit4ClassRunner.class)指定Test用什么单元测试来运行
// - 如果出现`ExceptionInInitializerError`错误,
// 是因为`SpringJUnit4ClassRunner`要求`JUnit 4.12`或更高,
// 在pop.xml中把junit版本调高即可。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
"classpath:applicationContext.xml"})
public class MapperTest {
//3.直接autowired要使用的组件即可
@Autowired
DepartmentMapper departmentMapper;
@Autowired
EmployeeMapper employeeMapper;
@Autowired
SqlSession sqlSession;
@Test
public void testCRUD(){
// 1.插入几个部门
departmentMapper.insertSelective(new Department(null, "开发部"));
departmentMapper.insertSelective(new Department(null, "测试部"));
departmentMapper.insertSelective(new Department(null, "运维部"));
// 2.生成员工数据,测试员工表的插入
employeeMapper.insertSelective(new Employee(null, "张三", "M", "[email protected]", 1));
// 3.批量插入多个员工;使用可以批量插入操作的sqlSession
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
for (int i = 0; i < 1000; i++) {
String uid = UUID.randomUUID().toString().substring(0, 5) + i;
employeeMapper.insertSelective(new Employee(null, uid, "M", uid + "@qq.com", 1));
}
}
}
1、访问 index.jsp页面
2、index.jsp页面发送出查询员工列表请求
3、EmployeeController来接受请求,查出员工数据
4、来到list.jsp页面进行展示
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<jsp:forward page="/emps"/>
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelperartifactId>
<version>5.1.8version>
dependency>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">plugin>
plugins>
package com.angenin.crud.controller;
import com.angenin.crud.bean.Employee;
import com.angenin.crud.service.EmployeeService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
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;
/**
* 查询员工数据(分页显示)
* @param pn 页码
* @param model
* @return
*/
@RequestMapping("/emps")
public String getEmps(@RequestParam(value="pn", defaultValue = "1")Integer pn, Model model){
//引入PageHelper分页插件
//查询前调用分页插件,之后的查询就是分页查询(页码和每页显示的数据数)
//第三个参数:emp_id asc 为指定排序的规则,按emp_id正序排序(不加后面插入数据后排序可能有问题)
PageHelper.startPage(pn, 5, "emp_id asc");
List<Employee> emps = employeeService.getAll();
//使用pageInfo包装查询后的结果,传入查询结果和连续显示的页数
PageInfo page = new PageInfo(emps, 5);
//把page传到下个页面
model.addAttribute("pageInfo", page);
// 页面跳转到WEB-INF/views下的list.jsp页面(springMVC的视图解析器)
return "list";
}
}
package com.angenin.crud.service;
import com.angenin.crud.bean.Employee;
import com.angenin.crud.dao.EmployeeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class EmployeeService {
@Autowired
EmployeeMapper employeeMapper;
/**
* 查询所有员工
* @return
*/
public List<Employee> getAll() {
return employeeMapper.selectByExampleWithDept(null);
}
}
在test/java下新建一个MvcTest.java用于测试。
import com.angenin.crud.bean.Employee;
import com.github.pagehelper.PageInfo;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import java.util.List;
/**
* 使用Spring测试模块提供的测试请求功能,测试crud请求的正确性
*/
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration //获取springmvc的ioc容器
@ContextConfiguration(locations = {
"classpath:applicationContext.xml", "classpath:springmvc.xml"})
public class MvcTest {
//传入SpringMVC的IOC
@Autowired
WebApplicationContext context;
//虚拟mvc请求,获取到处理结果
MockMvc mockMvc;
@Before //使用junit的Before
public void initMockMvc(){
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
@Test
public void testPage() throws Exception {
//模拟发送get请求 param为请求传入的键值对
//andReturn为获取返回值
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/emps").param("pn", "1")).andReturn();
//请求成功以后,请求域中会有pageInfo
MockHttpServletRequest request = result.getRequest();
PageInfo pi = (PageInfo) request.getAttribute("pageInfo");
System.out.println("当前页码:" + pi.getPageNum());
System.out.println("总页码:" + pi.getPages());
System.out.println("总记录数:" + pi.getTotal());
System.out.print("在页面需要连续显示的页码:");
int[] nums = pi.getNavigatepageNums();
for (int num : nums) {
System.out.print(" " + num);
}
System.out.println();
//获取员工数据
List<Employee> list = pi.getList();
for (Employee employee : list) {
System.out.println("ID: " + employee.getEmpId() + ",name: " + employee.getEmpName());
}
}
}
编写list.jsp。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入标签核心库--%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>员工列表title>
<%
//获取当前项目名(得到的是 /加项目名(例:/ssm_crud) ,所以使用的时候前面不用加/而后面需要)
pageContext.setAttribute("APP_PATH", request.getContextPath());
%>
<%-- web的路径:
- 不以/开始的相对路径,找资源,以当前资源的路径为基准,经常出问题。
- 以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:3306)再需要加上项目名(项目名也不是写死的)
http://localhost:8080/ssm_crud
--%>
<script type="text/javascript" src="${ APP_PATH }/static/js/jquery-3.4.1.min.js">script>
<link href="${ APP_PATH }/static/bootstrap-dist/css/bootstrap.min.css" rel="stylesheet"/>
<script src="${ APP_PATH }/static/bootstrap-dist/js/bootstrap.min.js">script>
head>
<body>
<%-- 搭建显示页面(官方的全局CSS样式的栅格系统) --%>
<div class="container">
<%-- 标题--%>
<div class="row">
<div class="col-md-12">
<h1>SSM_CRUDh1>
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>empIdth>
<th>empNameth>
<th>genderth>
<th>emailth>
<th>deptNameth>
<th>操作th>
tr>
<c:forEach items="${ pageInfo.list }" var="emp">
<tr>
<td>${ emp.empId }td>
<td>${ emp.empName }td>
<td>${ emp.gender == "M" ? "男" : "女" }td>
<td>${ emp.email }td>
<td>${ emp.department.deptName }td>
<td>
<button class="btn btn-info 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>
td>
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?pn=1">首页a>li>
<%--如果有上一页--%>
<c:if test="${ pageInfo.hasPreviousPage }">
<%--上一页--%>
<li>
<a href="${ APP_PATH }/emps?pn=${ pageInfo.pageNum - 1 }" aria-label="Previous">
<span aria-hidden="true">«span>
a>
li>
c:if>
<%--显示五页--%>
<c:forEach items="${ pageInfo.navigatepageNums }" var="page_Num">
<%--当前页--%>
<c:if test="${ page_Num == pageInfo.pageNum }">
<li class="active"><a href="/">${ page_Num }a>li>
c:if>
<%--不是当前页--%>
<c:if test="${ page_Num != pageInfo.pageNum }">
<li><a href="${ APP_PATH }/emps?pn=${ page_Num }">${ page_Num }a>li>
c:if>
c:forEach>
<%--如果有下一页--%>
<c:if test="${ pageInfo.hasNextPage }">
<%--下一页--%>
<li>
<a href="${ APP_PATH }/emps?pn=${ pageInfo.pageNum + 1 }" aria-label="Next">
<span aria-hidden="true">»span>
a>
li>
c:if>
<%--尾页--%>
<li><a href="${ APP_PATH }/emps?pn=${ pageInfo.pages }">尾页a>li>
ul>
nav>
div>
div>
div>
body>
html>
实现了数据的查询和分页以及分页的跳转功能。
但只能适用于PC浏览器和服务器的交互,实际上发送请求的可能是安卓或iOS,所以这里我们把数据用josn的格式用ajax发送给客户端。
1、 index.jsp页面直接发送ajax请求进行员工分页数据的查询
2、服务器将查出的数据,以json字符串的形式返回给浏览器
3、浏览器收到js字符串,可以使用js对json进行解析,使用js通过dom增删改改变页面
4、返回json,实现客户端的无关性
/**
* 查询员工数据(分页显示)
* json和ajax
* 导入jackson包
* @param pn 页码
* @return
*/
@RequestMapping("/emps")
@ResponseBody
public PageInfo getEmpsWithJson(@RequestParam(value="pn", defaultValue = "1")Integer pn){
//引入PageHelper分页插件
//查询前调用分页插件,之后的查询就是分页查询(页码和每页显示的数据数)
//第三个参数:emp_id asc 为指定排序的规则,按emp_id正序排序(不加后面插入数据后排序可能有问题)
PageHelper.startPage(pn, 5, "emp_id asc");
List<Employee> emps = employeeService.getAll();
//使用pageInfo包装查询后的结果,传入查询结果和连续显示的页数
PageInfo page = new PageInfo(emps, 5);
return page;
}
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.10.2version>
dependency>
启动后页面显示json格式的数据。
因为PageInfo返回到页面不清楚是成功还是失败,所以我们还需要在bean中定义一个提示信息的类。
package com.angenin.crud.bean;
import java.util.HashMap;
import java.util.Map;
public class Msg {
//状态码 100:成功;200:失败
private int code;
//提示信息
private String msg;
//用户要返回给浏览器的数据
private Map<String, Object> extend = new HashMap<>();
//请求成功方法
public static Msg success(){
Msg result = new Msg();
result.setCode(100);
result.setMsg("处理成功!");
return result;
}
//请求失败方法
public static Msg fail(){
Msg result = new Msg();
result.setCode(200);
result.setMsg("处理失败!");
return result;
}
//添加信息的方法
public Msg add(String key, Object value){
this.getExtend().put(key, value);
return this;
}
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方法。
public Msg getEmpsWithJson(@RequestParam(value="pn", defaultValue = "1")Integer pn){
//引入PageHelper分页插件
//查询前调用分页插件,之后的查询就是分页查询(页码和每页显示的数据数)
//第三个参数:emp_id asc 为指定排序的规则,按emp_id正序排序(不加后面插入数据后排序可能有问题)
PageHelper.startPage(pn, 5, "emp_id asc");
List<Employee> emps = employeeService.getAll();
//使用pageInfo包装查询后的结果,传入查询结果和连续显示的页数
PageInfo page = new PageInfo(emps, 5);
return Msg.success().add("pageInfo", page);
}
!-- 注册PageInterceptor分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="reasonable" value="true"/>
plugin>
plugins>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入标签核心库--%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>员工列表title>
<%
//获取当前项目名(得到的是 /加项目名(例:/ssm_crud) ,所以使用的时候前面不用加/而后面需要)
pageContext.setAttribute("APP_PATH", request.getContextPath());
%>
<%-- web的路径:
- 不以/开始的相对路径,找资源,以当前资源的路径为基准,经常出问题。
- 以/开始的相对路径,找资源,以服务器的路径为标准(http://localhost:3306)再需要加上项目名(项目名也不是写死的)
http://localhost:8080/ssm_crud
--%>
<script type="text/javascript" src="${ APP_PATH }/static/js/jquery-3.4.1.min.js">script>
<%-- 下载的bootstrap前端框架有问题,用第二种方式引入--%>
<%-- <link href="${ APP_PATH }/static/bootstrap-dist/css/bootstrap.min.css" rel="stylesheet"/>--%>
<%-- <script src="${ APP_PATH }/static/bootstrap-dist/js/bootstrap.min.js">script>--%>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous">script>
head>
<body>
<%-- 搭建显示页面(官方的全局CSS样式的栅格系统) --%>
<div class="container">
<%-- 标题--%>
<div class="row">
<div class="col-md-12">
<h1>SSM_CRUDh1>
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>empIdth>
<th>empNameth>
<th>genderth>
<th>emailth>
<th>deptNameth>
<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.页面加载完成后,发送一个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;
//jquery自带的遍历
$.each(emps, function (index, item) {
//员工信息单元格
var empIdTd = $(" ").append(item.empId);
var empNameTd = $(" ").append(item.empName);
var genderTd = $(" ").append(item.gender == 'M' ? "男" : "女");
var emailTd = $(" ").append(item.email);
var deptNameTd = $(" ").append(item.department.deptName);
//修改删除按钮,在同一个单元格内
var editBtn = $("").addClass("btn btn-info btn-sm edit_btn")
.append($("").addClass("glyphicon glyphicon-pencil"))
.append("编辑");
var delBtn = $("").addClass("btn btn-danger btn-sm delete_btn")
.append($("").addClass("glyphicon glyphicon-trash"))
.append("删除");
var btnTd = $(" ").append(editBtn).append(" ").append(delBtn);
//把相同员工的单元格添加到同一行
$(" ").append(empIdTd)
.append(empNameTd)
.append(genderTd)
.append(emailTd)
.append(deptNameTd)
.append(btnTd)
.appendTo("#emps_table tbody"); //添加到id为emps_table的表里的tbody中
});
}
//解析显示分页信息
function build_page_info(result) {
//每次页面跳转都先清空分页信息
$("#page_info_area").empty();
$("#page_info_area").append("当前 " + result.extend.pageInfo.pageNum + " 页," +
"总 " + result.extend.pageInfo.pages + " 页," +
"总 " + result.extend.pageInfo.total + " 条记录");
}
//解析显示分页条
function build_page_nav(result) {
//每次页面跳转都先清空table表格数据
$("#page_nav_area").empty();
var ul = $("
").addClass("pagination");
//首页
var firstPageLi = $("").append($("").append("首页").attr("href", "#"));
//上一页
var prePageLi = $("").append($("").append("«").attr("href", "#"));
//如果没有上一页,给首页和前一页按钮增加不能点击的效果
if(!result.extend.pageInfo.hasPreviousPage){
firstPageLi.addClass("disabled");
prePageLi.addClass("disabled");
}else{
//如果有上一页
//为首页添加点击翻页事件
firstPageLi.click(function () {
to_page(1);
});
//为上一页添加点击翻页事件
prePageLi.click(function () {
to_page(result.extend.pageInfo.pageNum - 1);
});
}
//下一页
var nextPageLi = $("").append($("").append("»").attr("href", "#"));
//尾页
var lastPageLi = $("").append($("").append("尾页").attr("href", "#"));
//如果没有下一页,给后一页和尾页按钮增加不能点击的效果
if(!result.extend.pageInfo.hasNextPage){
nextPageLi.addClass("disabled");
lastPageLi.addClass("disabled");
}else{
//如果有下一页
//为下一页添加点击翻页事件
nextPageLi.click(function () {
to_page(result.extend.pageInfo.pageNum + 1);
});
//为尾页页添加点击翻页事件
lastPageLi.click(function () {
to_page(result.extend.pageInfo.pages);
});
}
//添加首页
ul.append(firstPageLi).append(prePageLi);
//5个连续显示的页码
$.each(result.extend.pageInfo.navigatepageNums, function (index, item) {
var numLi = $("").append($("").append(item).attr("href", "#"));
//如果循环到的页码是当前页码
if(item == result.extend.pageInfo.pageNum){
numLi.addClass("active");
}
//为页码按钮添加点击事件
numLi.click(function () {
//页面跳转到对应的页数
to_page(item);
});
//添加页码
ul.append(numLi);
})
//添加下一页和尾页
ul.append(nextPageLi).append(lastPageLi);
//把ul添加到nav中
var navEle = $("").append(ul).appendTo("#page_nav_area");
}
script>
body>
html>
1、在 index.jsp页面点击”新增”按钮
2、弹出增对话框(bootstrap框架js插件的模态框里的动态实例)
3、到数据库查询部门列表,显示在对话框中
4、用户输入数据后进行校验(jquery前端校验,ajax用户名重复校验,重要数据(后端校验JSR303),唯一约束)
5、完成保存
...
<%-- 按钮--%>
<div class="row">
<div class="col-md-4 col-md-offset-8">
<button class="btn btn-primary" id="emp_add_modal_btn">新 增button>
<button class="btn btn-danger" id="emp_delete_modal_btn">删 除button>
div>
div>
<%-- 显示表格数据--%>
...
<%-- 员工添加按钮的模态框 --%>
<div class="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">
<%-- 表单 --%>
<%-- empName --%>
<form class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">empNamelabel>
<div class="col-sm-10">
<%-- 员工姓名不可修改 --%>
<p class="form-control-static" id="empName_update_static">p>
div>
div>
<%-- email --%>
<div class="form-group">
<label class="col-sm-2 control-label">emaillabel>
<div class="col-sm-10">
<input type="text" class="form-control" name="email" id="email_add_input" placeholder="[email protected]">
div>
div>
<%-- gender --%>
<div class="form-group">
<label class="col-sm-2 control-label">emaillabel>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="gender" id="gender1_add_input" value="M" checked> 男
label>
<label class="radio-inline">
<input type="radio" name="gender" id="gender2_add_input" value="F"> 女
label>
div>
div>
<%-- deptName --%>
<div class="form-group">
<label class="col-sm-2 control-label">deptNamelabel>
<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">保存button>
div>
div>
div>
div>
package com.angenin.crud.service;
import com.angenin.crud.bean.Department;
import com.angenin.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() {
// 查出所有部门信息并返回给对应的controller
return departmentMapper.selectByExample(null);
}
}
package com.angenin.crud.controller;
import com.angenin.crud.bean.Department;
import com.angenin.crud.bean.Msg;
import com.angenin.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);
}
}
//新增按钮的点击弹窗事件
$("#emp_add_modal_btn").click(function () {
//发送ajax请求,查出部门信息,显示在下拉列表中
getDepts("#empAddModal select");
//调用模态框
$("#empAddModal").modal({
backdrop: "static"
});
});
//查出所有部门信息并显示在下拉列表中
function getDepts(ele){
//清空下拉列表
$(ele).empty();
$.ajax({
url: "${APP_PATH}/depts",
type: "get",
success: function (result) {
//显示部门信息到下拉列表中
$.each(result.extend.depts, function () {
//不传参使用this代表当前被遍历的对象
var optionEle = $("").append(this.deptName)
.attr("value", this.deptId).appendTo("ele");
});
}
});
}
/**
* 员工保存方法
* @param employee
*/
public void saveEmp(Employee employee) {
//因为我们的员工id是自增的,所有使用insertSelective有选择的插入,而不是insert。
employeeMapper.insertSelective(employee);
}
/**
* 保存新增的员工信息
* Rest风格的URI,我们规定post的请求为保存请求
*/
@RequestMapping(value = "/emp", method = RequestMethod.POST)
@ResponseBody
public Msg saveEmp(Employee employee){
//接收页面表单传递过来的employee对象信息
employeeService.saveEmp(employee);
return Msg.success();
}
在index.jsp页面中为弹窗的保存按钮添加一个id=“emp_save_btn”。
在index.jsp页面底部的script中添加一个全局变量var totalRecord;
,为保存后的页面跳转做准备,此全局变量在分页信息那赋值。
//定义一个全局变量:总记录数和当前页(在分页信息那赋值)
var totalRecord, currentNum;
//----------------------------
function build_page_info(result) {
...
//为全局变量赋值
totalRecord = result.extend.pageInfo.total;
currentNum = result.extend.pageInfo.pageNum;
}
//弹窗的保存按钮的点击保存事件
$("#emp_save_btn").click(function () {
//调用JQuery的serialize,系列化表单里的员工信息
//系列化后的字符串:empName=cat&email=cat%40qq.com&gender=F&dId=3
// alert($("#empAddModal form").serialize());
//发送ajax请求把保存表单内新增的员工信息提交给服务器
$.ajax({
url: "${APP_PATH}/emp",
type: "post",
data: $("#empAddModal form").serialize(),
success: function(result){
// alert(result.msg);
//当保存成功:
//1.关闭弹窗
$("#empAddModal").modal('hide');
//2.跳转到最后一页
// (因为我们已经添加了bootstrap的合理化参数,所以当超过总页数将会跳转到最后一页)
to_page(totalRecord);
}
});
});
提交的数据需要满足才可保存:
//对提交的数据进行校验
if(!validate_add_form()){
return false;
}
//校验表单数据
function validate_add_form(){
//拿到数据,用jQuery的正则表达式进行校验
//校验员工姓名
var empName = $("#empName_add_input").val();
//正则:允许(6到16位,允许a-z A-Z 0-9 _ - 2到5个中文)
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;
}
下添加
,用于错误时显示的提示信息。//校验的提示信息
//ele校验的元素,status校验状态,msg提示信息
function show_validate_msg(ele, status, msg){
//清除当前元素的校验状态
$(ele).parent().removeClass("has-success has-error");
$(ele).next("span").text("");
if("success" == status){
//改变文本框颜色为绿色
$(ele).parent().addClass("has-success");
//清空错误提示信息
$(ele).next("span").text(msg);
}else if("error" == status){
//改变文本框颜色为红色
$(ele).parent().addClass("has-error");
//显示错误提示信息
$(ele).next("span").text(msg);
}
}
//校验表单数据
function validate_add_form(){
//拿到数据,用jQuery的正则表达式进行校验
//校验员工姓名
var empName = $("#empName_add_input").val();
//正则:允许(6到16位,允许a-z A-Z 0-9 _ - 2到5个中文)
var regName = /(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/;
if(!regName.test(empName)){
// alert("用户名可以是2-5位中文或6-16个英文和数字的组合!");
//显示校验的信息
show_validate_msg("#empName_add_input", "error", "用户名可以是2-5位中文或6-16个英文和数字的组合!");
return false;
}else{
show_validate_msg("#empName_add_input", "success", "");
}
//校验邮箱
var email = $("#email_add_input").val();
var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
if(!regEmail.test(email)){
// alert("邮箱格式不正确!");
//显示校验的信息
show_validate_msg("#email_add_input", "error", "邮箱格式不正确!");
return false;
}else{
show_validate_msg("#email_add_input", "success", "");
}
//校验通过
return true;
}
为了给用户更好的体验效果,我们再给输入文本框增加一个失去焦点事件,让用户输入完文本后,就发起校验,而不用等到去点击保存才提示,顺便解决了用户名和邮箱都不合法,只有用户名提示错误信息。(当然保存的校验也要保留)
//校验表单员工名
function validate_add_form_empName(ele){
//拿到数据,用jQuery的正则表达式进行校验
//校验员工姓名
var empName = $(ele).val();
//正则:允许(6到16位,允许a-z A-Z 0-9 _ - 2到5个中文)
var regName = /(^[a-zA-Z0-9_-]{6,16}$)|(^[\u2E80-\u9FFF]{2,5})/;
if(!regName.test(empName)){
// alert("用户名可以是2-5位中文或6-16个英文和数字的组合!");
//显示校验的信息
show_validate_msg(ele, "error", "用户名可以是2-5位中文或6-16个英文和数字的组合!");
return false;
}else{
show_validate_msg(ele, "success", "");
}
//校验通过
return true;
}
//校验表单邮箱
function validate_add_form_email(ele) {
//校验邮箱
var email = $(ele).val();
var regEmail = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/;
if(!regEmail.test(email)){
// alert("邮箱格式不正确!");
//显示校验的信息
show_validate_msg(ele, "error", "邮箱格式不正确!");
return false;
}else{
show_validate_msg(ele, "success", "");
}
//校验通过
return true;
}
//弹窗的保存按钮的点击保存事件
$("#emp_save_btn").click(function () {
//对提交的数据进行校验
if(!validate_add_form_empName("#empName_add_input") || !validate_add_form_email("#email_add_input")){
alert("请正确输入信息!");
return false;
}
//员工名输入框失去焦点校验事件(弹窗)
$("#empName_add_input").blur(function () {
validate_add_form_empName("#empName_add_input");
});
//邮箱输入框失去焦点校验事件(弹窗)
$("#email_add_input").blur(function () {
validate_add_form_email("#email_add_input");
});
//清除表单数据(表单重置)
//清空文本框内容
$("#empAddModal form")[0].reset();
//去除表单样式
$("#empAddModal form").find("*").removeClass("has-success has-error");
//清空有带有help-block类的内容(即错误信息)
$("#empAddModal form").find(".help-block").text("");
/**
* 查询用户名是否已存在
* @param empName
* @return true为可用(输入的用户名不存在)
*/
public boolean checkUser(String empName) {
//查询条件
EmployeeExample example = new EmployeeExample();
EmployeeExample.Criteria criteria = example.createCriteria();
//查询数据库中emp_name和传参empName相同的记录数
criteria.andEmpNameEqualTo(empName);
//countByExample方法返回符合条件的记录数
long count = employeeMapper.countByExample(example);
return count == 0;
}
/**
* 校验用户名是否已存在
*/
@RequestMapping("/checkuser")
@ResponseBody
public Msg checkuser(@RequestParam("empName")String empName){
boolean b = employeeService.checkUser(empName);
if(b){
//用户名可用
return Msg.success();
}else{
//用户名不可用
return Msg.fail();
}
}
//检验用户名是否可用
function validate_usable_empName(ele) {
//获取输入框的内容
var empName = $(ele).val();
//发送ajax请求校验用户名是否存在
$.ajax({
url: "${APP_PATH}/checkuser",
data: "empName=" + empName,
type: "get",
success: function (result) {
//判断是否成功(100为成功,200为失败)
if(100 == result.code){
show_validate_msg(ele, "success", "用户名可用");
//并给保存按钮添加一个自定义属性,success代表用户名可用
$("#emp_save_btn").attr("ajax-va", "success");
}else{
show_validate_msg(ele, "error", "用户名已存在!");
//并给保存按钮添加一个自定义属性,fail代表用户名不可用
$("#emp_save_btn").attr("ajax-va", "error");
}
}
});
}
在js的validate_add_form_empName方法中的else里中调用validate_usable_empName方法,实现用户名输入框失去焦点后先校验格式,格式符合再校验用户名是否可用。
因为用户名通过格式校验后,用户名是否可用保存按钮不知道,所以需要在保存按钮的点击事件里数据校验判断之下再加一个判断。
//判断用户名是否通过可用性校验
if($(this).attr("ajax-va") == "error"){
//没通过,不保存
alert("请正确输入信息!");
return false;
}
对用户名进行校验
/**
* 校验用户名是否已存在
*/
@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", "用户名已存在!");
}
}
function validate_usable_empName(ele) {
...
}else{
//错误信息从后端返回的数据中获取
show_validate_msg(ele, "error", result.extend.va_msg);
...
}
JSR303校验
<dependency>
<groupId>org.hibernate.validatorgroupId>
<artifactId>hibernate-validatorartifactId>
<version>6.1.2.Finalversion>
dependency>
//@Pattern(regexp = "", message 错误信息) 自定义校验注解
@Pattern(regexp = "(^[a-zA-Z0-9_-]{6,16}$)|(^[\\u2E80-\\u9FFF]{2,5})",
message = "用户名必须是6-16个英文和数字或2-5位中文的组合!")
private String empName;
//@Email //邮箱校验注解
@Pattern(regexp = "^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$",
message = "邮箱格式不正确!")
private String email;
/**
* 保存新增的员工信息
* Rest风格的URI,我们规定post的请求为保存请求
* @param employee 页面表单传递过来的employee对象信息
* @param result 封装校验的结果
* @return
*/
@RequestMapping(value = "/emp", method = RequestMethod.POST)
@ResponseBody //加上@Valid注解的参数需要校验
public Msg saveEmp(@Valid Employee employee, BindingResult result){
//进行校验
if(result.hasErrors()){
//校验失败
Map<String, Object> map = new HashMap<>();
List<FieldError> errors = result.getFieldErrors();
for (FieldError fieldError : errors) {
//获取错误的字段名
System.out.println("错误的字段名:" + fieldError.getField());
//获取错误的信息
System.out.println("错误信息:" + fieldError.getDefaultMessage());
//把错误信息保存到map中
map.put(fieldError.getField(), fieldError.getDefaultMessage());
}
return Msg.fail().add("errorFields", map);
}else{
//校验成功
employeeService.saveEmp(employee);
System.out.println(employee);
return Msg.success();
}
}
success: function(result){
if(100 == result.code){
//当保存成功:
//1.关闭弹窗
$("#empAddModal").modal('hide');
//2.跳转到最后一页
// (因为我们已经添加了bootstrap的合理化参数,所以当超过总页数将会跳转到最后一页)
to_page(totalRecord);
}else{
//显示失败信息,哪个错误就显示哪个
if(undefined != result.extend.errorFields.empName){
//显示员工名字的错误信息
show_validate_msg("#empName_add_input", "error", result.extend.errorFields.empName);
alert(result.extend.errorFields.empName);
}
if(undefined != result.extend.errorFields.email){
//显示邮箱的错误信息
show_validate_msg("#email_add_input", "error", result.extend.errorFields.email);
alert(result.extend.errorFields.email);
}
}
}
1、点击编辑
2、弹出用户修改的模态框(显示用户信息)
3、点击更新,完成用户修改
<%-- 员工修改的模态框 --%>
<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">×span>button>
<%-- 弹窗标题 --%>
<h4 class="modal-title">员工修改h4>
div>
<%-- 弹窗内容 --%>
<div class="modal-body">
<%-- 表单 --%>
<%-- empName --%>
<form class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">empNamelabel>
<div class="col-sm-10">
<input type="text" class="form-control" name="empName" id="empName_update_input" placeholder="empName">
<span class="help-block">span>
div>
div>
<%-- email --%>
<div class="form-group">
<label class="col-sm-2 control-label">emaillabel>
<div class="col-sm-10">
<input type="text" class="form-control" name="email" id="email_update_input" placeholder="[email protected]">
<span class="help-block">span>
div>
div>
<%-- gender --%>
<div class="form-group">
<label class="col-sm-2 control-label">emaillabel>
<div class="col-sm-10">
<label class="radio-inline">
<input type="radio" name="gender" id="gender1_update_input" value="M" checked> 男
label>
<label class="radio-inline">
<input type="radio" name="gender" id="gender2_update_input" value="F"> 女
label>
div>
div>
<%-- deptName --%>
<div class="form-group">
<label class="col-sm-2 control-label">deptNamelabel>
<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>
/**
* 根据id查询员工信息
* @param id
* @return
*/
public Employee getEmp(Integer id) {
Employee employee = employeeMapper.selectByPrimaryKey(id);
return employee;
}
/**
* 查询员工信息
* 查询指定是get请求
* @param id
* @return
*/
@RequestMapping(value = "/emp/{id}", method = RequestMethod.GET)
@ResponseBody
public Msg getEmp(@PathVariable("id")Integer id){
//查询指定的员工信息
Employee employeee = employeeService.getEmp(id);
return Msg.success().add("emp", employeee);
}
function build_emps_table(result) {
...
$.each(){
...
//修改删除按钮,在同一个单元格内
var editBtn = $("<butt..
//为编辑按钮添加一个自定义属性,表示当前员工id
editBtn.attr("edit-id", item.empId);
var delBtn...
//同理,也为删除按钮添加一个自定义属性
delBtn.attr("del-id", item.empId);
...
}
...
}
//因为在按钮创建之前就绑定click,所以绑定不了
//解决:在创建按钮后在绑定 或 绑定点击.live()
//jquery新版用on代替了live
//为修改按钮添加点击事件
$(document).on("click", ".edit_btn", function () {
//查出并显示部门信息
getDepts("#empUpdateModal select");
//查出并显示员工信息,$(this)当前被点击的编辑按钮
getEmp($(this).attr("edit-id"));
//把员工的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]);
}
});
完成员工对应信息在模态框显示。
//修改模态框邮箱输入框失去焦点校验事件(弹窗)
$("#email_add_input").blur(function () {
validate_add_form_email("#email_update_input");
});
$(document).on("click", ".edit_btn", function () {
//清除当前元素的校验状态
$("#email_update_input").parent().removeClass("has-success has-error");
$("#email_update_input").next("span").text("");
...
}
/**
* 根据主键有选择的更新员工信息
* @param employee
*/
public void updateEmp(Employee employee) {
employeeMapper.updateByPrimaryKeySelective(employee);
}
/**
* 员工更新
* 更新指定put请求
* @param employee
* @return
*/
@RequestMapping(value = "/emp/{empId}", method = RequestMethod.PUT)
@ResponseBody
public Msg saveEmp(Employee employee){
//更新员工信息
employeeService.updateEmp(employee);
return Msg.success();
}
//更新按钮的点击事件
$("#emp_update_btn").click(function () {
//校验邮箱信息
if (!validate_add_form_email("#email_update_input")){
alert("邮箱格式不正确,请输入正确的邮箱");
return false;
}
//发送ajax请求更新员工信息
$.ajax({
url: "${APP_PATH}/emp/" + $(this).attr("edit-id"),
//第一种方法:
// 如果ajax直接发put请求,请求体中的数据,request.getgetParameter("empName")拿不到
// tomcat发现是put请求,就不会封装数据为map,只有post形式的请求才会封装请求体为map
// 所以要在web.xml中注册FormContentFilter过滤器,支持put和delete请求
//第二种方法:
// ajax发送post请求,并在发送的data加上 "_method=put"
// HiddenHttpMethodFilter把post转为put
type: "put",
data: $("#empUpdateModal form").serialize(),
success: function (result) {
//关闭对话框
$("#empUpdateModal").modal("hide");
//回到本页面(刷新)
to_page(currentPage);
}
});
});
1、单个删除
2、多个删除
/**
* 根据id删除指定员工信息
* @param id
*/
public void deleteEmp(Integer id) {
employeeMapper.deleteByPrimaryKey(id);
}
/**
* 删除单个员工信息
* 删除指定delete请求
* @param id
* @return
*/
@RequestMapping(value = "/emp/{id}", method = RequestMethod.DELETE)
public Msg deleteEmpById(@PathVariable("id")Integer id){
//按照id删除指定员工信息
employeeService.deleteEmp(id);
return Msg.success();
}
//为员工对应的删除按钮添加点击事件(单个删除)
$(document).on("click", ".delete_btn", function () {
//获取删除的员工姓名
var empName = $(this).parents("tr").find("td:eq(1)").text();
//获取删除的员工id
var empId = $(this).attr("del-id");
//弹出是否确认删除提示
if(confirm("确定要删除【" + empName + "】吗?")){
//确认,发送ajax请求删除员工
$.ajax({
url: "${APP_PATH}/emp/" + empId,
type: "delete",
success: function (result) {
//回到本页(刷新页面)
to_page(currentPage);
}
});
}else {
//取消
return false;
}
});
...
<thead>
<tr>
<th>
<input type="checkbox" id="check_all">
th>
<th>empIdth>
<th>empNameth>
<th>genderth>
...
...
$.each(emps, function (index, item) {
var checkBoxTd = $(" ");
//员工信息单元格
...
//把相同员工的单元格添加到同一行
$(" ")
.append(checkBoxTd)
.append(empIdTd)
.append(empNameTd)
.append(genderTd)
.append(emailTd)
...
$("#check_all").click(function () {
//attr获取checked是undefined;
//attr获取自定义属性的值;
//prop修改和读取dom原生属性的值
//check_item应跟随全选按钮,一同选中或不选中
$(".check_item").prop("checked", $(this).prop("checked"));
});
//check_item的点击事件
$(document).on("click", ".check_item", function () {
//是否满足选中的个数和当前页员工的个数相同
var flag = $(".check_item:checked").length == $(".check_item").length;
//check_all全选按钮要和判断的结果相同,如果相同就选中,不同不选中
$("#check_all").prop("checked", flag);
});
/**
* 批量删除员工信息
* @param ids
*/
public void deleteBatch(List<Integer> ids) {
//自定义条件
EmployeeExample example = new EmployeeExample();
EmployeeExample.Criteria criteria = example.createCriteria();
//删除语句将变成:delete from xxx where emd_id in (1, 2, 4 ...)
criteria.andEmpIdIn(ids);
employeeMapper.deleteByExample(example);
}
/**
* 单个多选二合一删除员工信息
* 多个删除:id中间用-分隔,如 1-2-4
* 单个删除:如3
* 删除指定delete请求
* @param ids
* @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<>();
for (String id : str_ids) {
del_ids.add(Integer.parseInt(id));
}
//批量删除
employeeService.deleteBatch(del_ids);
}else{
//单个删除
//按照id删除指定员工信息
Integer id = Integer.parseInt(ids);
employeeService.deleteEmp(id);
}
return Msg.success();
}
//为上方删除按钮添加点击删除选中员工信息事件
$("#emp_delete_modal_btn").click(function () {
var empName = "";
var del_idstr = "";
//遍历当前页被选中的员工
$.each($(".check_item:checked"), function () {
//获取被选中的员工姓名
empName += $(this).parents("tr").find("td:eq(2)").text() + ",";
//组装员工id字符串
del_idstr += $(this).parents("tr").find("td:eq(1)").text() + "-";;
});
//去除多余 ,
empName = empName.substring(0, empName.length - 1);
//去除多余 -
del_idstr = del_idstr.substring(0, del_idstr.length - 1);
if(confirm("确定要删除【" + empName + "】吗?")){
//确认,发送ajax请求删除指定员工
$.ajax({
url: "${APP_PATH}/emp/" + del_idstr,
type: "delete",
success: function (result) {
//提示删除成功
alert(result.msg);
//回到当前页
to_page(currentPage);
}
});
}else{
//取消
return false;
}
});
磕磕碰碰的跟着老师做完了这个项目,第一次用ssm做项目,还是有很多知识点不够扎实,也学到了很多东西,学后端,前端的知识也需要了解,接下来继续练习,然后学习springboot。
学习视频:https://www.bilibili.com/video/BV17W411g7zP?p=1